mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2024-12-24 16:51:06 +00:00
spirv-fuzz: Add persistent state to the fuzzer (#4137)
Adds persistent state to the fuzzer so that it can be used as a custom mutator for mutation-based fuzzing.
This commit is contained in:
parent
939bc02603
commit
43cfa9bc1d
@ -17,9 +17,7 @@
|
||||
#include <cassert>
|
||||
#include <memory>
|
||||
#include <numeric>
|
||||
#include <sstream>
|
||||
|
||||
#include "source/fuzz/fact_manager/fact_manager.h"
|
||||
#include "source/fuzz/fuzzer_context.h"
|
||||
#include "source/fuzz/fuzzer_pass_add_access_chains.h"
|
||||
#include "source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.h"
|
||||
@ -91,9 +89,6 @@
|
||||
#include "source/fuzz/fuzzer_pass_toggle_access_chain_instruction.h"
|
||||
#include "source/fuzz/fuzzer_pass_wrap_regions_in_selections.h"
|
||||
#include "source/fuzz/pass_management/repeated_pass_manager.h"
|
||||
#include "source/fuzz/pass_management/repeated_pass_manager_looped_with_recommendations.h"
|
||||
#include "source/fuzz/pass_management/repeated_pass_manager_random_with_recommendations.h"
|
||||
#include "source/fuzz/pass_management/repeated_pass_manager_simple.h"
|
||||
#include "source/fuzz/pass_management/repeated_pass_recommender_standard.h"
|
||||
#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
|
||||
#include "source/fuzz/transformation_context.h"
|
||||
@ -104,35 +99,142 @@
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
namespace {
|
||||
const uint32_t kIdBoundGap = 100;
|
||||
|
||||
} // namespace
|
||||
|
||||
Fuzzer::Fuzzer(spv_target_env target_env, MessageConsumer consumer,
|
||||
const std::vector<uint32_t>& binary_in,
|
||||
const protobufs::FactSequence& initial_facts,
|
||||
Fuzzer::Fuzzer(std::unique_ptr<opt::IRContext> ir_context,
|
||||
std::unique_ptr<TransformationContext> transformation_context,
|
||||
std::unique_ptr<FuzzerContext> fuzzer_context,
|
||||
MessageConsumer consumer,
|
||||
const std::vector<fuzzerutil::ModuleSupplier>& donor_suppliers,
|
||||
std::unique_ptr<RandomGenerator> random_generator,
|
||||
bool enable_all_passes,
|
||||
RepeatedPassStrategy repeated_pass_strategy,
|
||||
bool validate_after_each_fuzzer_pass,
|
||||
spv_validator_options validator_options)
|
||||
: target_env_(target_env),
|
||||
consumer_(std::move(consumer)),
|
||||
binary_in_(binary_in),
|
||||
initial_facts_(initial_facts),
|
||||
donor_suppliers_(donor_suppliers),
|
||||
random_generator_(std::move(random_generator)),
|
||||
: consumer_(std::move(consumer)),
|
||||
enable_all_passes_(enable_all_passes),
|
||||
repeated_pass_strategy_(repeated_pass_strategy),
|
||||
validate_after_each_fuzzer_pass_(validate_after_each_fuzzer_pass),
|
||||
validator_options_(validator_options),
|
||||
num_repeated_passes_applied_(0),
|
||||
ir_context_(nullptr),
|
||||
fuzzer_context_(nullptr),
|
||||
transformation_context_(nullptr),
|
||||
transformation_sequence_out_() {}
|
||||
is_valid_(true),
|
||||
ir_context_(std::move(ir_context)),
|
||||
transformation_context_(std::move(transformation_context)),
|
||||
fuzzer_context_(std::move(fuzzer_context)),
|
||||
transformation_sequence_out_(),
|
||||
pass_instances_(),
|
||||
repeated_pass_recommender_(nullptr),
|
||||
repeated_pass_manager_(nullptr),
|
||||
final_passes_() {
|
||||
assert(ir_context_ && "IRContext is not initialized");
|
||||
assert(fuzzer_context_ && "FuzzerContext is not initialized");
|
||||
assert(transformation_context_ && "TransformationContext is not initialized");
|
||||
assert(fuzzerutil::IsValidAndWellFormed(ir_context_.get(), validator_options_,
|
||||
consumer_) &&
|
||||
"IRContext is invalid");
|
||||
|
||||
// The following passes are likely to be very useful: many other passes
|
||||
// introduce synonyms, irrelevant ids and constants that these passes can work
|
||||
// with. We thus enable them with high probability.
|
||||
MaybeAddRepeatedPass<FuzzerPassObfuscateConstants>(90, &pass_instances_);
|
||||
MaybeAddRepeatedPass<FuzzerPassApplyIdSynonyms>(90, &pass_instances_);
|
||||
MaybeAddRepeatedPass<FuzzerPassReplaceIrrelevantIds>(90, &pass_instances_);
|
||||
|
||||
do {
|
||||
// Each call to MaybeAddRepeatedPass randomly decides whether the given pass
|
||||
// should be enabled, and adds an instance of the pass to |pass_instances|
|
||||
// if it is enabled.
|
||||
MaybeAddRepeatedPass<FuzzerPassAddAccessChains>(&pass_instances_);
|
||||
MaybeAddRepeatedPass<FuzzerPassAddBitInstructionSynonyms>(&pass_instances_);
|
||||
MaybeAddRepeatedPass<FuzzerPassAddCompositeExtract>(&pass_instances_);
|
||||
MaybeAddRepeatedPass<FuzzerPassAddCompositeInserts>(&pass_instances_);
|
||||
MaybeAddRepeatedPass<FuzzerPassAddCompositeTypes>(&pass_instances_);
|
||||
MaybeAddRepeatedPass<FuzzerPassAddCopyMemory>(&pass_instances_);
|
||||
MaybeAddRepeatedPass<FuzzerPassAddDeadBlocks>(&pass_instances_);
|
||||
MaybeAddRepeatedPass<FuzzerPassAddDeadBreaks>(&pass_instances_);
|
||||
MaybeAddRepeatedPass<FuzzerPassAddDeadContinues>(&pass_instances_);
|
||||
MaybeAddRepeatedPass<FuzzerPassAddEquationInstructions>(&pass_instances_);
|
||||
MaybeAddRepeatedPass<FuzzerPassAddFunctionCalls>(&pass_instances_);
|
||||
MaybeAddRepeatedPass<FuzzerPassAddGlobalVariables>(&pass_instances_);
|
||||
MaybeAddRepeatedPass<FuzzerPassAddImageSampleUnusedComponents>(
|
||||
&pass_instances_);
|
||||
MaybeAddRepeatedPass<FuzzerPassAddLoads>(&pass_instances_);
|
||||
MaybeAddRepeatedPass<FuzzerPassAddLocalVariables>(&pass_instances_);
|
||||
MaybeAddRepeatedPass<FuzzerPassAddLoopPreheaders>(&pass_instances_);
|
||||
MaybeAddRepeatedPass<FuzzerPassAddLoopsToCreateIntConstantSynonyms>(
|
||||
&pass_instances_);
|
||||
MaybeAddRepeatedPass<FuzzerPassAddOpPhiSynonyms>(&pass_instances_);
|
||||
MaybeAddRepeatedPass<FuzzerPassAddParameters>(&pass_instances_);
|
||||
MaybeAddRepeatedPass<FuzzerPassAddRelaxedDecorations>(&pass_instances_);
|
||||
MaybeAddRepeatedPass<FuzzerPassAddStores>(&pass_instances_);
|
||||
MaybeAddRepeatedPass<FuzzerPassAddSynonyms>(&pass_instances_);
|
||||
MaybeAddRepeatedPass<FuzzerPassAddVectorShuffleInstructions>(
|
||||
&pass_instances_);
|
||||
MaybeAddRepeatedPass<FuzzerPassConstructComposites>(&pass_instances_);
|
||||
MaybeAddRepeatedPass<FuzzerPassCopyObjects>(&pass_instances_);
|
||||
MaybeAddRepeatedPass<FuzzerPassDonateModules>(&pass_instances_,
|
||||
donor_suppliers);
|
||||
MaybeAddRepeatedPass<FuzzerPassDuplicateRegionsWithSelections>(
|
||||
&pass_instances_);
|
||||
MaybeAddRepeatedPass<FuzzerPassExpandVectorReductions>(&pass_instances_);
|
||||
MaybeAddRepeatedPass<FuzzerPassFlattenConditionalBranches>(
|
||||
&pass_instances_);
|
||||
MaybeAddRepeatedPass<FuzzerPassInlineFunctions>(&pass_instances_);
|
||||
MaybeAddRepeatedPass<FuzzerPassInvertComparisonOperators>(&pass_instances_);
|
||||
MaybeAddRepeatedPass<FuzzerPassMakeVectorOperationsDynamic>(
|
||||
&pass_instances_);
|
||||
MaybeAddRepeatedPass<FuzzerPassMergeBlocks>(&pass_instances_);
|
||||
MaybeAddRepeatedPass<FuzzerPassMergeFunctionReturns>(&pass_instances_);
|
||||
MaybeAddRepeatedPass<FuzzerPassMutatePointers>(&pass_instances_);
|
||||
MaybeAddRepeatedPass<FuzzerPassOutlineFunctions>(&pass_instances_);
|
||||
MaybeAddRepeatedPass<FuzzerPassPermuteBlocks>(&pass_instances_);
|
||||
MaybeAddRepeatedPass<FuzzerPassPermuteFunctionParameters>(&pass_instances_);
|
||||
MaybeAddRepeatedPass<FuzzerPassPermuteInstructions>(&pass_instances_);
|
||||
MaybeAddRepeatedPass<FuzzerPassPropagateInstructionsDown>(&pass_instances_);
|
||||
MaybeAddRepeatedPass<FuzzerPassPropagateInstructionsUp>(&pass_instances_);
|
||||
MaybeAddRepeatedPass<FuzzerPassPushIdsThroughVariables>(&pass_instances_);
|
||||
MaybeAddRepeatedPass<FuzzerPassReplaceAddsSubsMulsWithCarryingExtended>(
|
||||
&pass_instances_);
|
||||
MaybeAddRepeatedPass<FuzzerPassReplaceBranchesFromDeadBlocksWithExits>(
|
||||
&pass_instances_);
|
||||
MaybeAddRepeatedPass<FuzzerPassReplaceCopyMemoriesWithLoadsStores>(
|
||||
&pass_instances_);
|
||||
MaybeAddRepeatedPass<FuzzerPassReplaceCopyObjectsWithStoresLoads>(
|
||||
&pass_instances_);
|
||||
MaybeAddRepeatedPass<FuzzerPassReplaceLoadsStoresWithCopyMemories>(
|
||||
&pass_instances_);
|
||||
MaybeAddRepeatedPass<FuzzerPassReplaceParameterWithGlobal>(
|
||||
&pass_instances_);
|
||||
MaybeAddRepeatedPass<FuzzerPassReplaceLinearAlgebraInstructions>(
|
||||
&pass_instances_);
|
||||
MaybeAddRepeatedPass<FuzzerPassReplaceOpPhiIdsFromDeadPredecessors>(
|
||||
&pass_instances_);
|
||||
MaybeAddRepeatedPass<FuzzerPassReplaceOpSelectsWithConditionalBranches>(
|
||||
&pass_instances_);
|
||||
MaybeAddRepeatedPass<FuzzerPassReplaceParamsWithStruct>(&pass_instances_);
|
||||
MaybeAddRepeatedPass<FuzzerPassSplitBlocks>(&pass_instances_);
|
||||
MaybeAddRepeatedPass<FuzzerPassSwapBranchConditionalOperands>(
|
||||
&pass_instances_);
|
||||
MaybeAddRepeatedPass<FuzzerPassWrapRegionsInSelections>(&pass_instances_);
|
||||
// There is a theoretical possibility that no pass instances were created
|
||||
// until now; loop again if so.
|
||||
} while (pass_instances_.GetPasses().empty());
|
||||
|
||||
repeated_pass_recommender_ = MakeUnique<RepeatedPassRecommenderStandard>(
|
||||
&pass_instances_, fuzzer_context_.get());
|
||||
repeated_pass_manager_ = RepeatedPassManager::Create(
|
||||
repeated_pass_strategy, fuzzer_context_.get(), &pass_instances_,
|
||||
repeated_pass_recommender_.get());
|
||||
|
||||
MaybeAddFinalPass<FuzzerPassAdjustBranchWeights>(&final_passes_);
|
||||
MaybeAddFinalPass<FuzzerPassAdjustFunctionControls>(&final_passes_);
|
||||
MaybeAddFinalPass<FuzzerPassAdjustLoopControls>(&final_passes_);
|
||||
MaybeAddFinalPass<FuzzerPassAdjustMemoryOperandsMasks>(&final_passes_);
|
||||
MaybeAddFinalPass<FuzzerPassAdjustSelectionControls>(&final_passes_);
|
||||
MaybeAddFinalPass<FuzzerPassAddNoContractionDecorations>(&final_passes_);
|
||||
MaybeAddFinalPass<FuzzerPassInterchangeSignednessOfIntegerOperands>(
|
||||
&final_passes_);
|
||||
MaybeAddFinalPass<FuzzerPassInterchangeZeroLikeConstants>(&final_passes_);
|
||||
MaybeAddFinalPass<FuzzerPassPermutePhiOperands>(&final_passes_);
|
||||
MaybeAddFinalPass<FuzzerPassSwapCommutableOperands>(&final_passes_);
|
||||
MaybeAddFinalPass<FuzzerPassToggleAccessChainInstruction>(&final_passes_);
|
||||
}
|
||||
|
||||
Fuzzer::~Fuzzer() = default;
|
||||
|
||||
@ -165,240 +267,104 @@ bool Fuzzer::ApplyPassAndCheckValidity(FuzzerPass* pass) const {
|
||||
consumer_);
|
||||
}
|
||||
|
||||
Fuzzer::FuzzerResult Fuzzer::Run() {
|
||||
// Check compatibility between the library version being linked with and the
|
||||
// header files being used.
|
||||
GOOGLE_PROTOBUF_VERIFY_VERSION;
|
||||
opt::IRContext* Fuzzer::GetIRContext() { return ir_context_.get(); }
|
||||
|
||||
assert(ir_context_ == nullptr && fuzzer_context_ == nullptr &&
|
||||
transformation_context_ == nullptr &&
|
||||
transformation_sequence_out_.transformation_size() == 0 &&
|
||||
"'Run' must not be invoked more than once.");
|
||||
|
||||
spvtools::SpirvTools tools(target_env_);
|
||||
tools.SetMessageConsumer(consumer_);
|
||||
if (!tools.IsValid()) {
|
||||
consumer_(SPV_MSG_ERROR, nullptr, {},
|
||||
"Failed to create SPIRV-Tools interface; stopping.");
|
||||
return {Fuzzer::FuzzerResultStatus::kFailedToCreateSpirvToolsInterface,
|
||||
std::vector<uint32_t>(), protobufs::TransformationSequence()};
|
||||
}
|
||||
|
||||
// Initial binary should be valid.
|
||||
if (!tools.Validate(&binary_in_[0], binary_in_.size(), validator_options_)) {
|
||||
consumer_(SPV_MSG_ERROR, nullptr, {},
|
||||
"Initial binary is invalid; stopping.");
|
||||
return {Fuzzer::FuzzerResultStatus::kInitialBinaryInvalid,
|
||||
std::vector<uint32_t>(), protobufs::TransformationSequence()};
|
||||
}
|
||||
|
||||
// Build the module from the input binary.
|
||||
ir_context_ =
|
||||
BuildModule(target_env_, consumer_, binary_in_.data(), binary_in_.size());
|
||||
assert(ir_context_);
|
||||
|
||||
// The fuzzer will introduce new ids into the module. The module's id bound
|
||||
// gives the smallest id that can be used for this purpose. We add an offset
|
||||
// to this so that there is a sizeable gap between the ids used in the
|
||||
// original module and the ids used for fuzzing, as a readability aid.
|
||||
//
|
||||
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2541) consider the
|
||||
// case where the maximum id bound is reached.
|
||||
auto minimum_fresh_id = ir_context_->module()->id_bound() + kIdBoundGap;
|
||||
fuzzer_context_ =
|
||||
MakeUnique<FuzzerContext>(random_generator_.get(), minimum_fresh_id);
|
||||
|
||||
transformation_context_ = MakeUnique<TransformationContext>(
|
||||
MakeUnique<FactManager>(ir_context_.get()), validator_options_);
|
||||
transformation_context_->GetFactManager()->AddInitialFacts(consumer_,
|
||||
initial_facts_);
|
||||
|
||||
RepeatedPassInstances pass_instances{};
|
||||
|
||||
// The following passes are likely to be very useful: many other passes
|
||||
// introduce synonyms, irrelevant ids and constants that these passes can work
|
||||
// with. We thus enable them with high probability.
|
||||
MaybeAddRepeatedPass<FuzzerPassObfuscateConstants>(90, &pass_instances);
|
||||
MaybeAddRepeatedPass<FuzzerPassApplyIdSynonyms>(90, &pass_instances);
|
||||
MaybeAddRepeatedPass<FuzzerPassReplaceIrrelevantIds>(90, &pass_instances);
|
||||
|
||||
do {
|
||||
// Each call to MaybeAddRepeatedPass randomly decides whether the given pass
|
||||
// should be enabled, and adds an instance of the pass to |pass_instances|
|
||||
// if it is enabled.
|
||||
MaybeAddRepeatedPass<FuzzerPassAddAccessChains>(&pass_instances);
|
||||
MaybeAddRepeatedPass<FuzzerPassAddBitInstructionSynonyms>(&pass_instances);
|
||||
MaybeAddRepeatedPass<FuzzerPassAddCompositeExtract>(&pass_instances);
|
||||
MaybeAddRepeatedPass<FuzzerPassAddCompositeInserts>(&pass_instances);
|
||||
MaybeAddRepeatedPass<FuzzerPassAddCompositeTypes>(&pass_instances);
|
||||
MaybeAddRepeatedPass<FuzzerPassAddCopyMemory>(&pass_instances);
|
||||
MaybeAddRepeatedPass<FuzzerPassAddDeadBlocks>(&pass_instances);
|
||||
MaybeAddRepeatedPass<FuzzerPassAddDeadBreaks>(&pass_instances);
|
||||
MaybeAddRepeatedPass<FuzzerPassAddDeadContinues>(&pass_instances);
|
||||
MaybeAddRepeatedPass<FuzzerPassAddEquationInstructions>(&pass_instances);
|
||||
MaybeAddRepeatedPass<FuzzerPassAddFunctionCalls>(&pass_instances);
|
||||
MaybeAddRepeatedPass<FuzzerPassAddGlobalVariables>(&pass_instances);
|
||||
MaybeAddRepeatedPass<FuzzerPassAddImageSampleUnusedComponents>(
|
||||
&pass_instances);
|
||||
MaybeAddRepeatedPass<FuzzerPassAddLoads>(&pass_instances);
|
||||
MaybeAddRepeatedPass<FuzzerPassAddLocalVariables>(&pass_instances);
|
||||
MaybeAddRepeatedPass<FuzzerPassAddLoopPreheaders>(&pass_instances);
|
||||
MaybeAddRepeatedPass<FuzzerPassAddLoopsToCreateIntConstantSynonyms>(
|
||||
&pass_instances);
|
||||
MaybeAddRepeatedPass<FuzzerPassAddOpPhiSynonyms>(&pass_instances);
|
||||
MaybeAddRepeatedPass<FuzzerPassAddParameters>(&pass_instances);
|
||||
MaybeAddRepeatedPass<FuzzerPassAddRelaxedDecorations>(&pass_instances);
|
||||
MaybeAddRepeatedPass<FuzzerPassAddStores>(&pass_instances);
|
||||
MaybeAddRepeatedPass<FuzzerPassAddSynonyms>(&pass_instances);
|
||||
MaybeAddRepeatedPass<FuzzerPassAddVectorShuffleInstructions>(
|
||||
&pass_instances);
|
||||
MaybeAddRepeatedPass<FuzzerPassConstructComposites>(&pass_instances);
|
||||
MaybeAddRepeatedPass<FuzzerPassCopyObjects>(&pass_instances);
|
||||
MaybeAddRepeatedPass<FuzzerPassDonateModules>(&pass_instances,
|
||||
donor_suppliers_);
|
||||
MaybeAddRepeatedPass<FuzzerPassDuplicateRegionsWithSelections>(
|
||||
&pass_instances);
|
||||
MaybeAddRepeatedPass<FuzzerPassExpandVectorReductions>(&pass_instances);
|
||||
MaybeAddRepeatedPass<FuzzerPassFlattenConditionalBranches>(&pass_instances);
|
||||
MaybeAddRepeatedPass<FuzzerPassInlineFunctions>(&pass_instances);
|
||||
MaybeAddRepeatedPass<FuzzerPassInvertComparisonOperators>(&pass_instances);
|
||||
MaybeAddRepeatedPass<FuzzerPassMakeVectorOperationsDynamic>(
|
||||
&pass_instances);
|
||||
MaybeAddRepeatedPass<FuzzerPassMergeBlocks>(&pass_instances);
|
||||
MaybeAddRepeatedPass<FuzzerPassMergeFunctionReturns>(&pass_instances);
|
||||
MaybeAddRepeatedPass<FuzzerPassMutatePointers>(&pass_instances);
|
||||
MaybeAddRepeatedPass<FuzzerPassOutlineFunctions>(&pass_instances);
|
||||
MaybeAddRepeatedPass<FuzzerPassPermuteBlocks>(&pass_instances);
|
||||
MaybeAddRepeatedPass<FuzzerPassPermuteFunctionParameters>(&pass_instances);
|
||||
MaybeAddRepeatedPass<FuzzerPassPermuteInstructions>(&pass_instances);
|
||||
MaybeAddRepeatedPass<FuzzerPassPropagateInstructionsDown>(&pass_instances);
|
||||
MaybeAddRepeatedPass<FuzzerPassPropagateInstructionsUp>(&pass_instances);
|
||||
MaybeAddRepeatedPass<FuzzerPassPushIdsThroughVariables>(&pass_instances);
|
||||
MaybeAddRepeatedPass<FuzzerPassReplaceAddsSubsMulsWithCarryingExtended>(
|
||||
&pass_instances);
|
||||
MaybeAddRepeatedPass<FuzzerPassReplaceBranchesFromDeadBlocksWithExits>(
|
||||
&pass_instances);
|
||||
MaybeAddRepeatedPass<FuzzerPassReplaceCopyMemoriesWithLoadsStores>(
|
||||
&pass_instances);
|
||||
MaybeAddRepeatedPass<FuzzerPassReplaceCopyObjectsWithStoresLoads>(
|
||||
&pass_instances);
|
||||
MaybeAddRepeatedPass<FuzzerPassReplaceLoadsStoresWithCopyMemories>(
|
||||
&pass_instances);
|
||||
MaybeAddRepeatedPass<FuzzerPassReplaceParameterWithGlobal>(&pass_instances);
|
||||
MaybeAddRepeatedPass<FuzzerPassReplaceLinearAlgebraInstructions>(
|
||||
&pass_instances);
|
||||
MaybeAddRepeatedPass<FuzzerPassReplaceOpPhiIdsFromDeadPredecessors>(
|
||||
&pass_instances);
|
||||
MaybeAddRepeatedPass<FuzzerPassReplaceOpSelectsWithConditionalBranches>(
|
||||
&pass_instances);
|
||||
MaybeAddRepeatedPass<FuzzerPassReplaceParamsWithStruct>(&pass_instances);
|
||||
MaybeAddRepeatedPass<FuzzerPassSplitBlocks>(&pass_instances);
|
||||
MaybeAddRepeatedPass<FuzzerPassSwapBranchConditionalOperands>(
|
||||
&pass_instances);
|
||||
MaybeAddRepeatedPass<FuzzerPassWrapRegionsInSelections>(&pass_instances);
|
||||
// There is a theoretical possibility that no pass instances were created
|
||||
// until now; loop again if so.
|
||||
} while (pass_instances.GetPasses().empty());
|
||||
|
||||
RepeatedPassRecommenderStandard pass_recommender(&pass_instances,
|
||||
fuzzer_context_.get());
|
||||
|
||||
std::unique_ptr<RepeatedPassManager> repeated_pass_manager = nullptr;
|
||||
switch (repeated_pass_strategy_) {
|
||||
case RepeatedPassStrategy::kSimple:
|
||||
repeated_pass_manager = MakeUnique<RepeatedPassManagerSimple>(
|
||||
fuzzer_context_.get(), &pass_instances);
|
||||
break;
|
||||
case RepeatedPassStrategy::kLoopedWithRecommendations:
|
||||
repeated_pass_manager =
|
||||
MakeUnique<RepeatedPassManagerLoopedWithRecommendations>(
|
||||
fuzzer_context_.get(), &pass_instances, &pass_recommender);
|
||||
break;
|
||||
case RepeatedPassStrategy::kRandomWithRecommendations:
|
||||
repeated_pass_manager =
|
||||
MakeUnique<RepeatedPassManagerRandomWithRecommendations>(
|
||||
fuzzer_context_.get(), &pass_instances, &pass_recommender);
|
||||
break;
|
||||
}
|
||||
|
||||
do {
|
||||
if (!ApplyPassAndCheckValidity(
|
||||
repeated_pass_manager->ChoosePass(transformation_sequence_out_))) {
|
||||
return {Fuzzer::FuzzerResultStatus::kFuzzerPassLedToInvalidModule,
|
||||
std::vector<uint32_t>(), protobufs::TransformationSequence()};
|
||||
}
|
||||
} while (ShouldContinueFuzzing());
|
||||
|
||||
// Now apply some passes that it does not make sense to apply repeatedly,
|
||||
// as they do not unlock other passes.
|
||||
std::vector<std::unique_ptr<FuzzerPass>> final_passes;
|
||||
MaybeAddFinalPass<FuzzerPassAdjustBranchWeights>(&final_passes);
|
||||
MaybeAddFinalPass<FuzzerPassAdjustFunctionControls>(&final_passes);
|
||||
MaybeAddFinalPass<FuzzerPassAdjustLoopControls>(&final_passes);
|
||||
MaybeAddFinalPass<FuzzerPassAdjustMemoryOperandsMasks>(&final_passes);
|
||||
MaybeAddFinalPass<FuzzerPassAdjustSelectionControls>(&final_passes);
|
||||
MaybeAddFinalPass<FuzzerPassAddNoContractionDecorations>(&final_passes);
|
||||
MaybeAddFinalPass<FuzzerPassInterchangeSignednessOfIntegerOperands>(
|
||||
&final_passes);
|
||||
MaybeAddFinalPass<FuzzerPassInterchangeZeroLikeConstants>(&final_passes);
|
||||
MaybeAddFinalPass<FuzzerPassPermutePhiOperands>(&final_passes);
|
||||
MaybeAddFinalPass<FuzzerPassSwapCommutableOperands>(&final_passes);
|
||||
MaybeAddFinalPass<FuzzerPassToggleAccessChainInstruction>(&final_passes);
|
||||
for (auto& pass : final_passes) {
|
||||
if (!ApplyPassAndCheckValidity(pass.get())) {
|
||||
return {Fuzzer::FuzzerResultStatus::kFuzzerPassLedToInvalidModule,
|
||||
std::vector<uint32_t>(), protobufs::TransformationSequence()};
|
||||
}
|
||||
}
|
||||
// Encode the module as a binary.
|
||||
std::vector<uint32_t> binary_out;
|
||||
ir_context_->module()->ToBinary(&binary_out, false);
|
||||
|
||||
return {Fuzzer::FuzzerResultStatus::kComplete, std::move(binary_out),
|
||||
std::move(transformation_sequence_out_)};
|
||||
const protobufs::TransformationSequence& Fuzzer::GetTransformationSequence()
|
||||
const {
|
||||
return transformation_sequence_out_;
|
||||
}
|
||||
|
||||
bool Fuzzer::ShouldContinueFuzzing() {
|
||||
// There's a risk that fuzzing could get stuck, if none of the enabled fuzzer
|
||||
// passes are able to apply any transformations. To guard against this we
|
||||
// count the number of times some repeated pass has been applied and ensure
|
||||
// that fuzzing stops if the number of repeated passes hits the limit on the
|
||||
// number of transformations that can be applied.
|
||||
assert(
|
||||
num_repeated_passes_applied_ <=
|
||||
fuzzer_context_->GetTransformationLimit() &&
|
||||
"The number of repeated passes applied must not exceed its upper limit.");
|
||||
if (ir_context_->module()->id_bound() >= fuzzer_context_->GetIdBoundLimit()) {
|
||||
return false;
|
||||
}
|
||||
if (num_repeated_passes_applied_ ==
|
||||
fuzzer_context_->GetTransformationLimit()) {
|
||||
// Stop because fuzzing has got stuck.
|
||||
return false;
|
||||
}
|
||||
auto transformations_applied_so_far =
|
||||
Fuzzer::Result Fuzzer::Run(uint32_t num_of_transformations_to_apply) {
|
||||
assert(is_valid_ && "The module was invalidated during the previous fuzzing");
|
||||
|
||||
const auto initial_num_of_transformations =
|
||||
static_cast<uint32_t>(transformation_sequence_out_.transformation_size());
|
||||
if (transformations_applied_so_far >=
|
||||
fuzzer_context_->GetTransformationLimit()) {
|
||||
// Stop because we have reached the transformation limit.
|
||||
return false;
|
||||
|
||||
auto status = Status::kComplete;
|
||||
do {
|
||||
// Check that the module is small enough.
|
||||
if (ir_context_->module()->id_bound() >=
|
||||
fuzzer_context_->GetIdBoundLimit()) {
|
||||
status = Status::kModuleTooBig;
|
||||
break;
|
||||
}
|
||||
|
||||
auto transformations_applied_so_far = static_cast<uint32_t>(
|
||||
transformation_sequence_out_.transformation_size());
|
||||
assert(transformations_applied_so_far >= initial_num_of_transformations &&
|
||||
"Number of transformations cannot decrease");
|
||||
|
||||
// Check if we've already applied the maximum number of transformations.
|
||||
if (transformations_applied_so_far >=
|
||||
fuzzer_context_->GetTransformationLimit()) {
|
||||
status = Status::kTransformationLimitReached;
|
||||
break;
|
||||
}
|
||||
|
||||
// If the number of transformations is still small
|
||||
if (num_repeated_passes_applied_ >=
|
||||
fuzzer_context_->GetTransformationLimit()) {
|
||||
status = Status::kFuzzerStuck;
|
||||
break;
|
||||
}
|
||||
|
||||
// Check whether we've exceeded the number of transformations we can apply
|
||||
// in a single call to this method.
|
||||
if (num_of_transformations_to_apply != 0 &&
|
||||
transformations_applied_so_far - initial_num_of_transformations >=
|
||||
num_of_transformations_to_apply) {
|
||||
status = Status::kComplete;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!ApplyPassAndCheckValidity(
|
||||
repeated_pass_manager_->ChoosePass(transformation_sequence_out_))) {
|
||||
status = Status::kFuzzerPassLedToInvalidModule;
|
||||
break;
|
||||
}
|
||||
} while (ShouldContinueRepeatedPasses(num_of_transformations_to_apply == 0));
|
||||
|
||||
if (status != Status::kFuzzerPassLedToInvalidModule) {
|
||||
// We apply this transformations despite the fact that we might exceed
|
||||
// |num_of_transformations_to_apply|. This is not a problem for us since
|
||||
// these fuzzer passes are relatively simple yet might trigger some bugs.
|
||||
for (auto& pass : final_passes_) {
|
||||
if (!ApplyPassAndCheckValidity(pass.get())) {
|
||||
status = Status::kFuzzerPassLedToInvalidModule;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// If we have applied T transformations so far, and the limit on the number of
|
||||
// transformations to apply is L (where T < L), the chance that we will
|
||||
// continue fuzzing is:
|
||||
//
|
||||
// 1 - T/(2*L)
|
||||
//
|
||||
// That is, the chance of continuing decreases as more transformations are
|
||||
// applied. Using 2*L instead of L increases the number of transformations
|
||||
// that are applied on average.
|
||||
auto chance_of_continuing = static_cast<uint32_t>(
|
||||
100.0 * (1.0 - (static_cast<double>(transformations_applied_so_far) /
|
||||
(2.0 * static_cast<double>(
|
||||
fuzzer_context_->GetTransformationLimit())))));
|
||||
if (!fuzzer_context_->ChoosePercentage(chance_of_continuing)) {
|
||||
// We have probabilistically decided to stop.
|
||||
return false;
|
||||
|
||||
is_valid_ = status != Status::kFuzzerPassLedToInvalidModule;
|
||||
return {status, static_cast<uint32_t>(
|
||||
transformation_sequence_out_.transformation_size()) !=
|
||||
initial_num_of_transformations};
|
||||
}
|
||||
|
||||
bool Fuzzer::ShouldContinueRepeatedPasses(
|
||||
bool continue_fuzzing_probabilistically) {
|
||||
if (continue_fuzzing_probabilistically) {
|
||||
// If we have applied T transformations so far, and the limit on the number
|
||||
// of transformations to apply is L (where T < L), the chance that we will
|
||||
// continue fuzzing is:
|
||||
//
|
||||
// 1 - T/(2*L)
|
||||
//
|
||||
// That is, the chance of continuing decreases as more transformations are
|
||||
// applied. Using 2*L instead of L increases the number of transformations
|
||||
// that are applied on average.
|
||||
auto transformations_applied_so_far = static_cast<uint32_t>(
|
||||
transformation_sequence_out_.transformation_size());
|
||||
auto chance_of_continuing = static_cast<uint32_t>(
|
||||
100.0 *
|
||||
(1.0 - (static_cast<double>(transformations_applied_so_far) /
|
||||
(2.0 * static_cast<double>(
|
||||
fuzzer_context_->GetTransformationLimit())))));
|
||||
if (!fuzzer_context_->ChoosePercentage(chance_of_continuing)) {
|
||||
// We have probabilistically decided to stop.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// Continue fuzzing!
|
||||
num_repeated_passes_applied_++;
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include "source/fuzz/fuzzer_pass.h"
|
||||
#include "source/fuzz/fuzzer_util.h"
|
||||
#include "source/fuzz/pass_management/repeated_pass_instances.h"
|
||||
#include "source/fuzz/pass_management/repeated_pass_manager.h"
|
||||
#include "source/fuzz/pass_management/repeated_pass_recommender.h"
|
||||
#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
|
||||
#include "source/fuzz/random_generator.h"
|
||||
@ -37,33 +38,28 @@ namespace fuzz {
|
||||
class Fuzzer {
|
||||
public:
|
||||
// Possible statuses that can result from running the fuzzer.
|
||||
enum class FuzzerResultStatus {
|
||||
enum class Status {
|
||||
kComplete,
|
||||
kFailedToCreateSpirvToolsInterface,
|
||||
kModuleTooBig,
|
||||
kTransformationLimitReached,
|
||||
kFuzzerStuck,
|
||||
kFuzzerPassLedToInvalidModule,
|
||||
kInitialBinaryInvalid,
|
||||
};
|
||||
|
||||
struct FuzzerResult {
|
||||
FuzzerResultStatus status;
|
||||
std::vector<uint32_t> transformed_binary;
|
||||
protobufs::TransformationSequence applied_transformations;
|
||||
struct Result {
|
||||
// Status of the fuzzing session.
|
||||
Status status;
|
||||
|
||||
// Equals to true if new transformations were applied during the previous
|
||||
// fuzzing session.
|
||||
bool is_changed;
|
||||
};
|
||||
|
||||
// Each field of this enum corresponds to an available repeated pass
|
||||
// strategy, and is used to decide which kind of RepeatedPassManager object
|
||||
// to create.
|
||||
enum class RepeatedPassStrategy {
|
||||
kSimple,
|
||||
kRandomWithRecommendations,
|
||||
kLoopedWithRecommendations
|
||||
};
|
||||
|
||||
Fuzzer(spv_target_env target_env, MessageConsumer consumer,
|
||||
const std::vector<uint32_t>& binary_in,
|
||||
const protobufs::FactSequence& initial_facts,
|
||||
Fuzzer(std::unique_ptr<opt::IRContext> ir_context,
|
||||
std::unique_ptr<TransformationContext> transformation_context,
|
||||
std::unique_ptr<FuzzerContext> fuzzer_context,
|
||||
MessageConsumer consumer,
|
||||
const std::vector<fuzzerutil::ModuleSupplier>& donor_suppliers,
|
||||
std::unique_ptr<RandomGenerator> random_generator,
|
||||
bool enable_all_passes, RepeatedPassStrategy repeated_pass_strategy,
|
||||
bool validate_after_each_fuzzer_pass,
|
||||
spv_validator_options validator_options);
|
||||
@ -76,15 +72,23 @@ class Fuzzer {
|
||||
|
||||
~Fuzzer();
|
||||
|
||||
// Transforms |binary_in_| by running a number of randomized fuzzer passes.
|
||||
// Initial facts about the input binary and the context in which it will
|
||||
// execute are provided via |initial_facts_|. A source of donor modules to be
|
||||
// used by transformations is provided via |donor_suppliers_|. On success,
|
||||
// returns a successful result status together with the transformed binary and
|
||||
// the sequence of transformations that were applied. Otherwise, returns an
|
||||
// appropriate result status together with an empty binary and empty
|
||||
// transformation sequence.
|
||||
FuzzerResult Run();
|
||||
// Transforms |ir_context_| by running a number of randomized fuzzer passes.
|
||||
// Initial facts about the input binary and the context in which it will be
|
||||
// executed are provided with |transformation_context_|.
|
||||
// |num_of_transformations| is equal to the maximum number of transformations
|
||||
// applied in a single call to this method. This parameter is ignored if its
|
||||
// value is equal to 0. Because fuzzing cannot stop mid way through a fuzzer
|
||||
// pass, fuzzing will stop after the fuzzer pass that exceeds
|
||||
// |num_of_transformations| has completed, so that the total number of
|
||||
// transformations may be somewhat larger than this number.
|
||||
Result Run(uint32_t num_of_transformations_to_apply);
|
||||
|
||||
// Returns the current IR context. It may be invalid if the Run method
|
||||
// returned Status::kFuzzerPassLedToInvalidModule previously.
|
||||
opt::IRContext* GetIRContext();
|
||||
|
||||
// Returns the sequence of applied transformations.
|
||||
const protobufs::TransformationSequence& GetTransformationSequence() const;
|
||||
|
||||
private:
|
||||
// A convenience method to add a repeated fuzzer pass to |pass_instances| with
|
||||
@ -119,7 +123,9 @@ class Fuzzer {
|
||||
|
||||
// Decides whether to apply more repeated passes. The probability decreases as
|
||||
// the number of transformations that have been applied increases.
|
||||
bool ShouldContinueFuzzing();
|
||||
// The described probability is only applied if
|
||||
// |continue_fuzzing_probabilistically| is true.
|
||||
bool ShouldContinueRepeatedPasses(bool continue_fuzzing_probabilistically);
|
||||
|
||||
// Applies |pass|, which must be a pass constructed with |ir_context|.
|
||||
// If |validate_after_each_fuzzer_pass_| is not set, true is always returned.
|
||||
@ -128,57 +134,59 @@ class Fuzzer {
|
||||
// instruction has a distinct unique id.
|
||||
bool ApplyPassAndCheckValidity(FuzzerPass* pass) const;
|
||||
|
||||
// Target environment.
|
||||
const spv_target_env target_env_;
|
||||
|
||||
// Message consumer that will be invoked once for each message communicated
|
||||
// from the library.
|
||||
MessageConsumer consumer_;
|
||||
|
||||
// The initial binary to which fuzzing should be applied.
|
||||
const std::vector<uint32_t>& binary_in_;
|
||||
|
||||
// Initial facts known to hold in advance of applying any transformations.
|
||||
const protobufs::FactSequence& initial_facts_;
|
||||
|
||||
// A source of modules whose contents can be donated into the module being
|
||||
// fuzzed.
|
||||
const std::vector<fuzzerutil::ModuleSupplier>& donor_suppliers_;
|
||||
|
||||
// Random number generator to control decision making during fuzzing.
|
||||
std::unique_ptr<RandomGenerator> random_generator_;
|
||||
const MessageConsumer consumer_;
|
||||
|
||||
// Determines whether all passes should be enabled, vs. having passes be
|
||||
// probabilistically enabled.
|
||||
bool enable_all_passes_;
|
||||
|
||||
// Controls which type of RepeatedPassManager object to create.
|
||||
RepeatedPassStrategy repeated_pass_strategy_;
|
||||
const bool enable_all_passes_;
|
||||
|
||||
// Determines whether the validator should be invoked after every fuzzer pass.
|
||||
bool validate_after_each_fuzzer_pass_;
|
||||
const bool validate_after_each_fuzzer_pass_;
|
||||
|
||||
// Options to control validation.
|
||||
spv_validator_options validator_options_;
|
||||
const spv_validator_options validator_options_;
|
||||
|
||||
// The number of repeated fuzzer passes that have been applied is kept track
|
||||
// of, in order to enforce a hard limit on the number of times such passes
|
||||
// can be applied.
|
||||
uint32_t num_repeated_passes_applied_;
|
||||
|
||||
// We use this to determine whether we can continue fuzzing incrementally
|
||||
// since the previous call to the Run method could've returned
|
||||
// kFuzzerPassLedToInvalidModule.
|
||||
bool is_valid_;
|
||||
|
||||
// Intermediate representation for the module being fuzzed, which gets
|
||||
// mutated as fuzzing proceeds.
|
||||
std::unique_ptr<opt::IRContext> ir_context_;
|
||||
|
||||
// Contextual information that is required in order to apply
|
||||
// transformations.
|
||||
std::unique_ptr<TransformationContext> transformation_context_;
|
||||
|
||||
// Provides probabilities that control the fuzzing process.
|
||||
std::unique_ptr<FuzzerContext> fuzzer_context_;
|
||||
|
||||
// Contextual information that is required in order to apply transformations.
|
||||
std::unique_ptr<TransformationContext> transformation_context_;
|
||||
|
||||
// The sequence of transformations that have been applied during fuzzing. It
|
||||
// The sequence of transformations that have been applied during fuzzing. It
|
||||
// is initially empty and grows as fuzzer passes are applied.
|
||||
protobufs::TransformationSequence transformation_sequence_out_;
|
||||
|
||||
// This object contains instances of all fuzzer passes that will participate
|
||||
// in the fuzzing.
|
||||
RepeatedPassInstances pass_instances_;
|
||||
|
||||
// This object defines the recommendation logic for fuzzer passes.
|
||||
std::unique_ptr<RepeatedPassRecommender> repeated_pass_recommender_;
|
||||
|
||||
// This object manager a list of fuzzer pass and their available
|
||||
// recommendations.
|
||||
std::unique_ptr<RepeatedPassManager> repeated_pass_manager_;
|
||||
|
||||
// Some passes that it does not make sense to apply repeatedly, as they do not
|
||||
// unlock other passes.
|
||||
std::vector<std::unique_ptr<FuzzerPass>> final_passes_;
|
||||
};
|
||||
|
||||
} // namespace fuzz
|
||||
|
@ -21,6 +21,12 @@ namespace fuzz {
|
||||
|
||||
namespace {
|
||||
|
||||
// An offset between the the module's id bound and the minimum fresh id.
|
||||
//
|
||||
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2541): consider
|
||||
// the case where the maximum id bound is reached.
|
||||
const uint32_t kIdBoundGap = 100;
|
||||
|
||||
// Limits to help control the overall fuzzing process and rein in individual
|
||||
// fuzzer passes.
|
||||
const uint32_t kIdBoundLimit = 50000;
|
||||
@ -177,9 +183,9 @@ const std::function<bool(uint32_t, RandomGenerator*)>
|
||||
|
||||
} // namespace
|
||||
|
||||
FuzzerContext::FuzzerContext(RandomGenerator* random_generator,
|
||||
FuzzerContext::FuzzerContext(std::unique_ptr<RandomGenerator> random_generator,
|
||||
uint32_t min_fresh_id)
|
||||
: random_generator_(random_generator),
|
||||
: random_generator_(std::move(random_generator)),
|
||||
next_fresh_id_(min_fresh_id),
|
||||
max_equivalence_class_size_for_data_synonym_fact_closure_(
|
||||
kDefaultMaxEquivalenceClassSizeForDataSynonymFactClosure),
|
||||
@ -403,5 +409,9 @@ uint32_t FuzzerContext::GetTransformationLimit() const {
|
||||
return kTransformationLimit;
|
||||
}
|
||||
|
||||
uint32_t FuzzerContext::GetMinFreshId(opt::IRContext* ir_context) {
|
||||
return ir_context->module()->id_bound() + kIdBoundGap;
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
|
||||
#include "source/fuzz/random_generator.h"
|
||||
#include "source/opt/function.h"
|
||||
#include "source/opt/ir_context.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
@ -32,7 +33,8 @@ class FuzzerContext {
|
||||
public:
|
||||
// Constructs a fuzzer context with a given random generator and the minimum
|
||||
// value that can be used for fresh ids.
|
||||
FuzzerContext(RandomGenerator* random_generator, uint32_t min_fresh_id);
|
||||
FuzzerContext(std::unique_ptr<RandomGenerator> random_generator,
|
||||
uint32_t min_fresh_id);
|
||||
|
||||
~FuzzerContext();
|
||||
|
||||
@ -115,6 +117,9 @@ class FuzzerContext {
|
||||
// fuzzer passes.
|
||||
uint32_t GetTransformationLimit() const;
|
||||
|
||||
// Returns the minimum fresh id that can be used given the |ir_context|.
|
||||
static uint32_t GetMinFreshId(opt::IRContext* ir_context);
|
||||
|
||||
// Probabilities associated with applying various transformations.
|
||||
// Keep them in alphabetical order.
|
||||
uint32_t GetChanceOfAcceptingRepeatedPassRecommendation() const {
|
||||
@ -442,12 +447,12 @@ class FuzzerContext {
|
||||
return random_generator_->RandomUint32(max_unused_component_count) + 1;
|
||||
}
|
||||
bool GoDeeperInConstantObfuscation(uint32_t depth) {
|
||||
return go_deeper_in_constant_obfuscation_(depth, random_generator_);
|
||||
return go_deeper_in_constant_obfuscation_(depth, random_generator_.get());
|
||||
}
|
||||
|
||||
private:
|
||||
// The source of randomness.
|
||||
RandomGenerator* random_generator_;
|
||||
std::unique_ptr<RandomGenerator> random_generator_;
|
||||
// The next fresh id to be issued.
|
||||
uint32_t next_fresh_id_;
|
||||
|
||||
|
@ -47,6 +47,34 @@ const spvtools::MessageConsumer kSilentMessageConsumer =
|
||||
[](spv_message_level_t, const char*, const spv_position_t&,
|
||||
const char*) -> void {};
|
||||
|
||||
bool BuildIRContext(spv_target_env target_env,
|
||||
const spvtools::MessageConsumer& message_consumer,
|
||||
const std::vector<uint32_t>& binary_in,
|
||||
spv_validator_options validator_options,
|
||||
std::unique_ptr<spvtools::opt::IRContext>* ir_context) {
|
||||
SpirvTools tools(target_env);
|
||||
tools.SetMessageConsumer(message_consumer);
|
||||
if (!tools.IsValid()) {
|
||||
message_consumer(SPV_MSG_ERROR, nullptr, {},
|
||||
"Failed to create SPIRV-Tools interface; stopping.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Initial binary should be valid.
|
||||
if (!tools.Validate(binary_in.data(), binary_in.size(), validator_options)) {
|
||||
message_consumer(SPV_MSG_ERROR, nullptr, {},
|
||||
"Initial binary is invalid; stopping.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Build the module from the input binary.
|
||||
auto result = BuildModule(target_env, message_consumer, binary_in.data(),
|
||||
binary_in.size());
|
||||
assert(result && "IRContext must be valid");
|
||||
*ir_context = std::move(result);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IsFreshId(opt::IRContext* context, uint32_t id) {
|
||||
return !context->get_def_use_mgr()->GetDef(id);
|
||||
}
|
||||
@ -410,7 +438,7 @@ bool IsValid(const opt::IRContext* context,
|
||||
std::vector<uint32_t> binary;
|
||||
context->module()->ToBinary(&binary, false);
|
||||
SpirvTools tools(context->grammar().target_env());
|
||||
tools.SetMessageConsumer(consumer);
|
||||
tools.SetMessageConsumer(std::move(consumer));
|
||||
return tools.Validate(binary.data(), binary.size(), validator_options);
|
||||
}
|
||||
|
||||
|
@ -38,6 +38,15 @@ extern const spvtools::MessageConsumer kSilentMessageConsumer;
|
||||
// Function type that produces a SPIR-V module.
|
||||
using ModuleSupplier = std::function<std::unique_ptr<opt::IRContext>()>;
|
||||
|
||||
// Builds a new opt::IRContext object. Returns true if successful and changes
|
||||
// the |ir_context| parameter. Otherwise (if any errors occur), returns false
|
||||
// and |ir_context| remains unchanged.
|
||||
bool BuildIRContext(spv_target_env target_env,
|
||||
const spvtools::MessageConsumer& message_consumer,
|
||||
const std::vector<uint32_t>& binary_in,
|
||||
spv_validator_options validator_options,
|
||||
std::unique_ptr<spvtools::opt::IRContext>* ir_context);
|
||||
|
||||
// Returns true if and only if the module does not define the given id.
|
||||
bool IsFreshId(opt::IRContext* context, uint32_t id);
|
||||
|
||||
|
@ -14,6 +14,10 @@
|
||||
|
||||
#include "source/fuzz/pass_management/repeated_pass_manager.h"
|
||||
|
||||
#include "source/fuzz/pass_management/repeated_pass_manager_looped_with_recommendations.h"
|
||||
#include "source/fuzz/pass_management/repeated_pass_manager_random_with_recommendations.h"
|
||||
#include "source/fuzz/pass_management/repeated_pass_manager_simple.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
@ -23,5 +27,25 @@ RepeatedPassManager::RepeatedPassManager(FuzzerContext* fuzzer_context,
|
||||
|
||||
RepeatedPassManager::~RepeatedPassManager() = default;
|
||||
|
||||
std::unique_ptr<RepeatedPassManager> RepeatedPassManager::Create(
|
||||
RepeatedPassStrategy strategy, FuzzerContext* fuzzer_context,
|
||||
RepeatedPassInstances* pass_instances,
|
||||
RepeatedPassRecommender* pass_recommender) {
|
||||
switch (strategy) {
|
||||
case RepeatedPassStrategy::kSimple:
|
||||
return MakeUnique<RepeatedPassManagerSimple>(fuzzer_context,
|
||||
pass_instances);
|
||||
case RepeatedPassStrategy::kLoopedWithRecommendations:
|
||||
return MakeUnique<RepeatedPassManagerLoopedWithRecommendations>(
|
||||
fuzzer_context, pass_instances, pass_recommender);
|
||||
case RepeatedPassStrategy::kRandomWithRecommendations:
|
||||
return MakeUnique<RepeatedPassManagerRandomWithRecommendations>(
|
||||
fuzzer_context, pass_instances, pass_recommender);
|
||||
}
|
||||
|
||||
assert(false && "Unreachable");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
@ -18,11 +18,21 @@
|
||||
#include "source/fuzz/fuzzer_context.h"
|
||||
#include "source/fuzz/fuzzer_pass.h"
|
||||
#include "source/fuzz/pass_management/repeated_pass_instances.h"
|
||||
#include "source/fuzz/pass_management/repeated_pass_recommender.h"
|
||||
#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
// Each field of this enum corresponds to an available repeated pass
|
||||
// strategy, and is used to decide which kind of RepeatedPassManager object
|
||||
// to create.
|
||||
enum class RepeatedPassStrategy {
|
||||
kSimple,
|
||||
kRandomWithRecommendations,
|
||||
kLoopedWithRecommendations
|
||||
};
|
||||
|
||||
// An interface to encapsulate the manner in which the sequence of repeated
|
||||
// passes that are applied during fuzzing is chosen. An implementation of this
|
||||
// interface could, for example, keep track of the history of passes that have
|
||||
@ -40,6 +50,12 @@ class RepeatedPassManager {
|
||||
virtual FuzzerPass* ChoosePass(
|
||||
const protobufs::TransformationSequence& applied_transformations) = 0;
|
||||
|
||||
// Creates a corresponding RepeatedPassManager based on the |strategy|.
|
||||
static std::unique_ptr<RepeatedPassManager> Create(
|
||||
RepeatedPassStrategy strategy, FuzzerContext* fuzzer_context,
|
||||
RepeatedPassInstances* pass_instances,
|
||||
RepeatedPassRecommender* pass_recommender);
|
||||
|
||||
protected:
|
||||
FuzzerContext* GetFuzzerContext() { return fuzzer_context_; }
|
||||
|
||||
|
@ -128,8 +128,7 @@ TEST(FuzzerPassAddOpPhiSynonymsTest, HelperFunctions) {
|
||||
kConsoleMessageConsumer));
|
||||
TransformationContext transformation_context(
|
||||
MakeUnique<FactManager>(context.get()), validator_options);
|
||||
PseudoRandomGenerator prng(0);
|
||||
FuzzerContext fuzzer_context(&prng, 100);
|
||||
FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0), 100);
|
||||
protobufs::TransformationSequence transformation_sequence;
|
||||
|
||||
FuzzerPassAddOpPhiSynonyms fuzzer_pass(context.get(), &transformation_context,
|
||||
|
@ -77,7 +77,7 @@ TEST(FuzzerPassConstructCompositesTest, IsomorphicStructs) {
|
||||
const auto env = SPV_ENV_UNIVERSAL_1_3;
|
||||
const auto consumer = nullptr;
|
||||
|
||||
auto prng = MakeUnique<PseudoRandomGenerator>(0);
|
||||
FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0), 100);
|
||||
|
||||
for (uint32_t i = 0; i < 10; i++) {
|
||||
const auto context =
|
||||
@ -87,7 +87,6 @@ TEST(FuzzerPassConstructCompositesTest, IsomorphicStructs) {
|
||||
context.get(), validator_options, kConsoleMessageConsumer));
|
||||
TransformationContext transformation_context(
|
||||
MakeUnique<FactManager>(context.get()), validator_options);
|
||||
FuzzerContext fuzzer_context(prng.get(), 100);
|
||||
protobufs::TransformationSequence transformation_sequence;
|
||||
|
||||
FuzzerPassConstructComposites fuzzer_pass(
|
||||
@ -158,7 +157,7 @@ TEST(FuzzerPassConstructCompositesTest, IsomorphicArrays) {
|
||||
const auto env = SPV_ENV_UNIVERSAL_1_3;
|
||||
const auto consumer = nullptr;
|
||||
|
||||
auto prng = MakeUnique<PseudoRandomGenerator>(0);
|
||||
FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0), 100);
|
||||
|
||||
for (uint32_t i = 0; i < 10; i++) {
|
||||
const auto context =
|
||||
@ -168,7 +167,6 @@ TEST(FuzzerPassConstructCompositesTest, IsomorphicArrays) {
|
||||
context.get(), validator_options, kConsoleMessageConsumer));
|
||||
TransformationContext transformation_context(
|
||||
MakeUnique<FactManager>(context.get()), validator_options);
|
||||
FuzzerContext fuzzer_context(prng.get(), 100);
|
||||
protobufs::TransformationSequence transformation_sequence;
|
||||
|
||||
FuzzerPassConstructComposites fuzzer_pass(
|
||||
|
@ -204,8 +204,7 @@ TEST(FuzzerPassDonateModulesTest, BasicDonation) {
|
||||
TransformationContext transformation_context(
|
||||
MakeUnique<FactManager>(recipient_context.get()), validator_options);
|
||||
|
||||
PseudoRandomGenerator prng(0);
|
||||
FuzzerContext fuzzer_context(&prng, 100);
|
||||
FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0), 100);
|
||||
protobufs::TransformationSequence transformation_sequence;
|
||||
|
||||
FuzzerPassDonateModules fuzzer_pass(recipient_context.get(),
|
||||
@ -285,8 +284,7 @@ TEST(FuzzerPassDonateModulesTest, DonationWithUniforms) {
|
||||
TransformationContext transformation_context(
|
||||
MakeUnique<FactManager>(recipient_context.get()), validator_options);
|
||||
|
||||
PseudoRandomGenerator prng(0);
|
||||
FuzzerContext fuzzer_context(&prng, 100);
|
||||
FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0), 100);
|
||||
protobufs::TransformationSequence transformation_sequence;
|
||||
|
||||
FuzzerPassDonateModules fuzzer_pass(recipient_context.get(),
|
||||
@ -416,8 +414,7 @@ TEST(FuzzerPassDonateModulesTest, DonationWithInputAndOutputVariables) {
|
||||
TransformationContext transformation_context(
|
||||
MakeUnique<FactManager>(recipient_context.get()), validator_options);
|
||||
|
||||
PseudoRandomGenerator prng(0);
|
||||
FuzzerContext fuzzer_context(&prng, 100);
|
||||
FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0), 100);
|
||||
protobufs::TransformationSequence transformation_sequence;
|
||||
|
||||
FuzzerPassDonateModules fuzzer_pass(recipient_context.get(),
|
||||
@ -511,8 +508,7 @@ TEST(FuzzerPassDonateModulesTest, DonateFunctionTypeWithDifferentPointers) {
|
||||
TransformationContext transformation_context(
|
||||
MakeUnique<FactManager>(recipient_context.get()), validator_options);
|
||||
|
||||
PseudoRandomGenerator prng(0);
|
||||
FuzzerContext fuzzer_context(&prng, 100);
|
||||
FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0), 100);
|
||||
protobufs::TransformationSequence transformation_sequence;
|
||||
|
||||
FuzzerPassDonateModules fuzzer_pass(recipient_context.get(),
|
||||
@ -581,8 +577,7 @@ TEST(FuzzerPassDonateModulesTest, DonateOpConstantNull) {
|
||||
TransformationContext transformation_context(
|
||||
MakeUnique<FactManager>(recipient_context.get()), validator_options);
|
||||
|
||||
PseudoRandomGenerator prng(0);
|
||||
FuzzerContext fuzzer_context(&prng, 100);
|
||||
FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0), 100);
|
||||
protobufs::TransformationSequence transformation_sequence;
|
||||
|
||||
FuzzerPassDonateModules fuzzer_pass(recipient_context.get(),
|
||||
@ -709,8 +704,7 @@ TEST(FuzzerPassDonateModulesTest, DonateCodeThatUsesImages) {
|
||||
TransformationContext transformation_context(
|
||||
MakeUnique<FactManager>(recipient_context.get()), validator_options);
|
||||
|
||||
PseudoRandomGenerator prng(0);
|
||||
FuzzerContext fuzzer_context(&prng, 100);
|
||||
FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0), 100);
|
||||
protobufs::TransformationSequence transformation_sequence;
|
||||
|
||||
FuzzerPassDonateModules fuzzer_pass(recipient_context.get(),
|
||||
@ -805,8 +799,7 @@ TEST(FuzzerPassDonateModulesTest, DonateCodeThatUsesSampler) {
|
||||
TransformationContext transformation_context(
|
||||
MakeUnique<FactManager>(recipient_context.get()), validator_options);
|
||||
|
||||
PseudoRandomGenerator prng(0);
|
||||
FuzzerContext fuzzer_context(&prng, 100);
|
||||
FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0), 100);
|
||||
protobufs::TransformationSequence transformation_sequence;
|
||||
|
||||
FuzzerPassDonateModules fuzzer_pass(recipient_context.get(),
|
||||
@ -937,8 +930,7 @@ TEST(FuzzerPassDonateModulesTest, DonateCodeThatUsesImageStructField) {
|
||||
TransformationContext transformation_context(
|
||||
MakeUnique<FactManager>(recipient_context.get()), validator_options);
|
||||
|
||||
PseudoRandomGenerator prng(0);
|
||||
FuzzerContext fuzzer_context(&prng, 100);
|
||||
FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0), 100);
|
||||
protobufs::TransformationSequence transformation_sequence;
|
||||
|
||||
FuzzerPassDonateModules fuzzer_pass(recipient_context.get(),
|
||||
@ -1073,8 +1065,7 @@ TEST(FuzzerPassDonateModulesTest, DonateCodeThatUsesImageFunctionParameter) {
|
||||
TransformationContext transformation_context(
|
||||
MakeUnique<FactManager>(recipient_context.get()), validator_options);
|
||||
|
||||
PseudoRandomGenerator prng(0);
|
||||
FuzzerContext fuzzer_context(&prng, 100);
|
||||
FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0), 100);
|
||||
protobufs::TransformationSequence transformation_sequence;
|
||||
|
||||
FuzzerPassDonateModules fuzzer_pass(recipient_context.get(),
|
||||
@ -1155,8 +1146,7 @@ TEST(FuzzerPassDonateModulesTest, DonateShaderWithImageStorageClass) {
|
||||
TransformationContext transformation_context(
|
||||
MakeUnique<FactManager>(recipient_context.get()), validator_options);
|
||||
|
||||
PseudoRandomGenerator prng(0);
|
||||
FuzzerContext fuzzer_context(&prng, 100);
|
||||
FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0), 100);
|
||||
protobufs::TransformationSequence transformation_sequence;
|
||||
|
||||
FuzzerPassDonateModules fuzzer_pass(recipient_context.get(),
|
||||
@ -1242,8 +1232,7 @@ TEST(FuzzerPassDonateModulesTest, DonateComputeShaderWithRuntimeArray) {
|
||||
TransformationContext transformation_context(
|
||||
MakeUnique<FactManager>(recipient_context.get()), validator_options);
|
||||
|
||||
PseudoRandomGenerator prng(0);
|
||||
FuzzerContext fuzzer_context(&prng, 100);
|
||||
FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0), 100);
|
||||
protobufs::TransformationSequence transformation_sequence;
|
||||
|
||||
FuzzerPassDonateModules fuzzer_pass(recipient_context.get(),
|
||||
@ -1346,8 +1335,7 @@ TEST(FuzzerPassDonateModulesTest, DonateComputeShaderWithRuntimeArrayLivesafe) {
|
||||
TransformationContext transformation_context(
|
||||
MakeUnique<FactManager>(recipient_context.get()), validator_options);
|
||||
|
||||
PseudoRandomGenerator prng(0);
|
||||
FuzzerContext fuzzer_context(&prng, 100);
|
||||
FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0), 100);
|
||||
protobufs::TransformationSequence transformation_sequence;
|
||||
|
||||
FuzzerPassDonateModules fuzzer_pass(recipient_context.get(),
|
||||
@ -1418,8 +1406,7 @@ TEST(FuzzerPassDonateModulesTest, DonateComputeShaderWithWorkgroupVariables) {
|
||||
TransformationContext transformation_context(
|
||||
MakeUnique<FactManager>(recipient_context.get()), validator_options);
|
||||
|
||||
PseudoRandomGenerator prng(0);
|
||||
FuzzerContext fuzzer_context(&prng, 100);
|
||||
FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0), 100);
|
||||
protobufs::TransformationSequence transformation_sequence;
|
||||
|
||||
FuzzerPassDonateModules fuzzer_pass(recipient_context.get(),
|
||||
@ -1528,8 +1515,7 @@ TEST(FuzzerPassDonateModulesTest, DonateComputeShaderWithAtomics) {
|
||||
TransformationContext transformation_context(
|
||||
MakeUnique<FactManager>(recipient_context.get()), validator_options);
|
||||
|
||||
PseudoRandomGenerator prng(0);
|
||||
FuzzerContext fuzzer_context(&prng, 100);
|
||||
FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0), 100);
|
||||
protobufs::TransformationSequence transformation_sequence;
|
||||
|
||||
FuzzerPassDonateModules fuzzer_pass(recipient_context.get(),
|
||||
@ -1712,8 +1698,7 @@ TEST(FuzzerPassDonateModulesTest, Miscellaneous1) {
|
||||
TransformationContext transformation_context(
|
||||
MakeUnique<FactManager>(recipient_context.get()), validator_options);
|
||||
|
||||
PseudoRandomGenerator rng(0);
|
||||
FuzzerContext fuzzer_context(&rng, 100);
|
||||
FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0), 100);
|
||||
protobufs::TransformationSequence transformation_sequence;
|
||||
|
||||
FuzzerPassDonateModules fuzzer_pass(recipient_context.get(),
|
||||
@ -1784,8 +1769,7 @@ TEST(FuzzerPassDonateModulesTest, OpSpecConstantInstructions) {
|
||||
TransformationContext transformation_context(
|
||||
MakeUnique<FactManager>(recipient_context.get()), validator_options);
|
||||
|
||||
PseudoRandomGenerator prng(0);
|
||||
FuzzerContext fuzzer_context(&prng, 100);
|
||||
FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0), 100);
|
||||
protobufs::TransformationSequence transformation_sequence;
|
||||
|
||||
FuzzerPassDonateModules fuzzer_pass(recipient_context.get(),
|
||||
@ -1941,8 +1925,7 @@ TEST(FuzzerPassDonateModulesTest, DonationSupportsOpTypeRuntimeArray) {
|
||||
TransformationContext transformation_context(
|
||||
MakeUnique<FactManager>(recipient_context.get()), validator_options);
|
||||
|
||||
PseudoRandomGenerator rng(0);
|
||||
FuzzerContext fuzzer_context(&rng, 100);
|
||||
FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0), 100);
|
||||
protobufs::TransformationSequence transformation_sequence;
|
||||
|
||||
FuzzerPassDonateModules fuzzer_pass(recipient_context.get(),
|
||||
@ -2014,8 +1997,7 @@ TEST(FuzzerPassDonateModulesTest, HandlesCapabilities) {
|
||||
TransformationContext transformation_context(
|
||||
MakeUnique<FactManager>(recipient_context.get()), validator_options);
|
||||
|
||||
PseudoRandomGenerator rng(0);
|
||||
FuzzerContext fuzzer_context(&rng, 100);
|
||||
FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0), 100);
|
||||
protobufs::TransformationSequence transformation_sequence;
|
||||
|
||||
FuzzerPassDonateModules fuzzer_pass(recipient_context.get(),
|
||||
@ -2247,8 +2229,7 @@ TEST(FuzzerPassDonateModulesTest, HandlesOpPhisInMergeBlock) {
|
||||
TransformationContext transformation_context(
|
||||
MakeUnique<FactManager>(recipient_context.get()), validator_options);
|
||||
|
||||
PseudoRandomGenerator prng(0);
|
||||
FuzzerContext fuzzer_context(&prng, 100);
|
||||
FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0), 100);
|
||||
protobufs::TransformationSequence transformation_sequence;
|
||||
|
||||
FuzzerPassDonateModules fuzzer_pass(recipient_context.get(),
|
||||
|
@ -124,8 +124,7 @@ TEST(FuzzerPassOutlineFunctionsTest, EntryIsAlreadySuitable) {
|
||||
kConsoleMessageConsumer));
|
||||
TransformationContext transformation_context(
|
||||
MakeUnique<FactManager>(context.get()), validator_options);
|
||||
PseudoRandomGenerator prng(0);
|
||||
FuzzerContext fuzzer_context(&prng, 100);
|
||||
FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0), 100);
|
||||
protobufs::TransformationSequence transformation_sequence;
|
||||
|
||||
FuzzerPassOutlineFunctions fuzzer_pass(context.get(), &transformation_context,
|
||||
@ -167,8 +166,7 @@ TEST(FuzzerPassOutlineFunctionsTest, EntryHasOpVariable) {
|
||||
kConsoleMessageConsumer));
|
||||
TransformationContext transformation_context(
|
||||
MakeUnique<FactManager>(context.get()), validator_options);
|
||||
PseudoRandomGenerator prng(0);
|
||||
FuzzerContext fuzzer_context(&prng, 100);
|
||||
FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0), 100);
|
||||
protobufs::TransformationSequence transformation_sequence;
|
||||
|
||||
FuzzerPassOutlineFunctions fuzzer_pass(context.get(), &transformation_context,
|
||||
@ -291,8 +289,7 @@ TEST(FuzzerPassOutlineFunctionsTest, EntryBlockIsHeader) {
|
||||
kConsoleMessageConsumer));
|
||||
TransformationContext transformation_context(
|
||||
MakeUnique<FactManager>(context.get()), validator_options);
|
||||
PseudoRandomGenerator prng(0);
|
||||
FuzzerContext fuzzer_context(&prng, 100);
|
||||
FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0), 100);
|
||||
protobufs::TransformationSequence transformation_sequence;
|
||||
|
||||
FuzzerPassOutlineFunctions fuzzer_pass(context.get(), &transformation_context,
|
||||
@ -458,8 +455,7 @@ TEST(FuzzerPassOutlineFunctionsTest, ExitBlock) {
|
||||
kConsoleMessageConsumer));
|
||||
TransformationContext transformation_context(
|
||||
MakeUnique<FactManager>(context.get()), validator_options);
|
||||
PseudoRandomGenerator prng(0);
|
||||
FuzzerContext fuzzer_context(&prng, 100);
|
||||
FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0), 100);
|
||||
protobufs::TransformationSequence transformation_sequence;
|
||||
|
||||
FuzzerPassOutlineFunctions fuzzer_pass(context.get(), &transformation_context,
|
||||
|
@ -87,8 +87,7 @@ TEST(FuzzerPassTest, ForEachInstructionWithInstructionDescriptor) {
|
||||
ASSERT_TRUE(dominator_analysis->IsReachable(5));
|
||||
ASSERT_FALSE(dominator_analysis->IsReachable(8));
|
||||
|
||||
PseudoRandomGenerator prng(0);
|
||||
FuzzerContext fuzzer_context(&prng, 100);
|
||||
FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0), 100);
|
||||
protobufs::TransformationSequence transformations;
|
||||
FuzzerPassMock fuzzer_pass_mock(context.get(), &transformation_context,
|
||||
&fuzzer_context, &transformations);
|
||||
|
@ -12,12 +12,11 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "source/fuzz/fuzzer.h"
|
||||
#include "source/fuzz/replayer.h"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "source/fuzz/fuzzer.h"
|
||||
#include "source/fuzz/fuzzer_util.h"
|
||||
#include "source/fuzz/pseudo_random_generator.h"
|
||||
#include "source/fuzz/replayer.h"
|
||||
#include "source/fuzz/uniform_buffer_element_descriptor.h"
|
||||
#include "test/fuzz/fuzz_test_util.h"
|
||||
|
||||
@ -1642,37 +1641,53 @@ void RunFuzzerAndReplayer(const std::string& shader,
|
||||
});
|
||||
}
|
||||
|
||||
std::vector<Fuzzer::RepeatedPassStrategy> strategies{
|
||||
Fuzzer::RepeatedPassStrategy::kSimple,
|
||||
Fuzzer::RepeatedPassStrategy::kLoopedWithRecommendations,
|
||||
Fuzzer::RepeatedPassStrategy::kRandomWithRecommendations};
|
||||
std::vector<RepeatedPassStrategy> strategies{
|
||||
RepeatedPassStrategy::kSimple,
|
||||
RepeatedPassStrategy::kLoopedWithRecommendations,
|
||||
RepeatedPassStrategy::kRandomWithRecommendations};
|
||||
uint32_t strategy_index = 0;
|
||||
for (uint32_t seed = initial_seed; seed < initial_seed + num_runs; seed++) {
|
||||
spvtools::ValidatorOptions validator_options;
|
||||
|
||||
std::unique_ptr<opt::IRContext> ir_context;
|
||||
ASSERT_TRUE(fuzzerutil::BuildIRContext(env, kConsoleMessageConsumer,
|
||||
binary_in, validator_options,
|
||||
&ir_context));
|
||||
|
||||
auto fuzzer_context = MakeUnique<FuzzerContext>(
|
||||
MakeUnique<PseudoRandomGenerator>(seed),
|
||||
FuzzerContext::GetMinFreshId(ir_context.get()));
|
||||
|
||||
auto transformation_context = MakeUnique<TransformationContext>(
|
||||
MakeUnique<FactManager>(ir_context.get()), validator_options);
|
||||
transformation_context->GetFactManager()->AddInitialFacts(
|
||||
kConsoleMessageConsumer, initial_facts);
|
||||
|
||||
// Every 4th time we run the fuzzer, enable all fuzzer passes.
|
||||
bool enable_all_passes = (seed % 4) == 0;
|
||||
auto fuzzer_result =
|
||||
Fuzzer(env, kConsoleMessageConsumer, binary_in, initial_facts,
|
||||
donor_suppliers, MakeUnique<PseudoRandomGenerator>(seed),
|
||||
enable_all_passes, strategies[strategy_index], true,
|
||||
validator_options)
|
||||
.Run();
|
||||
Fuzzer fuzzer(std::move(ir_context), std::move(transformation_context),
|
||||
std::move(fuzzer_context), kConsoleMessageConsumer,
|
||||
donor_suppliers, enable_all_passes,
|
||||
strategies[strategy_index], true, validator_options);
|
||||
auto fuzzer_result = fuzzer.Run(0);
|
||||
|
||||
// Cycle the repeated pass strategy so that we try a different one next time
|
||||
// we run the fuzzer.
|
||||
strategy_index =
|
||||
(strategy_index + 1) % static_cast<uint32_t>(strategies.size());
|
||||
|
||||
ASSERT_EQ(Fuzzer::FuzzerResultStatus::kComplete, fuzzer_result.status);
|
||||
ASSERT_TRUE(t.Validate(fuzzer_result.transformed_binary));
|
||||
ASSERT_NE(Fuzzer::Status::kFuzzerPassLedToInvalidModule,
|
||||
fuzzer_result.status);
|
||||
std::vector<uint32_t> transformed_binary;
|
||||
fuzzer.GetIRContext()->module()->ToBinary(&transformed_binary, true);
|
||||
ASSERT_TRUE(t.Validate(transformed_binary));
|
||||
|
||||
auto replayer_result =
|
||||
Replayer(
|
||||
env, kConsoleMessageConsumer, binary_in, initial_facts,
|
||||
fuzzer_result.applied_transformations,
|
||||
static_cast<uint32_t>(
|
||||
fuzzer_result.applied_transformations.transformation_size()),
|
||||
false, validator_options)
|
||||
Replayer(env, kConsoleMessageConsumer, binary_in, initial_facts,
|
||||
fuzzer.GetTransformationSequence(),
|
||||
static_cast<uint32_t>(
|
||||
fuzzer.GetTransformationSequence().transformation_size()),
|
||||
false, validator_options)
|
||||
.Run();
|
||||
ASSERT_EQ(Replayer::ReplayerResultStatus::kComplete,
|
||||
replayer_result.status);
|
||||
@ -1682,12 +1697,12 @@ void RunFuzzerAndReplayer(const std::string& shader,
|
||||
// replay should be identical to that which resulted from fuzzing.
|
||||
std::string fuzzer_transformations_string;
|
||||
std::string replayer_transformations_string;
|
||||
fuzzer_result.applied_transformations.SerializeToString(
|
||||
fuzzer.GetTransformationSequence().SerializeToString(
|
||||
&fuzzer_transformations_string);
|
||||
replayer_result.applied_transformations.SerializeToString(
|
||||
&replayer_transformations_string);
|
||||
ASSERT_EQ(fuzzer_transformations_string, replayer_transformations_string);
|
||||
ASSERT_TRUE(IsEqual(env, fuzzer_result.transformed_binary,
|
||||
ASSERT_TRUE(IsEqual(env, transformed_binary,
|
||||
replayer_result.transformed_module.get()));
|
||||
}
|
||||
}
|
||||
|
@ -12,15 +12,14 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "source/fuzz/fuzzer.h"
|
||||
#include "source/fuzz/shrinker.h"
|
||||
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "source/fuzz/fuzzer.h"
|
||||
#include "source/fuzz/fuzzer_util.h"
|
||||
#include "source/fuzz/pseudo_random_generator.h"
|
||||
#include "source/fuzz/shrinker.h"
|
||||
#include "source/fuzz/uniform_buffer_element_descriptor.h"
|
||||
#include "test/fuzz/fuzz_test_util.h"
|
||||
|
||||
@ -1044,24 +1043,38 @@ void RunFuzzerAndShrinker(const std::string& shader,
|
||||
// Depending on the seed, decide whether to enable all passes and which
|
||||
// repeated pass manager to use.
|
||||
bool enable_all_passes = (seed % 4) == 0;
|
||||
Fuzzer::RepeatedPassStrategy repeated_pass_strategy;
|
||||
RepeatedPassStrategy repeated_pass_strategy;
|
||||
if ((seed % 3) == 0) {
|
||||
repeated_pass_strategy = Fuzzer::RepeatedPassStrategy::kSimple;
|
||||
repeated_pass_strategy = RepeatedPassStrategy::kSimple;
|
||||
} else if ((seed % 3) == 1) {
|
||||
repeated_pass_strategy =
|
||||
Fuzzer::RepeatedPassStrategy::kLoopedWithRecommendations;
|
||||
repeated_pass_strategy = RepeatedPassStrategy::kLoopedWithRecommendations;
|
||||
} else {
|
||||
repeated_pass_strategy =
|
||||
Fuzzer::RepeatedPassStrategy::kRandomWithRecommendations;
|
||||
repeated_pass_strategy = RepeatedPassStrategy::kRandomWithRecommendations;
|
||||
}
|
||||
|
||||
auto fuzzer_result =
|
||||
Fuzzer(env, kConsoleMessageConsumer, binary_in, initial_facts,
|
||||
donor_suppliers, MakeUnique<PseudoRandomGenerator>(seed),
|
||||
enable_all_passes, repeated_pass_strategy, true, validator_options)
|
||||
.Run();
|
||||
ASSERT_EQ(Fuzzer::FuzzerResultStatus::kComplete, fuzzer_result.status);
|
||||
ASSERT_TRUE(t.Validate(fuzzer_result.transformed_binary));
|
||||
std::unique_ptr<opt::IRContext> ir_context;
|
||||
ASSERT_TRUE(fuzzerutil::BuildIRContext(
|
||||
env, kConsoleMessageConsumer, binary_in, validator_options, &ir_context));
|
||||
|
||||
auto fuzzer_context =
|
||||
MakeUnique<FuzzerContext>(MakeUnique<PseudoRandomGenerator>(seed),
|
||||
FuzzerContext::GetMinFreshId(ir_context.get()));
|
||||
|
||||
auto transformation_context = MakeUnique<TransformationContext>(
|
||||
MakeUnique<FactManager>(ir_context.get()), validator_options);
|
||||
transformation_context->GetFactManager()->AddInitialFacts(
|
||||
kConsoleMessageConsumer, initial_facts);
|
||||
|
||||
Fuzzer fuzzer(std::move(ir_context), std::move(transformation_context),
|
||||
std::move(fuzzer_context), kConsoleMessageConsumer,
|
||||
donor_suppliers, enable_all_passes, repeated_pass_strategy,
|
||||
true, validator_options);
|
||||
auto fuzzer_result = fuzzer.Run(0);
|
||||
ASSERT_NE(Fuzzer::Status::kFuzzerPassLedToInvalidModule,
|
||||
fuzzer_result.status);
|
||||
std::vector<uint32_t> transformed_binary;
|
||||
fuzzer.GetIRContext()->module()->ToBinary(&transformed_binary, true);
|
||||
ASSERT_TRUE(t.Validate(transformed_binary));
|
||||
|
||||
const uint32_t kReasonableStepLimit = 50;
|
||||
const uint32_t kSmallStepLimit = 20;
|
||||
@ -1069,30 +1082,30 @@ void RunFuzzerAndShrinker(const std::string& shader,
|
||||
// With the AlwaysInteresting test, we should quickly shrink to the original
|
||||
// binary with no transformations remaining.
|
||||
RunAndCheckShrinker(env, binary_in, initial_facts,
|
||||
fuzzer_result.applied_transformations,
|
||||
fuzzer.GetTransformationSequence(),
|
||||
AlwaysInteresting().AsFunction(), binary_in, 0,
|
||||
kReasonableStepLimit, validator_options);
|
||||
|
||||
// With the OnlyInterestingFirstTime test, no shrinking should be achieved.
|
||||
RunAndCheckShrinker(
|
||||
env, binary_in, initial_facts, fuzzer_result.applied_transformations,
|
||||
OnlyInterestingFirstTime().AsFunction(), fuzzer_result.transformed_binary,
|
||||
env, binary_in, initial_facts, fuzzer.GetTransformationSequence(),
|
||||
OnlyInterestingFirstTime().AsFunction(), transformed_binary,
|
||||
static_cast<uint32_t>(
|
||||
fuzzer_result.applied_transformations.transformation_size()),
|
||||
fuzzer.GetTransformationSequence().transformation_size()),
|
||||
kReasonableStepLimit, validator_options);
|
||||
|
||||
// The PingPong test is unpredictable; passing an empty expected binary
|
||||
// means that we don't check anything beyond that shrinking completes
|
||||
// successfully.
|
||||
RunAndCheckShrinker(
|
||||
env, binary_in, initial_facts, fuzzer_result.applied_transformations,
|
||||
env, binary_in, initial_facts, fuzzer.GetTransformationSequence(),
|
||||
PingPong().AsFunction(), {}, 0, kSmallStepLimit, validator_options);
|
||||
|
||||
// The InterestingThenRandom test is unpredictable; passing an empty
|
||||
// expected binary means that we do not check anything about shrinking
|
||||
// results.
|
||||
RunAndCheckShrinker(
|
||||
env, binary_in, initial_facts, fuzzer_result.applied_transformations,
|
||||
env, binary_in, initial_facts, fuzzer.GetTransformationSequence(),
|
||||
InterestingThenRandom(PseudoRandomGenerator(seed)).AsFunction(), {}, 0,
|
||||
kSmallStepLimit, validator_options);
|
||||
}
|
||||
|
@ -163,8 +163,7 @@ TEST(ShrinkerTest, ReduceAddedFunctions) {
|
||||
ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
|
||||
donor_ir_context.get(), validator_options, kConsoleMessageConsumer));
|
||||
|
||||
PseudoRandomGenerator random_generator(0);
|
||||
FuzzerContext fuzzer_context(&random_generator, 100);
|
||||
FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0), 100);
|
||||
TransformationContext transformation_context(
|
||||
MakeUnique<FactManager>(variant_ir_context.get()), validator_options);
|
||||
|
||||
@ -341,8 +340,7 @@ TEST(ShrinkerTest, HitStepLimitWhenReducingAddedFunctions) {
|
||||
ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
|
||||
donor_ir_context.get(), validator_options, kConsoleMessageConsumer));
|
||||
|
||||
PseudoRandomGenerator random_generator(0);
|
||||
FuzzerContext fuzzer_context(&random_generator, 100);
|
||||
FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0), 100);
|
||||
TransformationContext transformation_context(
|
||||
MakeUnique<FactManager>(variant_ir_context.get()), validator_options);
|
||||
|
||||
|
@ -204,7 +204,7 @@ FuzzStatus ParseFlags(
|
||||
std::vector<std::string>* interestingness_test,
|
||||
std::string* shrink_transformations_file,
|
||||
std::string* shrink_temp_file_prefix,
|
||||
spvtools::fuzz::Fuzzer::RepeatedPassStrategy* repeated_pass_strategy,
|
||||
spvtools::fuzz::RepeatedPassStrategy* repeated_pass_strategy,
|
||||
spvtools::FuzzerOptions* fuzzer_options,
|
||||
spvtools::ValidatorOptions* validator_options) {
|
||||
uint32_t positional_arg_index = 0;
|
||||
@ -212,7 +212,7 @@ FuzzStatus ParseFlags(
|
||||
bool force_render_red = false;
|
||||
|
||||
*repeated_pass_strategy =
|
||||
spvtools::fuzz::Fuzzer::RepeatedPassStrategy::kLoopedWithRecommendations;
|
||||
spvtools::fuzz::RepeatedPassStrategy::kLoopedWithRecommendations;
|
||||
|
||||
for (int argi = 1; argi < argc; ++argi) {
|
||||
const char* cur_arg = argv[argi];
|
||||
@ -250,14 +250,14 @@ FuzzStatus ParseFlags(
|
||||
sizeof("--repeated-pass-strategy=") - 1)) {
|
||||
std::string strategy = spvtools::utils::SplitFlagArgs(cur_arg).second;
|
||||
if (strategy == "looped") {
|
||||
*repeated_pass_strategy = spvtools::fuzz::Fuzzer::
|
||||
RepeatedPassStrategy::kLoopedWithRecommendations;
|
||||
*repeated_pass_strategy =
|
||||
spvtools::fuzz::RepeatedPassStrategy::kLoopedWithRecommendations;
|
||||
} else if (strategy == "random") {
|
||||
*repeated_pass_strategy = spvtools::fuzz::Fuzzer::
|
||||
RepeatedPassStrategy::kRandomWithRecommendations;
|
||||
*repeated_pass_strategy =
|
||||
spvtools::fuzz::RepeatedPassStrategy::kRandomWithRecommendations;
|
||||
} else if (strategy == "simple") {
|
||||
*repeated_pass_strategy =
|
||||
spvtools::fuzz::Fuzzer::RepeatedPassStrategy::kSimple;
|
||||
spvtools::fuzz::RepeatedPassStrategy::kSimple;
|
||||
} else {
|
||||
std::stringstream ss;
|
||||
ss << "Unknown repeated pass strategy '" << strategy << "'"
|
||||
@ -549,7 +549,7 @@ bool Fuzz(const spv_target_env& target_env,
|
||||
const std::vector<uint32_t>& binary_in,
|
||||
const spvtools::fuzz::protobufs::FactSequence& initial_facts,
|
||||
const std::string& donors,
|
||||
spvtools::fuzz::Fuzzer::RepeatedPassStrategy repeated_pass_strategy,
|
||||
spvtools::fuzz::RepeatedPassStrategy repeated_pass_strategy,
|
||||
std::vector<uint32_t>* binary_out,
|
||||
spvtools::fuzz::protobufs::TransformationSequence*
|
||||
transformations_applied) {
|
||||
@ -578,24 +578,42 @@ bool Fuzz(const spv_target_env& target_env,
|
||||
});
|
||||
}
|
||||
|
||||
auto fuzz_result =
|
||||
spvtools::fuzz::Fuzzer(
|
||||
target_env, message_consumer, binary_in, initial_facts,
|
||||
donor_suppliers,
|
||||
spvtools::MakeUnique<spvtools::fuzz::PseudoRandomGenerator>(
|
||||
fuzzer_options->has_random_seed
|
||||
? fuzzer_options->random_seed
|
||||
: static_cast<uint32_t>(std::random_device()())),
|
||||
fuzzer_options->all_passes_enabled, repeated_pass_strategy,
|
||||
fuzzer_options->fuzzer_pass_validation_enabled, validator_options)
|
||||
.Run();
|
||||
*binary_out = std::move(fuzz_result.transformed_binary);
|
||||
*transformations_applied = std::move(fuzz_result.applied_transformations);
|
||||
if (fuzz_result.status !=
|
||||
spvtools::fuzz::Fuzzer::FuzzerResultStatus::kComplete) {
|
||||
std::unique_ptr<spvtools::opt::IRContext> ir_context;
|
||||
if (!spvtools::fuzz::fuzzerutil::BuildIRContext(target_env, message_consumer,
|
||||
binary_in, validator_options,
|
||||
&ir_context)) {
|
||||
spvtools::Error(FuzzDiagnostic, nullptr, {}, "Initial binary is invalid");
|
||||
return false;
|
||||
}
|
||||
|
||||
auto fuzzer_context = spvtools::MakeUnique<spvtools::fuzz::FuzzerContext>(
|
||||
spvtools::MakeUnique<spvtools::fuzz::PseudoRandomGenerator>(
|
||||
fuzzer_options->has_random_seed
|
||||
? fuzzer_options->random_seed
|
||||
: static_cast<uint32_t>(std::random_device()())),
|
||||
spvtools::fuzz::FuzzerContext::GetMinFreshId(ir_context.get()));
|
||||
|
||||
auto transformation_context =
|
||||
spvtools::MakeUnique<spvtools::fuzz::TransformationContext>(
|
||||
spvtools::MakeUnique<spvtools::fuzz::FactManager>(ir_context.get()),
|
||||
validator_options);
|
||||
transformation_context->GetFactManager()->AddInitialFacts(message_consumer,
|
||||
initial_facts);
|
||||
|
||||
spvtools::fuzz::Fuzzer fuzzer(
|
||||
std::move(ir_context), std::move(transformation_context),
|
||||
std::move(fuzzer_context), message_consumer, donor_suppliers,
|
||||
fuzzer_options->all_passes_enabled, repeated_pass_strategy,
|
||||
fuzzer_options->fuzzer_pass_validation_enabled, validator_options);
|
||||
auto fuzz_result = fuzzer.Run(0);
|
||||
if (fuzz_result.status ==
|
||||
spvtools::fuzz::Fuzzer::Status::kFuzzerPassLedToInvalidModule) {
|
||||
spvtools::Error(FuzzDiagnostic, nullptr, {}, "Error running fuzzer");
|
||||
return false;
|
||||
}
|
||||
|
||||
fuzzer.GetIRContext()->module()->ToBinary(binary_out, true);
|
||||
*transformations_applied = fuzzer.GetTransformationSequence();
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -656,7 +674,7 @@ int main(int argc, const char** argv) {
|
||||
std::vector<std::string> interestingness_test;
|
||||
std::string shrink_transformations_file;
|
||||
std::string shrink_temp_file_prefix = "temp_";
|
||||
spvtools::fuzz::Fuzzer::RepeatedPassStrategy repeated_pass_strategy;
|
||||
spvtools::fuzz::RepeatedPassStrategy repeated_pass_strategy;
|
||||
|
||||
spvtools::FuzzerOptions fuzzer_options;
|
||||
spvtools::ValidatorOptions validator_options;
|
||||
|
Loading…
Reference in New Issue
Block a user