spirv-fuzz: Add pass recommendations (#3757)

This change introduces various strategies for controlling the manner
in which fuzzer passes are applied repeatedly, including infrastructure
to allow fuzzer passes to be recommended based on which passes ran
previously.
This commit is contained in:
Alastair Donaldson 2020-09-18 15:51:35 +01:00 коммит произвёл GitHub
Родитель 2945963cce
Коммит 60ce96e2ff
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
28 изменённых файлов: 1471 добавлений и 229 удалений

Просмотреть файл

@ -715,6 +715,11 @@ SPIRV_TOOLS_EXPORT void spvFuzzerOptionsSetShrinkerStepLimit(
SPIRV_TOOLS_EXPORT void spvFuzzerOptionsEnableFuzzerPassValidation(
spv_fuzzer_options options);
// Enables all fuzzer passes during a fuzzing run (instead of a random subset
// of passes).
SPIRV_TOOLS_EXPORT void spvFuzzerOptionsEnableAllPasses(
spv_fuzzer_options options);
// Encodes the given SPIR-V assembly text to its binary representation. The
// length parameter specifies the number of bytes for text. Encoded binary will
// be stored into *binary. Any error will be written into *diagnostic if

Просмотреть файл

@ -247,6 +247,9 @@ class FuzzerOptions {
spvFuzzerOptionsEnableFuzzerPassValidation(options_);
}
// See spvFuzzerOptionsEnableAllPasses.
void enable_all_passes() { spvFuzzerOptionsEnableAllPasses(options_); }
private:
spv_fuzzer_options options_;
};

Просмотреть файл

@ -118,6 +118,13 @@ if(SPIRV_BUILD_FUZZER)
instruction_descriptor.h
instruction_message.h
overflow_id_source.h
pass_management/repeated_pass_instances.h
pass_management/repeated_pass_manager.h
pass_management/repeated_pass_manager_looped_with_recommendations.h
pass_management/repeated_pass_manager_random_with_recommendations.h
pass_management/repeated_pass_manager_simple.h
pass_management/repeated_pass_recommender.h
pass_management/repeated_pass_recommender_standard.h
protobufs/spirvfuzz_protobufs.h
pseudo_random_generator.h
random_generator.h
@ -285,6 +292,12 @@ if(SPIRV_BUILD_FUZZER)
instruction_descriptor.cpp
instruction_message.cpp
overflow_id_source.cpp
pass_management/repeated_pass_manager.cpp
pass_management/repeated_pass_manager_looped_with_recommendations.cpp
pass_management/repeated_pass_manager_random_with_recommendations.cpp
pass_management/repeated_pass_manager_simple.cpp
pass_management/repeated_pass_recommender.cpp
pass_management/repeated_pass_recommender_standard.cpp
pseudo_random_generator.cpp
random_generator.cpp
replayer.cpp

Просмотреть файл

@ -16,6 +16,7 @@
#include <cassert>
#include <memory>
#include <numeric>
#include "source/fuzz/fact_manager/fact_manager.h"
#include "source/fuzz/fuzzer_context.h"
@ -81,6 +82,11 @@
#include "source/fuzz/fuzzer_pass_swap_commutable_operands.h"
#include "source/fuzz/fuzzer_pass_swap_conditional_branch_operands.h"
#include "source/fuzz/fuzzer_pass_toggle_access_chain_instruction.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/pseudo_random_generator.h"
#include "source/fuzz/transformation_context.h"
@ -94,37 +100,21 @@ namespace fuzz {
namespace {
const uint32_t kIdBoundGap = 100;
const uint32_t kTransformationLimit = 500;
const uint32_t kChanceOfApplyingAnotherPass = 85;
// A convenience method to add a fuzzer pass to |passes| with probability 0.5.
// All fuzzer passes take |ir_context|, |transformation_context|,
// |fuzzer_context| and |transformation_sequence_out| as parameters. Extra
// arguments can be provided via |extra_args|.
template <typename T, typename... Args>
void MaybeAddPass(
std::vector<std::unique_ptr<FuzzerPass>>* passes,
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
protobufs::TransformationSequence* transformation_sequence_out,
Args&&... extra_args) {
if (fuzzer_context->ChooseEven()) {
passes->push_back(MakeUnique<T>(ir_context, transformation_context,
fuzzer_context, transformation_sequence_out,
std::forward<Args>(extra_args)...));
}
}
const uint32_t kTransformationLimit = 2000;
} // namespace
Fuzzer::Fuzzer(spv_target_env target_env, uint32_t seed,
Fuzzer::Fuzzer(spv_target_env target_env, uint32_t seed, bool enable_all_passes,
RepeatedPassStrategy repeated_pass_strategy,
bool validate_after_each_fuzzer_pass,
spv_validator_options validator_options)
: target_env_(target_env),
seed_(seed),
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) {}
validator_options_(validator_options),
num_repeated_passes_applied_(0) {}
Fuzzer::~Fuzzer() = default;
@ -132,6 +122,34 @@ void Fuzzer::SetMessageConsumer(MessageConsumer consumer) {
consumer_ = std::move(consumer);
}
template <typename FuzzerPassT, typename... Args>
void Fuzzer::MaybeAddRepeatedPass(
RepeatedPassInstances* pass_instances, opt::IRContext* ir_context,
TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
protobufs::TransformationSequence* transformation_sequence_out,
Args&&... extra_args) const {
if (enable_all_passes_ || fuzzer_context->ChooseEven()) {
pass_instances->SetPass(MakeUnique<FuzzerPassT>(
ir_context, transformation_context, fuzzer_context,
transformation_sequence_out, std::forward<Args>(extra_args)...));
}
}
template <typename FuzzerPassT, typename... Args>
void Fuzzer::MaybeAddFinalPass(
std::vector<std::unique_ptr<FuzzerPass>>* passes,
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
protobufs::TransformationSequence* transformation_sequence_out,
Args&&... extra_args) const {
if (enable_all_passes_ || fuzzer_context->ChooseEven()) {
passes->push_back(MakeUnique<FuzzerPassT>(
ir_context, transformation_context, fuzzer_context,
transformation_sequence_out, std::forward<Args>(extra_args)...));
}
}
bool Fuzzer::ApplyPassAndCheckValidity(
FuzzerPass* pass, const opt::IRContext& ir_context,
const spvtools::SpirvTools& tools) const {
@ -155,7 +173,7 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run(
const protobufs::FactSequence& initial_facts,
const std::vector<fuzzerutil::ModuleSupplier>& donor_suppliers,
std::vector<uint32_t>* binary_out,
protobufs::TransformationSequence* transformation_sequence_out) const {
protobufs::TransformationSequence* transformation_sequence_out) {
// Check compatibility between the library version being linked with and the
// header files being used.
GOOGLE_PROTOBUF_VERIFY_VERSION;
@ -198,214 +216,233 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run(
TransformationContext transformation_context(&fact_manager,
validator_options_);
// Apply some semantics-preserving passes.
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3764): Enable
// certain passes to run with a higher priority than the others.
std::vector<std::unique_ptr<FuzzerPass>> passes;
while (passes.empty()) {
MaybeAddPass<FuzzerPassAddAccessChains>(
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
MaybeAddPass<FuzzerPassAddBitInstructionSynonyms>(
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
MaybeAddPass<FuzzerPassAddCompositeInserts>(
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
MaybeAddPass<FuzzerPassAddCompositeTypes>(
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
MaybeAddPass<FuzzerPassAddCopyMemory>(
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
MaybeAddPass<FuzzerPassAddDeadBlocks>(
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
MaybeAddPass<FuzzerPassAddDeadBreaks>(
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
MaybeAddPass<FuzzerPassAddDeadContinues>(
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
MaybeAddPass<FuzzerPassAddEquationInstructions>(
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
MaybeAddPass<FuzzerPassAddFunctionCalls>(
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
MaybeAddPass<FuzzerPassAddGlobalVariables>(
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
MaybeAddPass<FuzzerPassAddImageSampleUnusedComponents>(
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
MaybeAddPass<FuzzerPassAddLoads>(&passes, ir_context.get(),
&transformation_context, &fuzzer_context,
transformation_sequence_out);
MaybeAddPass<FuzzerPassAddLocalVariables>(
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
MaybeAddPass<FuzzerPassAddLoopPreheaders>(
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
MaybeAddPass<FuzzerPassAddOpPhiSynonyms>(
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
MaybeAddPass<FuzzerPassAddParameters>(
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
MaybeAddPass<FuzzerPassAddRelaxedDecorations>(
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
MaybeAddPass<FuzzerPassAddStores>(&passes, ir_context.get(),
&transformation_context, &fuzzer_context,
transformation_sequence_out);
MaybeAddPass<FuzzerPassAddSynonyms>(
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
MaybeAddPass<FuzzerPassAddVectorShuffleInstructions>(
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
MaybeAddPass<FuzzerPassApplyIdSynonyms>(
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
MaybeAddPass<FuzzerPassConstructComposites>(
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
MaybeAddPass<FuzzerPassCopyObjects>(
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
MaybeAddPass<FuzzerPassDonateModules>(
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out, donor_suppliers);
MaybeAddPass<FuzzerPassDuplicateRegionsWithSelections>(
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
MaybeAddPass<FuzzerPassFlattenConditionalBranches>(
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
MaybeAddPass<FuzzerPassInlineFunctions>(
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
MaybeAddPass<FuzzerPassInvertComparisonOperators>(
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
MaybeAddPass<FuzzerPassMakeVectorOperationsDynamic>(
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
MaybeAddPass<FuzzerPassMergeBlocks>(
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
MaybeAddPass<FuzzerPassMutatePointers>(
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
MaybeAddPass<FuzzerPassObfuscateConstants>(
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
MaybeAddPass<FuzzerPassOutlineFunctions>(
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
MaybeAddPass<FuzzerPassPermuteBlocks>(
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
MaybeAddPass<FuzzerPassPermuteFunctionParameters>(
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
MaybeAddPass<FuzzerPassPermuteInstructions>(
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
MaybeAddPass<FuzzerPassPropagateInstructionsUp>(
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
MaybeAddPass<FuzzerPassPushIdsThroughVariables>(
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
MaybeAddPass<FuzzerPassReplaceAddsSubsMulsWithCarryingExtended>(
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
MaybeAddPass<FuzzerPassReplaceCopyMemoriesWithLoadsStores>(
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
MaybeAddPass<FuzzerPassReplaceCopyObjectsWithStoresLoads>(
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
MaybeAddPass<FuzzerPassReplaceLoadsStoresWithCopyMemories>(
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
MaybeAddPass<FuzzerPassReplaceParameterWithGlobal>(
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
MaybeAddPass<FuzzerPassReplaceLinearAlgebraInstructions>(
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
MaybeAddPass<FuzzerPassReplaceOpSelectsWithConditionalBranches>(
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
MaybeAddPass<FuzzerPassReplaceParamsWithStruct>(
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
MaybeAddPass<FuzzerPassSplitBlocks>(
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
MaybeAddPass<FuzzerPassSwapBranchConditionalOperands>(
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
RepeatedPassInstances 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.
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3764): Consider
// enabling some passes always, or with higher probability.
MaybeAddRepeatedPass<FuzzerPassAddAccessChains>(
&pass_instances, ir_context.get(), &transformation_context,
&fuzzer_context, transformation_sequence_out);
MaybeAddRepeatedPass<FuzzerPassAddBitInstructionSynonyms>(
&pass_instances, ir_context.get(), &transformation_context,
&fuzzer_context, transformation_sequence_out);
MaybeAddRepeatedPass<FuzzerPassAddCompositeInserts>(
&pass_instances, ir_context.get(), &transformation_context,
&fuzzer_context, transformation_sequence_out);
MaybeAddRepeatedPass<FuzzerPassAddCompositeTypes>(
&pass_instances, ir_context.get(), &transformation_context,
&fuzzer_context, transformation_sequence_out);
MaybeAddRepeatedPass<FuzzerPassAddCopyMemory>(
&pass_instances, ir_context.get(), &transformation_context,
&fuzzer_context, transformation_sequence_out);
MaybeAddRepeatedPass<FuzzerPassAddDeadBlocks>(
&pass_instances, ir_context.get(), &transformation_context,
&fuzzer_context, transformation_sequence_out);
MaybeAddRepeatedPass<FuzzerPassAddDeadBreaks>(
&pass_instances, ir_context.get(), &transformation_context,
&fuzzer_context, transformation_sequence_out);
MaybeAddRepeatedPass<FuzzerPassAddDeadContinues>(
&pass_instances, ir_context.get(), &transformation_context,
&fuzzer_context, transformation_sequence_out);
MaybeAddRepeatedPass<FuzzerPassAddEquationInstructions>(
&pass_instances, ir_context.get(), &transformation_context,
&fuzzer_context, transformation_sequence_out);
MaybeAddRepeatedPass<FuzzerPassAddFunctionCalls>(
&pass_instances, ir_context.get(), &transformation_context,
&fuzzer_context, transformation_sequence_out);
MaybeAddRepeatedPass<FuzzerPassAddGlobalVariables>(
&pass_instances, ir_context.get(), &transformation_context,
&fuzzer_context, transformation_sequence_out);
MaybeAddRepeatedPass<FuzzerPassAddImageSampleUnusedComponents>(
&pass_instances, ir_context.get(), &transformation_context,
&fuzzer_context, transformation_sequence_out);
MaybeAddRepeatedPass<FuzzerPassAddLoads>(
&pass_instances, ir_context.get(), &transformation_context,
&fuzzer_context, transformation_sequence_out);
MaybeAddRepeatedPass<FuzzerPassAddLocalVariables>(
&pass_instances, ir_context.get(), &transformation_context,
&fuzzer_context, transformation_sequence_out);
MaybeAddRepeatedPass<FuzzerPassAddLoopPreheaders>(
&pass_instances, ir_context.get(), &transformation_context,
&fuzzer_context, transformation_sequence_out);
MaybeAddRepeatedPass<FuzzerPassAddOpPhiSynonyms>(
&pass_instances, ir_context.get(), &transformation_context,
&fuzzer_context, transformation_sequence_out);
MaybeAddRepeatedPass<FuzzerPassAddParameters>(
&pass_instances, ir_context.get(), &transformation_context,
&fuzzer_context, transformation_sequence_out);
MaybeAddRepeatedPass<FuzzerPassAddRelaxedDecorations>(
&pass_instances, ir_context.get(), &transformation_context,
&fuzzer_context, transformation_sequence_out);
MaybeAddRepeatedPass<FuzzerPassAddStores>(
&pass_instances, ir_context.get(), &transformation_context,
&fuzzer_context, transformation_sequence_out);
MaybeAddRepeatedPass<FuzzerPassAddSynonyms>(
&pass_instances, ir_context.get(), &transformation_context,
&fuzzer_context, transformation_sequence_out);
MaybeAddRepeatedPass<FuzzerPassAddVectorShuffleInstructions>(
&pass_instances, ir_context.get(), &transformation_context,
&fuzzer_context, transformation_sequence_out);
MaybeAddRepeatedPass<FuzzerPassApplyIdSynonyms>(
&pass_instances, ir_context.get(), &transformation_context,
&fuzzer_context, transformation_sequence_out);
MaybeAddRepeatedPass<FuzzerPassConstructComposites>(
&pass_instances, ir_context.get(), &transformation_context,
&fuzzer_context, transformation_sequence_out);
MaybeAddRepeatedPass<FuzzerPassCopyObjects>(
&pass_instances, ir_context.get(), &transformation_context,
&fuzzer_context, transformation_sequence_out);
MaybeAddRepeatedPass<FuzzerPassDonateModules>(
&pass_instances, ir_context.get(), &transformation_context,
&fuzzer_context, transformation_sequence_out, donor_suppliers);
MaybeAddRepeatedPass<FuzzerPassDuplicateRegionsWithSelections>(
&pass_instances, ir_context.get(), &transformation_context,
&fuzzer_context, transformation_sequence_out);
MaybeAddRepeatedPass<FuzzerPassFlattenConditionalBranches>(
&pass_instances, ir_context.get(), &transformation_context,
&fuzzer_context, transformation_sequence_out);
MaybeAddRepeatedPass<FuzzerPassInlineFunctions>(
&pass_instances, ir_context.get(), &transformation_context,
&fuzzer_context, transformation_sequence_out);
MaybeAddRepeatedPass<FuzzerPassInvertComparisonOperators>(
&pass_instances, ir_context.get(), &transformation_context,
&fuzzer_context, transformation_sequence_out);
MaybeAddRepeatedPass<FuzzerPassMakeVectorOperationsDynamic>(
&pass_instances, ir_context.get(), &transformation_context,
&fuzzer_context, transformation_sequence_out);
MaybeAddRepeatedPass<FuzzerPassMergeBlocks>(
&pass_instances, ir_context.get(), &transformation_context,
&fuzzer_context, transformation_sequence_out);
MaybeAddRepeatedPass<FuzzerPassMutatePointers>(
&pass_instances, ir_context.get(), &transformation_context,
&fuzzer_context, transformation_sequence_out);
MaybeAddRepeatedPass<FuzzerPassObfuscateConstants>(
&pass_instances, ir_context.get(), &transformation_context,
&fuzzer_context, transformation_sequence_out);
MaybeAddRepeatedPass<FuzzerPassOutlineFunctions>(
&pass_instances, ir_context.get(), &transformation_context,
&fuzzer_context, transformation_sequence_out);
MaybeAddRepeatedPass<FuzzerPassPermuteBlocks>(
&pass_instances, ir_context.get(), &transformation_context,
&fuzzer_context, transformation_sequence_out);
MaybeAddRepeatedPass<FuzzerPassPermuteFunctionParameters>(
&pass_instances, ir_context.get(), &transformation_context,
&fuzzer_context, transformation_sequence_out);
MaybeAddRepeatedPass<FuzzerPassPermuteInstructions>(
&pass_instances, ir_context.get(), &transformation_context,
&fuzzer_context, transformation_sequence_out);
MaybeAddRepeatedPass<FuzzerPassPropagateInstructionsUp>(
&pass_instances, ir_context.get(), &transformation_context,
&fuzzer_context, transformation_sequence_out);
MaybeAddRepeatedPass<FuzzerPassPushIdsThroughVariables>(
&pass_instances, ir_context.get(), &transformation_context,
&fuzzer_context, transformation_sequence_out);
MaybeAddRepeatedPass<FuzzerPassReplaceAddsSubsMulsWithCarryingExtended>(
&pass_instances, ir_context.get(), &transformation_context,
&fuzzer_context, transformation_sequence_out);
MaybeAddRepeatedPass<FuzzerPassReplaceCopyMemoriesWithLoadsStores>(
&pass_instances, ir_context.get(), &transformation_context,
&fuzzer_context, transformation_sequence_out);
MaybeAddRepeatedPass<FuzzerPassReplaceCopyObjectsWithStoresLoads>(
&pass_instances, ir_context.get(), &transformation_context,
&fuzzer_context, transformation_sequence_out);
MaybeAddRepeatedPass<FuzzerPassReplaceLoadsStoresWithCopyMemories>(
&pass_instances, ir_context.get(), &transformation_context,
&fuzzer_context, transformation_sequence_out);
MaybeAddRepeatedPass<FuzzerPassReplaceParameterWithGlobal>(
&pass_instances, ir_context.get(), &transformation_context,
&fuzzer_context, transformation_sequence_out);
MaybeAddRepeatedPass<FuzzerPassReplaceLinearAlgebraInstructions>(
&pass_instances, ir_context.get(), &transformation_context,
&fuzzer_context, transformation_sequence_out);
MaybeAddRepeatedPass<FuzzerPassReplaceIrrelevantIds>(
&pass_instances, ir_context.get(), &transformation_context,
&fuzzer_context, transformation_sequence_out);
MaybeAddRepeatedPass<FuzzerPassReplaceOpPhiIdsFromDeadPredecessors>(
&pass_instances, ir_context.get(), &transformation_context,
&fuzzer_context, transformation_sequence_out);
MaybeAddRepeatedPass<FuzzerPassReplaceOpSelectsWithConditionalBranches>(
&pass_instances, ir_context.get(), &transformation_context,
&fuzzer_context, transformation_sequence_out);
MaybeAddRepeatedPass<FuzzerPassReplaceParamsWithStruct>(
&pass_instances, ir_context.get(), &transformation_context,
&fuzzer_context, transformation_sequence_out);
MaybeAddRepeatedPass<FuzzerPassSplitBlocks>(
&pass_instances, ir_context.get(), &transformation_context,
&fuzzer_context, transformation_sequence_out);
MaybeAddRepeatedPass<FuzzerPassSwapBranchConditionalOperands>(
&pass_instances, ir_context.get(), &transformation_context,
&fuzzer_context, transformation_sequence_out);
// 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);
std::unique_ptr<RepeatedPassManager> repeated_pass_manager = nullptr;
switch (repeated_pass_strategy_) {
case RepeatedPassStrategy::kSimple:
repeated_pass_manager = MakeUnique<RepeatedPassManagerSimple>(
&fuzzer_context, &pass_instances);
break;
case RepeatedPassStrategy::kLoopedWithRecommendations:
repeated_pass_manager =
MakeUnique<RepeatedPassManagerLoopedWithRecommendations>(
&fuzzer_context, &pass_instances, &pass_recommender);
break;
case RepeatedPassStrategy::kRandomWithRecommendations:
repeated_pass_manager =
MakeUnique<RepeatedPassManagerRandomWithRecommendations>(
&fuzzer_context, &pass_instances, &pass_recommender);
break;
}
bool is_first = true;
while (static_cast<uint32_t>(
transformation_sequence_out->transformation_size()) <
kTransformationLimit &&
(is_first ||
fuzzer_context.ChoosePercentage(kChanceOfApplyingAnotherPass))) {
is_first = false;
if (!ApplyPassAndCheckValidity(
passes[fuzzer_context.RandomIndex(passes)].get(), *ir_context,
tools)) {
do {
if (!ApplyPassAndCheckValidity(repeated_pass_manager->ChoosePass(),
*ir_context, tools)) {
return Fuzzer::FuzzerResultStatus::kFuzzerPassLedToInvalidModule;
}
}
} while (
ShouldContinueFuzzing(*transformation_sequence_out, &fuzzer_context));
// 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;
MaybeAddPass<FuzzerPassAdjustBranchWeights>(
MaybeAddFinalPass<FuzzerPassAdjustBranchWeights>(
&final_passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
MaybeAddPass<FuzzerPassAdjustFunctionControls>(
MaybeAddFinalPass<FuzzerPassAdjustFunctionControls>(
&final_passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
MaybeAddPass<FuzzerPassAdjustLoopControls>(
MaybeAddFinalPass<FuzzerPassAdjustLoopControls>(
&final_passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
MaybeAddPass<FuzzerPassAdjustMemoryOperandsMasks>(
MaybeAddFinalPass<FuzzerPassAdjustMemoryOperandsMasks>(
&final_passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
MaybeAddPass<FuzzerPassAdjustSelectionControls>(
MaybeAddFinalPass<FuzzerPassAdjustSelectionControls>(
&final_passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
MaybeAddPass<FuzzerPassAddNoContractionDecorations>(
MaybeAddFinalPass<FuzzerPassAddNoContractionDecorations>(
&final_passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
MaybeAddPass<FuzzerPassInterchangeSignednessOfIntegerOperands>(
MaybeAddFinalPass<FuzzerPassInterchangeSignednessOfIntegerOperands>(
&final_passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
MaybeAddPass<FuzzerPassInterchangeZeroLikeConstants>(
MaybeAddFinalPass<FuzzerPassInterchangeZeroLikeConstants>(
&final_passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
MaybeAddPass<FuzzerPassPermutePhiOperands>(
MaybeAddFinalPass<FuzzerPassPermutePhiOperands>(
&final_passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
MaybeAddPass<FuzzerPassReplaceOpPhiIdsFromDeadPredecessors>(
MaybeAddFinalPass<FuzzerPassSwapCommutableOperands>(
&final_passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
MaybeAddPass<FuzzerPassReplaceIrrelevantIds>(
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
MaybeAddPass<FuzzerPassSwapCommutableOperands>(
&final_passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
MaybeAddPass<FuzzerPassToggleAccessChainInstruction>(
MaybeAddFinalPass<FuzzerPassToggleAccessChainInstruction>(
&final_passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
for (auto& pass : final_passes) {
@ -420,5 +457,38 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run(
return Fuzzer::FuzzerResultStatus::kComplete;
}
bool Fuzzer::ShouldContinueFuzzing(
const protobufs::TransformationSequence& transformation_sequence_out,
FuzzerContext* fuzzer_context) {
// 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_ <= kTransformationLimit &&
"The number of repeated passes applied must not exceed its upper limit.");
if (num_repeated_passes_applied_ == kTransformationLimit) {
// Stop because fuzzing has got stuck.
return false;
}
auto transformations_applied_so_far =
static_cast<uint32_t>(transformation_sequence_out.transformation_size());
if (transformations_applied_so_far >= kTransformationLimit) {
// Stop because we have reached the transformation limit.
return false;
}
auto chance_of_continuing = static_cast<uint32_t>(
100.0 * (1.0 - (static_cast<double>(transformations_applied_so_far) /
static_cast<double>(kTransformationLimit))));
if (!fuzzer_context->ChoosePercentage(chance_of_continuing)) {
// We have probabilistically decided to stop.
return false;
}
// Continue fuzzing!
num_repeated_passes_applied_++;
return true;
}
} // namespace fuzz
} // namespace spvtools

Просмотреть файл

@ -18,8 +18,11 @@
#include <memory>
#include <vector>
#include "source/fuzz/fuzzer_context.h"
#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_recommender.h"
#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
#include "spirv-tools/libspirv.hpp"
@ -38,11 +41,24 @@ class Fuzzer {
kInitialBinaryInvalid,
};
// 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
};
// Constructs a fuzzer from the given target environment |target_env|. |seed|
// is a seed for pseudo-random number generation.
// |validate_after_each_fuzzer_pass| controls whether the validator will be
// invoked after every fuzzer pass is applied.
Fuzzer(spv_target_env target_env, uint32_t seed,
// is a seed for pseudo-random number generation. If |enable_all_passes| is
// true then all fuzzer passes will be enabled, otherwise a random subset of
// fuzzer passes will be enabled. |validate_after_each_fuzzer_pass| controls
// whether the validator will be invoked after every fuzzer pass is applied,
// and |validator_options| provides the options that should be used during
// validation if so.
Fuzzer(spv_target_env target_env, uint32_t seed, bool enable_all_passes,
RepeatedPassStrategy repeated_pass_strategy,
bool validate_after_each_fuzzer_pass,
spv_validator_options validator_options);
@ -69,9 +85,43 @@ class Fuzzer {
const protobufs::FactSequence& initial_facts,
const std::vector<fuzzerutil::ModuleSupplier>& donor_suppliers,
std::vector<uint32_t>* binary_out,
protobufs::TransformationSequence* transformation_sequence_out) const;
protobufs::TransformationSequence* transformation_sequence_out);
private:
// A convenience method to add a repeated fuzzer pass to |pass_instances| with
// probability 0.5, or with probability 1 if |enable_all_passes_| is true.
//
// All fuzzer passes take |ir_context|, |transformation_context|,
// |fuzzer_context| and |transformation_sequence_out| as parameters. Extra
// arguments can be provided via |extra_args|.
template <typename FuzzerPassT, typename... Args>
void MaybeAddRepeatedPass(
RepeatedPassInstances* pass_instances, opt::IRContext* ir_context,
TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
protobufs::TransformationSequence* transformation_sequence_out,
Args&&... extra_args) const;
// A convenience method to add a final fuzzer pass to |passes| with
// probability 0.5, or with probability 1 if |enable_all_passes_| is true.
//
// All fuzzer passes take |ir_context|, |transformation_context|,
// |fuzzer_context| and |transformation_sequence_out| as parameters. Extra
// arguments can be provided via |extra_args|.
template <typename FuzzerPassT, typename... Args>
void MaybeAddFinalPass(
std::vector<std::unique_ptr<FuzzerPass>>* passes,
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
protobufs::TransformationSequence* transformation_sequence_out,
Args&&... extra_args) const;
// Decides whether to apply more repeated passes. The probability decreases as
// the number of transformations that have been applied increases.
bool ShouldContinueFuzzing(
const protobufs::TransformationSequence& transformation_sequence_out,
FuzzerContext* fuzzer_context);
// Applies |pass|, which must be a pass constructed with |ir_context|, and
// then returns true if and only if |ir_context| is valid. |tools| is used to
// check validity.
@ -88,11 +138,23 @@ class Fuzzer {
// Seed for random number generator.
const uint32_t seed_;
// 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_;
// Determines whether the validator should be invoked after every fuzzer pass.
bool validate_after_each_fuzzer_pass_;
// Options to control validation.
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_;
};
} // namespace fuzz

Просмотреть файл

@ -23,7 +23,11 @@ namespace {
// Default <minimum, maximum> pairs of probabilities for applying various
// transformations. All values are percentages. Keep them in alphabetical order.
const std::pair<uint32_t, uint32_t>
kChanceOfAcceptingRepeatedPassRecommendation = {70, 100};
const std::pair<uint32_t, uint32_t> kChanceOfAddingAccessChain = {5, 50};
const std::pair<uint32_t, uint32_t> kChanceOfAddingAnotherPassToPassLoop = {85,
95};
const std::pair<uint32_t, uint32_t> kChanceOfAddingAnotherStructField = {20,
90};
const std::pair<uint32_t, uint32_t> kChanceOfAddingArrayOrStructType = {20, 90};
@ -168,8 +172,12 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator,
kGetDefaultMaxNumberOfParametersReplacedWithStruct),
go_deeper_in_constant_obfuscation_(
kDefaultGoDeeperInConstantObfuscation) {
chance_of_accepting_repeated_pass_recommendation_ =
ChooseBetweenMinAndMax(kChanceOfAcceptingRepeatedPassRecommendation);
chance_of_adding_access_chain_ =
ChooseBetweenMinAndMax(kChanceOfAddingAccessChain);
chance_of_adding_another_pass_to_pass_loop_ =
ChooseBetweenMinAndMax(kChanceOfAddingAnotherPassToPassLoop);
chance_of_adding_another_struct_field_ =
ChooseBetweenMinAndMax(kChanceOfAddingAnotherStructField);
chance_of_adding_array_or_struct_type_ =

Просмотреть файл

@ -106,9 +106,15 @@ class FuzzerContext {
// Probabilities associated with applying various transformations.
// Keep them in alphabetical order.
uint32_t GetChanceOfAcceptingRepeatedPassRecommendation() {
return chance_of_accepting_repeated_pass_recommendation_;
}
uint32_t GetChanceOfAddingAccessChain() {
return chance_of_adding_access_chain_;
}
uint32_t GetChanceOfAddingAnotherPassToPassLoop() {
return chance_of_adding_another_pass_to_pass_loop_;
}
uint32_t GetChanceOfAddingAnotherStructField() {
return chance_of_adding_another_struct_field_;
}
@ -379,7 +385,9 @@ class FuzzerContext {
// Probabilities associated with applying various transformations.
// Keep them in alphabetical order.
uint32_t chance_of_accepting_repeated_pass_recommendation_;
uint32_t chance_of_adding_access_chain_;
uint32_t chance_of_adding_another_pass_to_pass_loop_;
uint32_t chance_of_adding_another_struct_field_;
uint32_t chance_of_adding_array_or_struct_type_;
uint32_t chance_of_adding_bit_instruction_synonym_;

Просмотреть файл

@ -81,8 +81,7 @@ void FuzzerPassAddCompositeInserts::Apply() {
}
// No components of the composite can be pointers.
// TODO:
// (https://github.com/KhronosGroup/SPIRV-Tools/issues/3658)
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3658):
// Structs can have components of pointer type.
// FindOrCreateZeroConstant cannot be called on a
// pointer. We ignore pointers for now. Consider adding

Просмотреть файл

@ -0,0 +1,173 @@
// Copyright (c) 2020 Google LLC
//
// 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.
#ifndef SOURCE_FUZZ_REPEATED_PASS_INSTANCES_H_
#define SOURCE_FUZZ_REPEATED_PASS_INSTANCES_H_
#include "source/fuzz/fuzzer_pass_add_access_chains.h"
#include "source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.h"
#include "source/fuzz/fuzzer_pass_add_composite_inserts.h"
#include "source/fuzz/fuzzer_pass_add_composite_types.h"
#include "source/fuzz/fuzzer_pass_add_copy_memory.h"
#include "source/fuzz/fuzzer_pass_add_dead_blocks.h"
#include "source/fuzz/fuzzer_pass_add_dead_breaks.h"
#include "source/fuzz/fuzzer_pass_add_dead_continues.h"
#include "source/fuzz/fuzzer_pass_add_equation_instructions.h"
#include "source/fuzz/fuzzer_pass_add_function_calls.h"
#include "source/fuzz/fuzzer_pass_add_global_variables.h"
#include "source/fuzz/fuzzer_pass_add_image_sample_unused_components.h"
#include "source/fuzz/fuzzer_pass_add_loads.h"
#include "source/fuzz/fuzzer_pass_add_local_variables.h"
#include "source/fuzz/fuzzer_pass_add_loop_preheaders.h"
#include "source/fuzz/fuzzer_pass_add_opphi_synonyms.h"
#include "source/fuzz/fuzzer_pass_add_parameters.h"
#include "source/fuzz/fuzzer_pass_add_relaxed_decorations.h"
#include "source/fuzz/fuzzer_pass_add_stores.h"
#include "source/fuzz/fuzzer_pass_add_synonyms.h"
#include "source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.h"
#include "source/fuzz/fuzzer_pass_apply_id_synonyms.h"
#include "source/fuzz/fuzzer_pass_construct_composites.h"
#include "source/fuzz/fuzzer_pass_copy_objects.h"
#include "source/fuzz/fuzzer_pass_donate_modules.h"
#include "source/fuzz/fuzzer_pass_duplicate_regions_with_selections.h"
#include "source/fuzz/fuzzer_pass_flatten_conditional_branches.h"
#include "source/fuzz/fuzzer_pass_inline_functions.h"
#include "source/fuzz/fuzzer_pass_invert_comparison_operators.h"
#include "source/fuzz/fuzzer_pass_make_vector_operations_dynamic.h"
#include "source/fuzz/fuzzer_pass_merge_blocks.h"
#include "source/fuzz/fuzzer_pass_mutate_pointers.h"
#include "source/fuzz/fuzzer_pass_obfuscate_constants.h"
#include "source/fuzz/fuzzer_pass_outline_functions.h"
#include "source/fuzz/fuzzer_pass_permute_blocks.h"
#include "source/fuzz/fuzzer_pass_permute_function_parameters.h"
#include "source/fuzz/fuzzer_pass_permute_instructions.h"
#include "source/fuzz/fuzzer_pass_propagate_instructions_up.h"
#include "source/fuzz/fuzzer_pass_push_ids_through_variables.h"
#include "source/fuzz/fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.h"
#include "source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.h"
#include "source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.h"
#include "source/fuzz/fuzzer_pass_replace_irrelevant_ids.h"
#include "source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.h"
#include "source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.h"
#include "source/fuzz/fuzzer_pass_replace_opphi_ids_from_dead_predecessors.h"
#include "source/fuzz/fuzzer_pass_replace_opselects_with_conditional_branches.h"
#include "source/fuzz/fuzzer_pass_replace_parameter_with_global.h"
#include "source/fuzz/fuzzer_pass_replace_params_with_struct.h"
#include "source/fuzz/fuzzer_pass_split_blocks.h"
#include "source/fuzz/fuzzer_pass_swap_conditional_branch_operands.h"
namespace spvtools {
namespace fuzz {
// This class has a distinct member for each repeated fuzzer pass (i.e., a
// fuzzer pass that it makes sense to run multiple times). If a member is null
// then we do not have an instance of that fuzzer pass, i.e. it is disabled.
// The class also provides access to the set of passes that are enabled.
class RepeatedPassInstances {
// This macro should be invoked below for every repeated fuzzer pass. If a
// repeated fuzzer pass is called FuzzerPassFoo then the macro invocation:
//
// REPEATED_PASS_INSTANCE(Foo);
//
// should be used. This adds a private member of type FuzzerPassFoo*, and
// provides the following public methods:
//
// // Requires that SetPass has not been called previously with FuzzerPassFoo.
// // Adds |pass| to the set of known pass instances.
// void SetPass(std::unique_ptr<FuzzerPassFoo> pass);
//
// // Returns a pointer to a pass instance of type FuzzerPassFoo that was
// // previously registered via SetPass(), or nullptr if no such instance was
// // registered
// FuzzerPassFoo* GetFoo();
#define REPEATED_PASS_INSTANCE(NAME) \
public: \
FuzzerPass##NAME* Get##NAME() const { return NAME##_; } \
void SetPass(std::unique_ptr<FuzzerPass##NAME> pass) { \
assert(NAME##_ == nullptr && "Attempt to set pass multiple times."); \
NAME##_ = pass.get(); \
passes_.push_back(std::move(pass)); \
} \
\
private: \
FuzzerPass##NAME* NAME##_ = nullptr
REPEATED_PASS_INSTANCE(AddAccessChains);
REPEATED_PASS_INSTANCE(AddBitInstructionSynonyms);
REPEATED_PASS_INSTANCE(AddCompositeInserts);
REPEATED_PASS_INSTANCE(AddCompositeTypes);
REPEATED_PASS_INSTANCE(AddCopyMemory);
REPEATED_PASS_INSTANCE(AddDeadBlocks);
REPEATED_PASS_INSTANCE(AddDeadBreaks);
REPEATED_PASS_INSTANCE(AddDeadContinues);
REPEATED_PASS_INSTANCE(AddEquationInstructions);
REPEATED_PASS_INSTANCE(AddFunctionCalls);
REPEATED_PASS_INSTANCE(AddGlobalVariables);
REPEATED_PASS_INSTANCE(AddImageSampleUnusedComponents);
REPEATED_PASS_INSTANCE(AddLoads);
REPEATED_PASS_INSTANCE(AddLocalVariables);
REPEATED_PASS_INSTANCE(AddLoopPreheaders);
REPEATED_PASS_INSTANCE(AddOpPhiSynonyms);
REPEATED_PASS_INSTANCE(AddParameters);
REPEATED_PASS_INSTANCE(AddRelaxedDecorations);
REPEATED_PASS_INSTANCE(AddStores);
REPEATED_PASS_INSTANCE(AddSynonyms);
REPEATED_PASS_INSTANCE(AddVectorShuffleInstructions);
REPEATED_PASS_INSTANCE(ApplyIdSynonyms);
REPEATED_PASS_INSTANCE(ConstructComposites);
REPEATED_PASS_INSTANCE(CopyObjects);
REPEATED_PASS_INSTANCE(DonateModules);
REPEATED_PASS_INSTANCE(DuplicateRegionsWithSelections);
REPEATED_PASS_INSTANCE(FlattenConditionalBranches);
REPEATED_PASS_INSTANCE(InlineFunctions);
REPEATED_PASS_INSTANCE(InvertComparisonOperators);
REPEATED_PASS_INSTANCE(MakeVectorOperationsDynamic);
REPEATED_PASS_INSTANCE(MergeBlocks);
REPEATED_PASS_INSTANCE(MutatePointers);
REPEATED_PASS_INSTANCE(ObfuscateConstants);
REPEATED_PASS_INSTANCE(OutlineFunctions);
REPEATED_PASS_INSTANCE(PermuteBlocks);
REPEATED_PASS_INSTANCE(PermuteFunctionParameters);
REPEATED_PASS_INSTANCE(PermuteInstructions);
REPEATED_PASS_INSTANCE(PropagateInstructionsUp);
REPEATED_PASS_INSTANCE(PushIdsThroughVariables);
REPEATED_PASS_INSTANCE(ReplaceAddsSubsMulsWithCarryingExtended);
REPEATED_PASS_INSTANCE(ReplaceCopyMemoriesWithLoadsStores);
REPEATED_PASS_INSTANCE(ReplaceCopyObjectsWithStoresLoads);
REPEATED_PASS_INSTANCE(ReplaceLoadsStoresWithCopyMemories);
REPEATED_PASS_INSTANCE(ReplaceIrrelevantIds);
REPEATED_PASS_INSTANCE(ReplaceOpPhiIdsFromDeadPredecessors);
REPEATED_PASS_INSTANCE(ReplaceOpSelectsWithConditionalBranches);
REPEATED_PASS_INSTANCE(ReplaceParameterWithGlobal);
REPEATED_PASS_INSTANCE(ReplaceLinearAlgebraInstructions);
REPEATED_PASS_INSTANCE(ReplaceParamsWithStruct);
REPEATED_PASS_INSTANCE(SplitBlocks);
REPEATED_PASS_INSTANCE(SwapBranchConditionalOperands);
#undef REPEATED_PASS_INSTANCE
public:
// Yields the sequence of fuzzer pass instances that have been registered.
const std::vector<std::unique_ptr<FuzzerPass>>& GetPasses() const {
return passes_;
}
private:
// The distinct fuzzer pass instances that have been registered via SetPass().
std::vector<std::unique_ptr<FuzzerPass>> passes_;
};
} // namespace fuzz
} // namespace spvtools
#endif // SOURCE_FUZZ_REPEATED_PASS_INSTANCES_H_

Просмотреть файл

@ -0,0 +1,27 @@
// Copyright (c) 2020 Google LLC
//
// 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 "source/fuzz/pass_management/repeated_pass_manager.h"
namespace spvtools {
namespace fuzz {
RepeatedPassManager::RepeatedPassManager(FuzzerContext* fuzzer_context,
RepeatedPassInstances* pass_instances)
: fuzzer_context_(fuzzer_context), pass_instances_(pass_instances) {}
RepeatedPassManager::~RepeatedPassManager() = default;
} // namespace fuzz
} // namespace spvtools

Просмотреть файл

@ -0,0 +1,55 @@
// Copyright (c) 2020 Google LLC
//
// 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.
#ifndef SOURCE_FUZZ_REPEATED_PASS_MANAGER_H_
#define SOURCE_FUZZ_REPEATED_PASS_MANAGER_H_
#include "source/fuzz/fuzzer_context.h"
#include "source/fuzz/fuzzer_pass.h"
#include "source/fuzz/pass_management/repeated_pass_instances.h"
namespace spvtools {
namespace fuzz {
// 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
// been run and bias the selection of future passes according to this history.
class RepeatedPassManager {
public:
RepeatedPassManager(FuzzerContext* fuzzer_context,
RepeatedPassInstances* pass_instances);
virtual ~RepeatedPassManager();
// Returns the fuzzer pass instance that should be run next.
virtual FuzzerPass* ChoosePass() = 0;
protected:
FuzzerContext* GetFuzzerContext() { return fuzzer_context_; }
RepeatedPassInstances* GetPassInstances() { return pass_instances_; }
private:
// Provided in order to allow the pass manager to make random decisions.
FuzzerContext* fuzzer_context_;
// The repeated fuzzer passes that are enabled.
RepeatedPassInstances* pass_instances_;
};
} // namespace fuzz
} // namespace spvtools
#endif // SOURCE_FUZZ_REPEATED_PASS_MANAGER_H_

Просмотреть файл

@ -0,0 +1,49 @@
// Copyright (c) 2020 Google LLC
//
// 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 "source/fuzz/pass_management/repeated_pass_manager_looped_with_recommendations.h"
namespace spvtools {
namespace fuzz {
RepeatedPassManagerLoopedWithRecommendations::
RepeatedPassManagerLoopedWithRecommendations(
FuzzerContext* fuzzer_context, RepeatedPassInstances* pass_instances,
RepeatedPassRecommender* pass_recommender)
: RepeatedPassManager(fuzzer_context, pass_instances), next_pass_index_(0) {
auto& passes = GetPassInstances()->GetPasses();
do {
FuzzerPass* current_pass =
passes[GetFuzzerContext()->RandomIndex(passes)].get();
pass_loop_.push_back(current_pass);
for (auto future_pass :
pass_recommender->GetFuturePassRecommendations(*current_pass)) {
pass_loop_.push_back(future_pass);
}
} while (fuzzer_context->ChoosePercentage(
fuzzer_context->GetChanceOfAddingAnotherPassToPassLoop()));
}
RepeatedPassManagerLoopedWithRecommendations::
~RepeatedPassManagerLoopedWithRecommendations() = default;
FuzzerPass* RepeatedPassManagerLoopedWithRecommendations::ChoosePass() {
auto result = pass_loop_[next_pass_index_];
next_pass_index_ =
(next_pass_index_ + 1) % static_cast<uint32_t>(pass_loop_.size());
return result;
}
} // namespace fuzz
} // namespace spvtools

Просмотреть файл

@ -0,0 +1,58 @@
// Copyright (c) 2020 Google LLC
//
// 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.
#ifndef SOURCE_FUZZ_REPEATED_PASS_MANAGER_LOOPED_WITH_RECOMMENDATIONS_H_
#define SOURCE_FUZZ_REPEATED_PASS_MANAGER_LOOPED_WITH_RECOMMENDATIONS_H_
#include <vector>
#include "source/fuzz/pass_management/repeated_pass_manager.h"
#include "source/fuzz/pass_management/repeated_pass_recommender.h"
namespace spvtools {
namespace fuzz {
// On construction, this pass manager creates a sequence of fuzzer passes which
// is not changed thereafter. Passes from this sequence are served up in round
// robin fashion each time ChoosePass is invoked - i.e., the sequence is a "pass
// loop".
//
// The pass loop is constructed by repeatedly:
// - Randomly adding an enabled pass
// - Adding all recommended follow-on passes for this pass
// and probabilistically terminating this process.
class RepeatedPassManagerLoopedWithRecommendations
: public RepeatedPassManager {
public:
RepeatedPassManagerLoopedWithRecommendations(
FuzzerContext* fuzzer_context, RepeatedPassInstances* pass_instances,
RepeatedPassRecommender* pass_recommender);
~RepeatedPassManagerLoopedWithRecommendations() override;
FuzzerPass* ChoosePass() override;
private:
// The loop of fuzzer passes to be applied, populated on construction.
std::vector<FuzzerPass*> pass_loop_;
// An index into |pass_loop_| specifying which pass should be served up next
// time ChoosePass is invoked.
uint32_t next_pass_index_;
};
} // namespace fuzz
} // namespace spvtools
#endif // SOURCE_FUZZ_REPEATED_PASS_MANAGER_LOOPED_WITH_RECOMMENDATIONS_H_

Просмотреть файл

@ -0,0 +1,47 @@
// Copyright (c) 2020 Google LLC
//
// 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 "source/fuzz/pass_management/repeated_pass_manager_random_with_recommendations.h"
namespace spvtools {
namespace fuzz {
RepeatedPassManagerRandomWithRecommendations::
RepeatedPassManagerRandomWithRecommendations(
FuzzerContext* fuzzer_context, RepeatedPassInstances* pass_instances,
RepeatedPassRecommender* pass_recommender)
: RepeatedPassManager(fuzzer_context, pass_instances),
pass_recommender_(pass_recommender) {}
RepeatedPassManagerRandomWithRecommendations::
~RepeatedPassManagerRandomWithRecommendations() = default;
FuzzerPass* RepeatedPassManagerRandomWithRecommendations::ChoosePass() {
FuzzerPass* result;
if (recommended_passes_.empty() || GetFuzzerContext()->ChooseEven()) {
auto& passes = GetPassInstances()->GetPasses();
result = passes[GetFuzzerContext()->RandomIndex(passes)].get();
} else {
result = recommended_passes_.front();
recommended_passes_.pop_front();
}
for (auto future_pass :
pass_recommender_->GetFuturePassRecommendations(*result)) {
recommended_passes_.push_back(future_pass);
}
return result;
}
} // namespace fuzz
} // namespace spvtools

Просмотреть файл

@ -0,0 +1,59 @@
// Copyright (c) 2020 Google LLC
//
// 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.
#ifndef SOURCE_FUZZ_REPEATED_PASS_MANAGER_RANDOM_WITH_RECOMMENDATIONS_H_
#define SOURCE_FUZZ_REPEATED_PASS_MANAGER_RANDOM_WITH_RECOMMENDATIONS_H_
#include <deque>
#include "source/fuzz/pass_management/repeated_pass_manager.h"
#include "source/fuzz/pass_management/repeated_pass_recommender.h"
namespace spvtools {
namespace fuzz {
// This repeated pass manager uses a pass recommender to recommend future passes
// each time a fuzzer pass is run. It keeps a queue of recommended passes.
//
// Each time a fuzzer pass is requested, the manager either selects an enabled
// fuzzer pass at random, or selects the pass at the front of the recommendation
// queue, removing it from the queue. The decision of which of these pass
// selection methods to use is made randomly each time ChoosePass is called.
//
// Either way, recommended follow-on passes for the chosen pass are added to
// the recommendation queue.
class RepeatedPassManagerRandomWithRecommendations
: public RepeatedPassManager {
public:
RepeatedPassManagerRandomWithRecommendations(
FuzzerContext* fuzzer_context, RepeatedPassInstances* pass_instances,
RepeatedPassRecommender* pass_recommender);
~RepeatedPassManagerRandomWithRecommendations() override;
FuzzerPass* ChoosePass() override;
private:
// The queue of passes that have been recommended based on previously-chosen
// passes.
std::deque<FuzzerPass*> recommended_passes_;
// Used to recommend future passes.
RepeatedPassRecommender* pass_recommender_;
};
} // namespace fuzz
} // namespace spvtools
#endif // SOURCE_FUZZ_REPEATED_PASS_MANAGER_RANDOM_WITH_RECOMMENDATIONS_H_

Просмотреть файл

@ -0,0 +1,32 @@
// Copyright (c) 2020 Google LLC
//
// 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 "source/fuzz/pass_management/repeated_pass_manager_simple.h"
namespace spvtools {
namespace fuzz {
RepeatedPassManagerSimple::RepeatedPassManagerSimple(
FuzzerContext* fuzzer_context, RepeatedPassInstances* pass_instances)
: RepeatedPassManager(fuzzer_context, pass_instances) {}
RepeatedPassManagerSimple::~RepeatedPassManagerSimple() = default;
FuzzerPass* RepeatedPassManagerSimple::ChoosePass() {
auto& passes = GetPassInstances()->GetPasses();
return passes[GetFuzzerContext()->RandomIndex(passes)].get();
}
} // namespace fuzz
} // namespace spvtools

Просмотреть файл

@ -0,0 +1,38 @@
// Copyright (c) 2020 Google LLC
//
// 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.
#ifndef SOURCE_FUZZ_REPEATED_PASS_MANAGER_SIMPLE_H_
#define SOURCE_FUZZ_REPEATED_PASS_MANAGER_SIMPLE_H_
#include "source/fuzz/pass_management/repeated_pass_manager.h"
namespace spvtools {
namespace fuzz {
// Selects the next pass to run uniformly at random from the enabled repeated
// passes. Recommendations are not used.
class RepeatedPassManagerSimple : public RepeatedPassManager {
public:
RepeatedPassManagerSimple(FuzzerContext* fuzzer_context,
RepeatedPassInstances* pass_instances);
~RepeatedPassManagerSimple() override;
FuzzerPass* ChoosePass() override;
};
} // namespace fuzz
} // namespace spvtools
#endif // SOURCE_FUZZ_REPEATED_PASS_MANAGER_SIMPLE_H_

Просмотреть файл

@ -0,0 +1,23 @@
// Copyright (c) 2020 Google LLC
//
// 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 "source/fuzz/pass_management/repeated_pass_recommender.h"
namespace spvtools {
namespace fuzz {
RepeatedPassRecommender::~RepeatedPassRecommender() = default;
} // namespace fuzz
} // namespace spvtools

Просмотреть файл

@ -0,0 +1,42 @@
// Copyright (c) 2020 Google LLC
//
// 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.
#ifndef SOURCE_FUZZ_REPEATED_PASS_RECOMMENDER_H_
#define SOURCE_FUZZ_REPEATED_PASS_RECOMMENDER_H_
#include <vector>
#include "source/fuzz/fuzzer_pass.h"
namespace spvtools {
namespace fuzz {
// Interface for influencing interactions between repeated fuzzer passes, by
// allowing hints as to which passes are recommended to be run after one
// another.
class RepeatedPassRecommender {
public:
virtual ~RepeatedPassRecommender();
// Given a reference to a repeated pass, |pass|, returns a sequence of
// repeated pass instances that might be worth running soon after having
// run |pass|.
virtual std::vector<FuzzerPass*> GetFuturePassRecommendations(
const FuzzerPass& pass) = 0;
};
} // namespace fuzz
} // namespace spvtools
#endif // SOURCE_FUZZ_REPEATED_PASS_RECOMMENDER_H_

Просмотреть файл

@ -0,0 +1,330 @@
// Copyright (c) 2020 Google LLC
//
// 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 "source/fuzz/pass_management/repeated_pass_recommender_standard.h"
#include <numeric>
namespace spvtools {
namespace fuzz {
RepeatedPassRecommenderStandard::RepeatedPassRecommenderStandard(
RepeatedPassInstances* pass_instances, FuzzerContext* fuzzer_context)
: pass_instances_(pass_instances), fuzzer_context_(fuzzer_context) {}
RepeatedPassRecommenderStandard::~RepeatedPassRecommenderStandard() = default;
std::vector<FuzzerPass*>
RepeatedPassRecommenderStandard::GetFuturePassRecommendations(
const FuzzerPass& pass) {
if (&pass == pass_instances_->GetAddAccessChains()) {
// - Adding access chains means there is more scope for loading and storing
// - It could be worth making more access chains from the recently-added
// access chains
return RandomOrderAndNonNull({pass_instances_->GetAddLoads(),
pass_instances_->GetAddStores(),
pass_instances_->GetAddAccessChains()});
}
if (&pass == pass_instances_->GetAddBitInstructionSynonyms()) {
// - Adding bit instruction synonyms creates opportunities to apply synonyms
return RandomOrderAndNonNull({pass_instances_->GetApplyIdSynonyms()});
}
if (&pass == pass_instances_->GetAddCompositeInserts()) {
// - Having added inserts we will have more vectors, so there is scope for
// vector shuffling
// - Adding inserts creates synonyms, which we should try to use
// - Vector inserts can be made dynamic
return RandomOrderAndNonNull(
{pass_instances_->GetAddVectorShuffleInstructions(),
pass_instances_->GetApplyIdSynonyms(),
pass_instances_->GetMakeVectorOperationsDynamic()});
}
if (&pass == pass_instances_->GetAddCompositeTypes()) {
// - More composite types gives more scope for constructing composites
return RandomOrderAndNonNull({pass_instances_->GetConstructComposites()});
}
if (&pass == pass_instances_->GetAddCopyMemory()) {
// - Recently-added copy memories could be replace with load-store pairs
return RandomOrderAndNonNull(
{pass_instances_->GetReplaceCopyMemoriesWithLoadsStores()});
}
if (&pass == pass_instances_->GetAddDeadBlocks()) {
// - Dead blocks are great for adding function calls
// - Dead blocks are also great for adding loads and stores
// - The guard associated with a dead block can be obfuscated
return RandomOrderAndNonNull({pass_instances_->GetAddFunctionCalls(),
pass_instances_->GetAddLoads(),
pass_instances_->GetAddStores(),
pass_instances_->GetObfuscateConstants()});
}
if (&pass == pass_instances_->GetAddDeadBreaks()) {
// - The guard of the dead break is a good candidate for obfuscation
return RandomOrderAndNonNull({pass_instances_->GetObfuscateConstants()});
}
if (&pass == pass_instances_->GetAddDeadContinues()) {
// - The guard of the dead continue is a good candidate for obfuscation
return RandomOrderAndNonNull({pass_instances_->GetObfuscateConstants()});
}
if (&pass == pass_instances_->GetAddEquationInstructions()) {
// - Equation instructions can create synonyms, which we can apply
// - Equation instructions collaborate with one another to make synonyms, so
// having added some it is worth adding more
return RandomOrderAndNonNull(
{pass_instances_->GetApplyIdSynonyms(),
pass_instances_->GetAddEquationInstructions()});
}
if (&pass == pass_instances_->GetAddFunctionCalls()) {
// - Called functions can be inlined
// - Irrelevant ids are created, so they can be replaced
return RandomOrderAndNonNull({pass_instances_->GetInlineFunctions(),
pass_instances_->GetReplaceIrrelevantIds()});
}
if (&pass == pass_instances_->GetAddGlobalVariables()) {
// - New globals provide new possibilities for making access chains
// - We can load from and store to new globals
return RandomOrderAndNonNull({pass_instances_->GetAddAccessChains(),
pass_instances_->GetAddLoads(),
pass_instances_->GetAddStores()});
}
if (&pass == pass_instances_->GetAddImageSampleUnusedComponents()) {
// - This introduces an unused component whose id is irrelevant and can be
// replaced
return RandomOrderAndNonNull({pass_instances_->GetReplaceIrrelevantIds()});
}
if (&pass == pass_instances_->GetAddLoads()) {
// - Loads might end up with corresponding stores, so that pairs can be
// replaced with memory copies
return RandomOrderAndNonNull(
{pass_instances_->GetReplaceLoadsStoresWithCopyMemories()});
}
if (&pass == pass_instances_->GetAddLocalVariables()) {
// - New locals provide new possibilities for making access chains
// - We can load from and store to new locals
return RandomOrderAndNonNull({pass_instances_->GetAddAccessChains(),
pass_instances_->GetAddLoads(),
pass_instances_->GetAddStores()});
}
if (&pass == pass_instances_->GetAddLoopPreheaders()) {
// - The loop preheader provides more scope for duplicating regions and
// outlining functions.
return RandomOrderAndNonNull(
{pass_instances_->GetDuplicateRegionsWithSelections(),
pass_instances_->GetOutlineFunctions()});
}
if (&pass == pass_instances_->GetAddOpPhiSynonyms()) {
// - New synonyms can be applied
// - If OpPhi synonyms are introduced for blocks with dead predecessors, the
// values consumed from dead predecessors can be replaced
return RandomOrderAndNonNull(
{pass_instances_->GetApplyIdSynonyms(),
pass_instances_->GetReplaceOpPhiIdsFromDeadPredecessors()});
}
if (&pass == pass_instances_->GetAddParameters()) {
// - We might be able to create interesting synonyms of new parameters.
// - This introduces irrelevant ids, which can be replaced
return RandomOrderAndNonNull({pass_instances_->GetAddSynonyms(),
pass_instances_->GetReplaceIrrelevantIds()});
}
if (&pass == pass_instances_->GetAddRelaxedDecorations()) {
// - No obvious follow-on passes
return {};
}
if (&pass == pass_instances_->GetAddStores()) {
// - Stores might end up with corresponding loads, so that pairs can be
// replaced with memory copies
return RandomOrderAndNonNull(
{pass_instances_->GetReplaceLoadsStoresWithCopyMemories()});
}
if (&pass == pass_instances_->GetAddSynonyms()) {
// - New synonyms can be applied
// - Synonym instructions use constants, which can be obfuscated
// - Synonym instructions use irrelevant ids, which can be replaced
// - Synonym instructions introduce addition/subtraction, which can be
// replaced with carrying/extended versions
return RandomOrderAndNonNull(
{pass_instances_->GetApplyIdSynonyms(),
pass_instances_->GetObfuscateConstants(),
pass_instances_->GetReplaceAddsSubsMulsWithCarryingExtended(),
pass_instances_->GetReplaceIrrelevantIds()});
}
if (&pass == pass_instances_->GetAddVectorShuffleInstructions()) {
// - Vector shuffles create synonyms that can be applied
// - TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3806) Extract
// from composites.
return RandomOrderAndNonNull({pass_instances_->GetApplyIdSynonyms()});
}
if (&pass == pass_instances_->GetApplyIdSynonyms()) {
// - No obvious follow-on passes
return {};
}
if (&pass == pass_instances_->GetConstructComposites()) {
// - TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3806): Extract
// from composites.
return RandomOrderAndNonNull({});
}
if (&pass == pass_instances_->GetCopyObjects()) {
// - Object copies create synonyms that can be applied
// - OpCopyObject can be replaced with a store/load pair
return RandomOrderAndNonNull(
{pass_instances_->GetApplyIdSynonyms(),
pass_instances_->GetReplaceCopyObjectsWithStoresLoads()});
}
if (&pass == pass_instances_->GetDonateModules()) {
// - New functions in the module can be called
// - Donated dead functions produce irrelevant ids, which can be replaced
return RandomOrderAndNonNull({pass_instances_->GetAddFunctionCalls(),
pass_instances_->GetReplaceIrrelevantIds()});
}
if (&pass == pass_instances_->GetDuplicateRegionsWithSelections()) {
// - Parts of duplicated regions can be outlined
return RandomOrderAndNonNull({pass_instances_->GetOutlineFunctions()});
}
if (&pass == pass_instances_->GetFlattenConditionalBranches()) {
// - Parts of flattened selections can be outlined
// - The flattening transformation introduces constants and irrelevant ids
// for enclosing hard-to-flatten operations; these can be obfuscated or
// replaced
return RandomOrderAndNonNull({pass_instances_->GetObfuscateConstants(),
pass_instances_->GetOutlineFunctions(),
pass_instances_->GetReplaceIrrelevantIds()});
}
if (&pass == pass_instances_->GetInlineFunctions()) {
// - Parts of inlined functions can be outlined again
return RandomOrderAndNonNull({pass_instances_->GetOutlineFunctions()});
}
if (&pass == pass_instances_->GetInvertComparisonOperators()) {
// - No obvious follow-on passes
return {};
}
if (&pass == pass_instances_->GetMakeVectorOperationsDynamic()) {
// - No obvious follow-on passes
return {};
}
if (&pass == pass_instances_->GetMergeBlocks()) {
// - Having merged some blocks it may be interesting to split them in a
// different way
return RandomOrderAndNonNull({pass_instances_->GetSplitBlocks()});
}
if (&pass == pass_instances_->GetMutatePointers()) {
// - This creates irrelevant ids, which can be replaced
return RandomOrderAndNonNull({pass_instances_->GetReplaceIrrelevantIds()});
}
if (&pass == pass_instances_->GetObfuscateConstants()) {
// - No obvious follow-on passes
return {};
}
if (&pass == pass_instances_->GetOutlineFunctions()) {
// - This creates more functions, which can be called
// - Inlining the function for the region that was outlined might also be
// fruitful; it will be inlined in a different form
return RandomOrderAndNonNull({pass_instances_->GetAddFunctionCalls(),
pass_instances_->GetInlineFunctions()});
}
if (&pass == pass_instances_->GetPermuteBlocks()) {
// No obvious follow-on passes
return {};
}
if (&pass == pass_instances_->GetPermuteFunctionParameters()) {
// No obvious follow-on passes
return {};
}
if (&pass == pass_instances_->GetPermuteInstructions()) {
// No obvious follow-on passes
return {};
}
if (&pass == pass_instances_->GetPropagateInstructionsUp()) {
// No obvious follow-on passes
return {};
}
if (&pass == pass_instances_->GetPushIdsThroughVariables()) {
// - This pass creates synonyms, so it is worth applying them
return RandomOrderAndNonNull({pass_instances_->GetApplyIdSynonyms()});
}
if (&pass == pass_instances_->GetReplaceAddsSubsMulsWithCarryingExtended()) {
// No obvious follow-on passes
return {};
}
if (&pass == pass_instances_->GetReplaceCopyMemoriesWithLoadsStores()) {
// No obvious follow-on passes
return {};
}
if (&pass == pass_instances_->GetReplaceCopyObjectsWithStoresLoads()) {
// - We may end up with load/store pairs that could be used to create memory
// copies
return RandomOrderAndNonNull(
{pass_instances_->GetReplaceLoadsStoresWithCopyMemories()});
}
if (&pass == pass_instances_->GetReplaceIrrelevantIds()) {
// No obvious follow-on passes
return {};
}
if (&pass == pass_instances_->GetReplaceLinearAlgebraInstructions()) {
// No obvious follow-on passes
return {};
}
if (&pass == pass_instances_->GetReplaceLoadsStoresWithCopyMemories()) {
// No obvious follow-on passes
return {};
}
if (&pass == pass_instances_->GetReplaceOpPhiIdsFromDeadPredecessors()) {
// No obvious follow-on passes
return {};
}
if (&pass == pass_instances_->GetReplaceOpSelectsWithConditionalBranches()) {
// No obvious follow-on passes
return {};
}
if (&pass == pass_instances_->GetReplaceParameterWithGlobal()) {
// No obvious follow-on passes
return {};
}
if (&pass == pass_instances_->GetReplaceParamsWithStruct()) {
// No obvious follow-on passes
return {};
}
if (&pass == pass_instances_->GetSplitBlocks()) {
// - More blocks means more chances for adding dead breaks/continues, and
// for adding dead blocks
return RandomOrderAndNonNull({pass_instances_->GetAddDeadBreaks(),
pass_instances_->GetAddDeadContinues(),
pass_instances_->GetAddDeadBlocks()});
}
if (&pass == pass_instances_->GetSwapBranchConditionalOperands()) {
// No obvious follow-on passes
return {};
}
assert(false && "Unreachable: every fuzzer pass should be dealt with.");
return {};
}
std::vector<FuzzerPass*> RepeatedPassRecommenderStandard::RandomOrderAndNonNull(
const std::vector<FuzzerPass*>& passes) {
std::vector<uint32_t> indices(passes.size());
std::iota(indices.begin(), indices.end(), 0);
std::vector<FuzzerPass*> result;
while (!indices.empty()) {
FuzzerPass* maybe_pass =
passes[fuzzer_context_->RemoveAtRandomIndex(&indices)];
if (maybe_pass != nullptr &&
fuzzer_context_->ChoosePercentage(
fuzzer_context_
->GetChanceOfAcceptingRepeatedPassRecommendation())) {
result.push_back(maybe_pass);
}
}
return result;
}
} // namespace fuzz
} // namespace spvtools

Просмотреть файл

@ -0,0 +1,50 @@
// Copyright (c) 2020 Google LLC
//
// 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.
#ifndef SOURCE_FUZZ_REPEATED_PASS_RECOMMENDER_STANDARD_H_
#define SOURCE_FUZZ_REPEATED_PASS_RECOMMENDER_STANDARD_H_
#include "source/fuzz/fuzzer_context.h"
#include "source/fuzz/pass_management/repeated_pass_instances.h"
#include "source/fuzz/pass_management/repeated_pass_recommender.h"
namespace spvtools {
namespace fuzz {
// A manually-crafter recommender of repeated passes, designed based on
// knowledge of how the various fuzzer passes work and speculation as to how
// they might interact in interesting ways.
class RepeatedPassRecommenderStandard : public RepeatedPassRecommender {
public:
RepeatedPassRecommenderStandard(RepeatedPassInstances* pass_instances,
FuzzerContext* fuzzer_context);
~RepeatedPassRecommenderStandard();
std::vector<FuzzerPass*> GetFuturePassRecommendations(
const FuzzerPass& pass) override;
private:
std::vector<FuzzerPass*> RandomOrderAndNonNull(
const std::vector<FuzzerPass*>& passes);
RepeatedPassInstances* pass_instances_;
FuzzerContext* fuzzer_context_;
};
} // namespace fuzz
} // namespace spvtools
#endif // SOURCE_FUZZ_REPEATED_PASS_RECOMMENDER_STANDARD_H_

Просмотреть файл

@ -166,7 +166,7 @@ void TransformationCompositeInsert::Apply(
continue;
}
current_index.push_back(i);
// TODO: (https://github.com/KhronosGroup/SPIRV-Tools/issues/3659)
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3659):
// Google C++ guide restricts the use of r-value references.
// https://google.github.io/styleguide/cppguide.html#Rvalue_references
// Consider changing the signature of MakeDataDescriptor()

Просмотреть файл

@ -110,7 +110,7 @@ bool TransformationDuplicateRegionWithSelection::IsApplicable(
return false;
}
// TODO (https://github.com/KhronosGroup/SPIRV-Tools/issues/3785):
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3785):
// The following code has been copied from TransformationOutlineFunction.
// Consider refactoring to avoid duplication.
auto region_set = GetRegionBlocks(ir_context, entry_block, exit_block);
@ -555,7 +555,7 @@ void TransformationDuplicateRegionWithSelection::Apply(
ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
}
// TODO (https://github.com/KhronosGroup/SPIRV-Tools/issues/3785):
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3785):
// The following method has been copied from
// TransformationOutlineFunction. Consider refactoring to avoid
// duplication.

Просмотреть файл

@ -25,7 +25,8 @@ spv_fuzzer_options_t::spv_fuzzer_options_t()
replay_range(0),
replay_validation_enabled(false),
shrinker_step_limit(kDefaultStepLimit),
fuzzer_pass_validation_enabled(false) {}
fuzzer_pass_validation_enabled(false),
all_passes_enabled(false) {}
SPIRV_TOOLS_EXPORT spv_fuzzer_options spvFuzzerOptionsCreate() {
return new spv_fuzzer_options_t();
@ -60,3 +61,8 @@ SPIRV_TOOLS_EXPORT void spvFuzzerOptionsEnableFuzzerPassValidation(
spv_fuzzer_options options) {
options->fuzzer_pass_validation_enabled = true;
}
SPIRV_TOOLS_EXPORT void spvFuzzerOptionsEnableAllPasses(
spv_fuzzer_options options) {
options->all_passes_enabled = true;
}

Просмотреть файл

@ -40,6 +40,9 @@ struct spv_fuzzer_options_t {
// See spvFuzzerOptionsValidateAfterEveryPass.
bool fuzzer_pass_validation_enabled;
// See spvFuzzerOptionsEnableAllPasses.
bool all_passes_enabled;
};
#endif // SOURCE_SPIRV_FUZZER_OPTIONS_H_

Просмотреть файл

@ -1639,12 +1639,26 @@ void RunFuzzerAndReplayer(const std::string& shader,
});
}
std::vector<Fuzzer::RepeatedPassStrategy> strategies{
Fuzzer::RepeatedPassStrategy::kSimple,
Fuzzer::RepeatedPassStrategy::kLoopedWithRecommendations,
Fuzzer::RepeatedPassStrategy::kRandomWithRecommendations};
uint32_t strategy_index = 0;
for (uint32_t seed = initial_seed; seed < initial_seed + num_runs; seed++) {
std::vector<uint32_t> fuzzer_binary_out;
protobufs::TransformationSequence fuzzer_transformation_sequence_out;
spvtools::ValidatorOptions validator_options;
Fuzzer fuzzer(env, seed, true, validator_options);
// Every 4th time we run the fuzzer, enable all fuzzer passes.
bool enable_all_passes = (seed % 4) == 0;
Fuzzer fuzzer(env, seed, enable_all_passes, strategies[strategy_index],
true, validator_options);
// 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());
fuzzer.SetMessageConsumer(kConsoleMessageConsumer);
auto fuzzer_result_status =
fuzzer.Run(binary_in, initial_facts, donor_suppliers,

Просмотреть файл

@ -1040,7 +1040,23 @@ void RunFuzzerAndShrinker(const std::string& shader,
std::vector<uint32_t> fuzzer_binary_out;
protobufs::TransformationSequence fuzzer_transformation_sequence_out;
spvtools::ValidatorOptions validator_options;
Fuzzer fuzzer(env, seed, true, validator_options);
// 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;
if ((seed % 3) == 0) {
repeated_pass_strategy = Fuzzer::RepeatedPassStrategy::kSimple;
} else if ((seed % 3) == 1) {
repeated_pass_strategy =
Fuzzer::RepeatedPassStrategy::kLoopedWithRecommendations;
} else {
repeated_pass_strategy =
Fuzzer::RepeatedPassStrategy::kRandomWithRecommendations;
}
Fuzzer fuzzer(env, seed, enable_all_passes, repeated_pass_strategy, true,
validator_options);
fuzzer.SetMessageConsumer(kSilentConsumer);
auto fuzzer_result_status =
fuzzer.Run(binary_in, initial_facts, donor_suppliers, &fuzzer_binary_out,

Просмотреть файл

@ -16,7 +16,6 @@
#include <cerrno>
#include <cstring>
#include <fstream>
#include <functional>
#include <random>
#include <sstream>
#include <string>
@ -107,6 +106,14 @@ Options (in lexicographical order):
provided if the tool is invoked in fuzzing mode; incompatible
with replay and shrink modes. The file should be empty if no
donors are to be used.
--enable-all-passes
By default, spirv-fuzz follows the philosophy of "swarm testing"
(Groce et al., 2012): only a subset of fuzzer passes are enabled
on any given fuzzer run, with the subset being chosen randomly.
This flag instead forces *all* fuzzer passes to be enabled. When
running spirv-fuzz many times this is likely to produce *less*
diverse fuzzed modules than when swarm testing is used. The
purpose of the flag is to allow that hypothesis to be tested.
--force-render-red
Transforms the input shader into a shader that writes red to the
output buffer, and then captures the original shader as the body
@ -118,6 +125,19 @@ Options (in lexicographical order):
Run the validator after applying each fuzzer pass during
fuzzing. Aborts fuzzing early if an invalid binary is created.
Useful for debugging spirv-fuzz.
--repeated-pass-strategy=
Available strategies are:
- looped (the default): a sequence of fuzzer passes is chosen at
the start of fuzzing, via randomly choosing enabled passes, and
augmenting these choices with fuzzer passes that it is
recommended to run subsequently. Fuzzing then involves
repeatedly applying this fixed sequence of passes.
- random: each time a fuzzer pass is requested, this strategy
either provides one at random from the set of enabled passes,
or provides a pass that has been recommended based on a pass
that was used previously.
- simple: each time a fuzzer pass is requested, one is provided
at random from the set of enabled passes.
--replay
File from which to read a sequence of transformations to replay
(instead of fuzzing)
@ -174,18 +194,23 @@ void FuzzDiagnostic(spv_message_level_t level, const char* /*source*/,
fprintf(stderr, "%s\n", message);
}
FuzzStatus ParseFlags(int argc, const char** argv, std::string* in_binary_file,
std::string* out_binary_file, std::string* donors_file,
std::string* replay_transformations_file,
std::vector<std::string>* interestingness_test,
std::string* shrink_transformations_file,
std::string* shrink_temp_file_prefix,
spvtools::FuzzerOptions* fuzzer_options,
spvtools::ValidatorOptions* validator_options) {
FuzzStatus ParseFlags(
int argc, const char** argv, std::string* in_binary_file,
std::string* out_binary_file, std::string* donors_file,
std::string* replay_transformations_file,
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::FuzzerOptions* fuzzer_options,
spvtools::ValidatorOptions* validator_options) {
uint32_t positional_arg_index = 0;
bool only_positional_arguments_remain = false;
bool force_render_red = false;
*repeated_pass_strategy =
spvtools::fuzz::Fuzzer::RepeatedPassStrategy::kLoopedWithRecommendations;
for (int argi = 1; argi < argc; ++argi) {
const char* cur_arg = argv[argi];
if ('-' == cur_arg[0] && !only_positional_arguments_remain) {
@ -206,6 +231,9 @@ FuzzStatus ParseFlags(int argc, const char** argv, std::string* in_binary_file,
} else if (0 == strncmp(cur_arg, "--donors=", sizeof("--donors=") - 1)) {
const auto split_flag = spvtools::utils::SplitFlagArgs(cur_arg);
*donors_file = std::string(split_flag.second);
} else if (0 == strncmp(cur_arg, "--enable-all-passes",
sizeof("--enable-all-passes") - 1)) {
fuzzer_options->enable_all_passes();
} else if (0 == strncmp(cur_arg, "--force-render-red",
sizeof("--force-render-red") - 1)) {
force_render_red = true;
@ -215,6 +243,26 @@ FuzzStatus ParseFlags(int argc, const char** argv, std::string* in_binary_file,
} else if (0 == strncmp(cur_arg, "--replay=", sizeof("--replay=") - 1)) {
const auto split_flag = spvtools::utils::SplitFlagArgs(cur_arg);
*replay_transformations_file = std::string(split_flag.second);
} else if (0 == strncmp(cur_arg, "--repeated-pass-strategy=",
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;
} else if (strategy == "random") {
*repeated_pass_strategy = spvtools::fuzz::Fuzzer::
RepeatedPassStrategy::kRandomWithRecommendations;
} else if (strategy == "simple") {
*repeated_pass_strategy =
spvtools::fuzz::Fuzzer::RepeatedPassStrategy::kSimple;
} else {
std::stringstream ss;
ss << "Unknown repeated pass strategy '" << strategy << "'"
<< std::endl;
ss << "Valid options are 'looped', 'random' and 'simple'.";
spvtools::Error(FuzzDiagnostic, nullptr, {}, ss.str().c_str());
return {FuzzActions::STOP, 1};
}
} else if (0 == strncmp(cur_arg, "--replay-range=",
sizeof("--replay-range=") - 1)) {
const auto split_flag = spvtools::utils::SplitFlagArgs(cur_arg);
@ -493,7 +541,9 @@ bool Fuzz(const spv_target_env& target_env,
spv_validator_options validator_options,
const std::vector<uint32_t>& binary_in,
const spvtools::fuzz::protobufs::FactSequence& initial_facts,
const std::string& donors, std::vector<uint32_t>* binary_out,
const std::string& donors,
spvtools::fuzz::Fuzzer::RepeatedPassStrategy repeated_pass_strategy,
std::vector<uint32_t>* binary_out,
spvtools::fuzz::protobufs::TransformationSequence*
transformations_applied) {
auto message_consumer = spvtools::utils::CLIMessageConsumer;
@ -526,6 +576,7 @@ bool Fuzz(const spv_target_env& target_env,
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);
fuzzer.SetMessageConsumer(message_consumer);
auto fuzz_result_status =
@ -568,6 +619,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::FuzzerOptions fuzzer_options;
spvtools::ValidatorOptions validator_options;
@ -576,7 +628,7 @@ int main(int argc, const char** argv) {
ParseFlags(argc, argv, &in_binary_file, &out_binary_file, &donors_file,
&replay_transformations_file, &interestingness_test,
&shrink_transformations_file, &shrink_temp_file_prefix,
&fuzzer_options, &validator_options);
&repeated_pass_strategy, &fuzzer_options, &validator_options);
if (status.action == FuzzActions::STOP) {
return status.code;
@ -622,7 +674,7 @@ int main(int argc, const char** argv) {
break;
case FuzzActions::FUZZ:
if (!Fuzz(target_env, fuzzer_options, validator_options, binary_in,
initial_facts, donors_file, &binary_out,
initial_facts, donors_file, repeated_pass_strategy, &binary_out,
&transformations_applied)) {
return 1;
}