Index: lib/fuzzer/FuzzerDriver.cpp =================================================================== --- lib/fuzzer/FuzzerDriver.cpp +++ lib/fuzzer/FuzzerDriver.cpp @@ -616,6 +616,7 @@ Options.PrintNewCovFuncs = Flags.print_funcs; Options.PrintFinalStats = Flags.print_final_stats; Options.PrintMutationStats = Flags.print_mutation_stats; + Options.UseWeightedMutations = Flags.use_weighted_mutations; Options.PrintCorpusStats = Flags.print_corpus_stats; Options.PrintCoverage = Flags.print_coverage; Options.PrintUnstableStats = Flags.print_unstable_stats; Index: lib/fuzzer/FuzzerFlags.def =================================================================== --- lib/fuzzer/FuzzerFlags.def +++ lib/fuzzer/FuzzerFlags.def @@ -161,3 +161,4 @@ FUZZER_DEPRECATED_FLAG(use_clang_coverage) FUZZER_FLAG_STRING(data_flow_trace, "Experimental: use the data flow trace") FUZZER_FLAG_INT(print_mutation_stats, 0, "Experimental") +FUZZER_FLAG_INT(use_weighted_mutations, 0, "Experimental: If 1, fuzzing will favor mutations that perform better during runtime.") Index: lib/fuzzer/FuzzerLoop.cpp =================================================================== --- lib/fuzzer/FuzzerLoop.cpp +++ lib/fuzzer/FuzzerLoop.cpp @@ -38,6 +38,7 @@ namespace fuzzer { static const size_t kMaxUnitSizeToPrint = 256; +const size_t kRunsBeforeWeightedMutations = 10000; thread_local bool Fuzzer::IsMyThread; @@ -553,6 +554,9 @@ void Fuzzer::ExecuteCallback(const uint8_t *Data, size_t Size) { TPC.RecordInitialStack(); + if (TotalNumberOfRuns % kRunsBeforeWeightedMutations == 0 && + Options.UseWeightedMutations) + MD.AssignMutationWeights(); TotalNumberOfRuns++; assert(InFuzzingThread()); if (SMR.IsClient()) Index: lib/fuzzer/FuzzerMutate.h =================================================================== --- lib/fuzzer/FuzzerMutate.h +++ lib/fuzzer/FuzzerMutate.h @@ -93,10 +93,22 @@ Random &GetRand() { return Rand; } + // Returns a line of output containing usefulness of each mutation. void PrintMutationStats(); + // Records tally of mutations resulting in new coverage, for usefulness + // metric. void RecordUsefulMutations(); + // Retrieves the usefulness statistics for each mutation currently being used. + void SetMutationStats(Vector &Stats); + + // Sets weights based on mutation performance during fuzzer run. + void AssignMutationWeights(); + + // Has a higher probability of returning a mutation with a greater weight. + size_t WeightedIndex(); + private: struct Mutator { size_t (MutationDispatcher::*Fn)(uint8_t *Data, size_t Size, size_t Max); @@ -156,6 +168,9 @@ Vector Mutators; Vector DefaultMutators; + + // Used to weight mutations based on usefulness. + Vector MutationWeights; }; } // namespace fuzzer Index: lib/fuzzer/FuzzerMutate.cpp =================================================================== --- lib/fuzzer/FuzzerMutate.cpp +++ lib/fuzzer/FuzzerMutate.cpp @@ -19,6 +19,10 @@ namespace fuzzer { const size_t Dictionary::kMaxDictSize; +double SumOfWeights; +double SumOfStats; +Vector Stats; +const double kDefaultMutationWeight = 1; static void PrintASCII(const Word &W, const char *PrintAfter) { PrintASCII(W.data(), W.size(), PrintAfter); @@ -60,6 +64,9 @@ if (EF->LLVMFuzzerCustomCrossOver) Mutators.push_back( {&MutationDispatcher::Mutate_CustomCrossOver, "CustomCrossOver", 0, 0}); + + // For weighted mutation selection + MutationWeights.resize(DefaultMutators.size(), 0); } static char RandCh(Random &Rand) { @@ -514,8 +521,12 @@ // Some mutations may fail (e.g. can't insert more bytes if Size == MaxSize), // in which case they will return 0. // Try several times before returning un-mutated data. + Mutator *M; for (int Iter = 0; Iter < 100; Iter++) { - auto M = &Mutators[Rand(Mutators.size())]; + if (Options.UseWeightedMutations) + M = &Mutators[WeightedIndex()]; + else + M = &Mutators[Rand(Mutators.size())]; size_t NewSize = (this->*(M->Fn))(Data, Size, MaxSize); if (NewSize && NewSize <= MaxSize) { if (Options.OnlyASCII) @@ -579,4 +590,45 @@ Printf("\n"); } +// Refreshes current mutation stats recalculated based on most previous runs. +void MutationDispatcher::SetMutationStats(Vector &Stats) { + Stats.resize(Mutators.size(), 0); + for (auto &M : Mutators) { + double UsefulPercentage = M.TotalCount ? (M.UsefulCount) / M.TotalCount : 0; + Stats.push_back(UsefulPercentage); + } +} + +void MutationDispatcher::AssignMutationWeights() { + SumOfStats = 0; + SumOfWeights = 0; + SetMutationStats(Stats); + for (const double &Stat : Stats) SumOfStats += Stat; + // Convert stats into weights. + for (size_t i = 0; i < Mutators.size(); i++) { + // Set to default weight if usefulness is zero so far. + if (Stats[i] == 0) + MutationWeights[i] = kDefaultMutationWeight; + else + MutationWeights[i] = + ((Stats[i] * 1000) / SumOfStats) + kDefaultMutationWeight; + SumOfWeights += MutationWeights[i]; + } +} + +// Returns mutations with greater weights more often, but will return even the +// lowest weighted mutations. +size_t MutationDispatcher::WeightedIndex() { + double RandomMark = Rand(0, SumOfWeights); + for (size_t i = 0; i < MutationWeights.size(); i++) { + if (RandomMark <= MutationWeights[i]) + return i; + else + RandomMark -= MutationWeights[i]; + } + // Fallback, should never get to this point. + assert(false); + return 0; +} + } // namespace fuzzer Index: lib/fuzzer/FuzzerOptions.h =================================================================== --- lib/fuzzer/FuzzerOptions.h +++ lib/fuzzer/FuzzerOptions.h @@ -53,6 +53,7 @@ int PrintNewCovFuncs = 0; bool PrintFinalStats = false; bool PrintMutationStats = false; + bool UseWeightedMutations = false; bool PrintCorpusStats = false; bool PrintCoverage = false; bool PrintUnstableStats = false; Index: test/fuzzer/fuzzer-weightedmutations.test =================================================================== --- /dev/null +++ test/fuzzer/fuzzer-weightedmutations.test @@ -0,0 +1,6 @@ +RUN: %cpp_compiler %S/SimpleTest.cpp -o %t-WeightedMutationsTest + +# Weighted mutations only trigger after first 10,000 runs, hence flag. +RUN: not %run %t-WeightedMutationsTest -use_weighted_mutations=1 -runs=100000 2>&1 | FileCheck %s + +CHECK: BINGO