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 @@ -156,3 +156,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") Index: lib/fuzzer/FuzzerLoop.cpp =================================================================== --- lib/fuzzer/FuzzerLoop.cpp +++ lib/fuzzer/FuzzerLoop.cpp @@ -543,6 +543,9 @@ void Fuzzer::ExecuteCallback(const uint8_t *Data, size_t Size) { TPC.RecordInitialStack(); TotalNumberOfRuns++; + assert(TotalNumberOfRuns > 0); + if (TotalNumberOfRuns % 10000 == 0 && Options.UseWeightedMutations) + MD.AssignMutationWeights(); assert(InFuzzingThread()); if (SMR.IsClient()) SMR.WriteByteArray(Data, Size); Index: lib/fuzzer/FuzzerMutate.h =================================================================== --- lib/fuzzer/FuzzerMutate.h +++ lib/fuzzer/FuzzerMutate.h @@ -93,10 +93,21 @@ Random &GetRand() { return Rand; } + // Returns a line of output containing usefulness of each mutation. void PrintMutationStats(); + // Gets tally of mutations resulting in new coverage for usefulness metric. void RecordUsefulMutations(); + // Retrieves the usefulness statistics for each mutation currently being used. + void GetMutationStats(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 +167,10 @@ Vector Mutators; Vector DefaultMutators; + + // For experimental runtime mutation stats leveraging. + Vector MutationWeights; + bool UseMutationWeights = false; }; } // namespace fuzzer Index: lib/fuzzer/FuzzerMutate.cpp =================================================================== --- lib/fuzzer/FuzzerMutate.cpp +++ lib/fuzzer/FuzzerMutate.cpp @@ -19,6 +19,7 @@ namespace fuzzer { const size_t Dictionary::kMaxDictSize; +size_t SumOfWeights; static void PrintASCII(const Word &W, const char *PrintAfter) { PrintASCII(W.data(), W.size(), PrintAfter); @@ -60,6 +61,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) { @@ -516,6 +520,7 @@ // Try several times before returning un-mutated data. for (int Iter = 0; Iter < 100; Iter++) { auto M = &Mutators[Rand(Mutators.size())]; + if (UseMutationWeights) M = &Mutators[WeightedIndex()]; size_t NewSize = (this->*(M->Fn))(Data, Size, MaxSize); if (NewSize && NewSize <= MaxSize) { if (Options.OnlyASCII) @@ -579,4 +584,45 @@ Printf("\n"); } +void MutationDispatcher::GetMutationStats(Vector &Stats) { + Stats.resize(Mutators.size(), 0); + for (auto &M : Mutators) { + double UsefulPercentage = + M.TotalCount ? (100.0 * M.UsefulCount) / M.TotalCount : 0; + Stats.push_back(UsefulPercentage); + } +} + +void MutationDispatcher::AssignMutationWeights() { + Vector Stats; + GetMutationStats(Stats); + double SumOfStats = 0; + SumOfWeights = 0; + for (const double &Stat : Stats) SumOfStats += Stat; + // Convert stats into weights + for (size_t i = 0; i < Mutators.size(); i++) { + // Set to least weighted (1) if usefulness is zero so far + if (Stats[i] == 0) MutationWeights[i] = 1; + // 1000 due to 3 decimal places + else + MutationWeights[i] = static_cast((Stats[i] * 1000) / SumOfStats); + } + for (const size_t Weight : MutationWeights) SumOfWeights += Weight; + UseMutationWeights = true; +} + +size_t MutationDispatcher::WeightedIndex() { + // Will return even the lowest weighted mutations, but ones with greater + // weights have a higher probability of being returned. + size_t RandomMark = Rand(1, SumOfWeights); + for (size_t i = 0; i < MutationWeights.size(); i++) { + if (RandomMark - MutationWeights[i] <= 0) + return i; + else + RandomMark -= MutationWeights[i]; + } + // Fallback, should never get to this point. + 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