blob: dca10f1f224e76e34263bca76f77a921a6bba9de [file] [log] [blame]
// 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