128 lines
4.7 KiB
C++
128 lines
4.7 KiB
C++
|
// Copyright 2021 The Tint Authors.
|
||
|
//
|
||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
// you may not use this file except in compliance with the License.
|
||
|
// You may obtain a copy of the License at
|
||
|
//
|
||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||
|
//
|
||
|
// Unless required by applicable law or agreed to in writing, software
|
||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
// See the License for the specific language governing permissions and
|
||
|
// limitations under the License.
|
||
|
|
||
|
#include "fuzzers/tint_spirv_tools_fuzzer/spirv_fuzz_mutator.h"
|
||
|
|
||
|
#include <fstream>
|
||
|
#include <utility>
|
||
|
|
||
|
#include "fuzzers/tint_spirv_tools_fuzzer/util.h"
|
||
|
#include "source/opt/build_module.h"
|
||
|
|
||
|
namespace tint {
|
||
|
namespace fuzzers {
|
||
|
namespace spvtools_fuzzer {
|
||
|
|
||
|
SpirvFuzzMutator::SpirvFuzzMutator(
|
||
|
spv_target_env target_env,
|
||
|
std::vector<uint32_t> binary,
|
||
|
unsigned seed,
|
||
|
const std::vector<spvtools::fuzz::fuzzerutil::ModuleSupplier>& donors,
|
||
|
bool enable_all_passes,
|
||
|
spvtools::fuzz::RepeatedPassStrategy repeated_pass_strategy,
|
||
|
bool validate_after_each_pass,
|
||
|
uint32_t transformation_batch_size)
|
||
|
: transformation_batch_size_(transformation_batch_size),
|
||
|
errors_(std::make_unique<std::stringstream>()),
|
||
|
fuzzer_(nullptr),
|
||
|
validator_options_(),
|
||
|
original_binary_(std::move(binary)),
|
||
|
seed_(seed) {
|
||
|
auto ir_context = spvtools::BuildModule(
|
||
|
target_env, spvtools::fuzz::fuzzerutil::kSilentMessageConsumer,
|
||
|
original_binary_.data(), original_binary_.size());
|
||
|
assert(ir_context && "|binary| is invalid");
|
||
|
|
||
|
auto transformation_context =
|
||
|
std::make_unique<spvtools::fuzz::TransformationContext>(
|
||
|
std::make_unique<spvtools::fuzz::FactManager>(ir_context.get()),
|
||
|
validator_options_);
|
||
|
|
||
|
auto fuzzer_context = std::make_unique<spvtools::fuzz::FuzzerContext>(
|
||
|
std::make_unique<spvtools::fuzz::PseudoRandomGenerator>(seed),
|
||
|
spvtools::fuzz::FuzzerContext::GetMinFreshId(ir_context.get()), false);
|
||
|
fuzzer_ = std::make_unique<spvtools::fuzz::Fuzzer>(
|
||
|
std::move(ir_context), std::move(transformation_context),
|
||
|
std::move(fuzzer_context), util::GetBufferMessageConsumer(errors_.get()),
|
||
|
donors, enable_all_passes, repeated_pass_strategy,
|
||
|
validate_after_each_pass, validator_options_);
|
||
|
}
|
||
|
|
||
|
Mutator::Result SpirvFuzzMutator::Mutate() {
|
||
|
// The assertion will fail in |fuzzer_->Run| if the previous fuzzing led to
|
||
|
// invalid module.
|
||
|
auto result = fuzzer_->Run(transformation_batch_size_);
|
||
|
switch (result.status) {
|
||
|
case spvtools::fuzz::Fuzzer::Status::kComplete:
|
||
|
return {Mutator::Status::kComplete, result.is_changed};
|
||
|
case spvtools::fuzz::Fuzzer::Status::kModuleTooBig:
|
||
|
case spvtools::fuzz::Fuzzer::Status::kTransformationLimitReached:
|
||
|
return {Mutator::Status::kLimitReached, result.is_changed};
|
||
|
case spvtools::fuzz::Fuzzer::Status::kFuzzerStuck:
|
||
|
return {Mutator::Status::kStuck, result.is_changed};
|
||
|
case spvtools::fuzz::Fuzzer::Status::kFuzzerPassLedToInvalidModule:
|
||
|
return {Mutator::Status::kInvalid, result.is_changed};
|
||
|
}
|
||
|
}
|
||
|
|
||
|
std::vector<uint32_t> SpirvFuzzMutator::GetBinary() const {
|
||
|
std::vector<uint32_t> result;
|
||
|
fuzzer_->GetIRContext()->module()->ToBinary(&result, true);
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
std::string SpirvFuzzMutator::GetErrors() const {
|
||
|
return errors_->str();
|
||
|
}
|
||
|
|
||
|
void SpirvFuzzMutator::LogErrors(const std::string* path,
|
||
|
uint32_t count) const {
|
||
|
auto message = GetErrors();
|
||
|
std::cout << count << " | SpirvFuzzMutator (seed: " << seed_ << ")"
|
||
|
<< std::endl;
|
||
|
std::cout << message << std::endl;
|
||
|
|
||
|
if (path) {
|
||
|
auto prefix = *path + std::to_string(count);
|
||
|
|
||
|
// Write errors to file.
|
||
|
std::ofstream(prefix + ".fuzzer.log") << "seed: " << seed_ << std::endl
|
||
|
<< message << std::endl;
|
||
|
|
||
|
// Write the invalid SPIR-V binary.
|
||
|
util::WriteBinary(prefix + ".fuzzer.invalid.spv", GetBinary());
|
||
|
|
||
|
// Write the original SPIR-V binary.
|
||
|
util::WriteBinary(prefix + ".fuzzer.original.spv", original_binary_);
|
||
|
|
||
|
// Write transformations.
|
||
|
google::protobuf::util::JsonOptions options;
|
||
|
options.add_whitespace = true;
|
||
|
std::string json;
|
||
|
google::protobuf::util::MessageToJsonString(
|
||
|
fuzzer_->GetTransformationSequence(), &json, options);
|
||
|
std::ofstream(prefix + ".fuzzer.transformations.json") << json << std::endl;
|
||
|
|
||
|
std::ofstream binary_transformations(
|
||
|
prefix + ".fuzzer.transformations.binary",
|
||
|
std::ios::binary | std::ios::out);
|
||
|
fuzzer_->GetTransformationSequence().SerializeToOstream(
|
||
|
&binary_transformations);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
} // namespace spvtools_fuzzer
|
||
|
} // namespace fuzzers
|
||
|
} // namespace tint
|