Index: lib/fuzzer/FuzzerCorpus.h =================================================================== --- lib/fuzzer/FuzzerCorpus.h +++ lib/fuzzer/FuzzerCorpus.h @@ -238,6 +238,12 @@ return false; } + bool IsFeatureNew(size_t Idx, uint32_t NewSize, bool Shrink) { + assert(NewSize); + uint32_t OldSize = GetFeature(Idx % kFeatureSetSize); + return OldSize == 0 || (Shrink && OldSize > NewSize); + } + size_t NumFeatures() const { return NumAddedFeatures; } size_t NumFeatureUpdates() const { return NumUpdatedFeatures; } Index: lib/fuzzer/FuzzerDriver.cpp =================================================================== --- lib/fuzzer/FuzzerDriver.cpp +++ lib/fuzzer/FuzzerDriver.cpp @@ -619,6 +619,8 @@ Options.PrintCorpusStats = Flags.print_corpus_stats; Options.PrintCoverage = Flags.print_coverage; Options.PrintUnstableStats = Flags.print_unstable_stats; + if (Flags.handle_unstable) + Options.HandleUnstable = Flags.handle_unstable; Options.DumpCoverage = Flags.dump_coverage; if (Flags.exit_on_src_pos) Options.ExitOnSrcPos = Flags.exit_on_src_pos; Index: lib/fuzzer/FuzzerFlags.def =================================================================== --- lib/fuzzer/FuzzerFlags.def +++ lib/fuzzer/FuzzerFlags.def @@ -110,6 +110,11 @@ FUZZER_FLAG_INT(dump_coverage, 0, "Deprecated." " If 1, dump coverage information as a" " .sancov file at exit.") +FUZZER_FLAG_INT(handle_unstable, 0, "Experimental." + " Executes every input 3 times in total if a unique feature" + " is found during the first execution." + " If 1, we only use the minimum hit count from the 3 runs" + " to determine whether an input is interesting.") FUZZER_FLAG_INT(print_unstable_stats, 0, "Experimental." " If 1, print unstable statistics at exit.") FUZZER_FLAG_INT(handle_segv, 1, "If 1, try to intercept SIGSEGV.") Index: lib/fuzzer/FuzzerLoop.cpp =================================================================== --- lib/fuzzer/FuzzerLoop.cpp +++ lib/fuzzer/FuzzerLoop.cpp @@ -465,11 +465,15 @@ // First Rerun CBSetupAndRun(); - TPC.UpdateUnstableCounters(); + TPC.UpdateUnstableCounters(Options.HandleUnstable); // Second Rerun CBSetupAndRun(); - TPC.UpdateUnstableCounters(); + TPC.UpdateUnstableCounters(Options.HandleUnstable); + + // Move minimum hit counts back to ModuleInline8bitCounters + if (Options.HandleUnstable) + TPC.ApplyUnstableCounters(); } bool Fuzzer::RunOne(const uint8_t *Data, size_t Size, bool MayDeleteFile, @@ -482,6 +486,17 @@ UniqFeatureSetTmp.clear(); size_t FoundUniqFeaturesOfII = 0; size_t NumUpdatesBefore = Corpus.NumFeatureUpdates(); + bool NewFeaturesUnstable = false; + + if (Options.HandleUnstable || Options.PrintUnstableStats) { + TPC.CollectFeatures([&](size_t Feature) { + if (Corpus.IsFeatureNew(Feature, Size, Options.Shrink)) + NewFeaturesUnstable = true; + }); + if (NewFeaturesUnstable) + CheckForUnstableCounters(Data, Size); + } + TPC.CollectFeatures([&](size_t Feature) { if (Corpus.AddFeature(Feature, Size, Options.Shrink)) UniqFeatureSetTmp.push_back(Feature); @@ -490,16 +505,12 @@ II->UniqFeatureSet.end(), Feature)) FoundUniqFeaturesOfII++; }); + if (FoundUniqFeatures) *FoundUniqFeatures = FoundUniqFeaturesOfII; PrintPulseAndReportSlowInput(Data, Size); size_t NumNewFeatures = Corpus.NumFeatureUpdates() - NumUpdatesBefore; - // If print_unstable_stats, execute the same input two more times to detect - // unstable edges. - if (NumNewFeatures && Options.PrintUnstableStats) - CheckForUnstableCounters(Data, Size); - if (NumNewFeatures) { TPC.UpdateObservedPCs(); Corpus.AddToCorpus({Data, Data + Size}, NumNewFeatures, MayDeleteFile, Index: lib/fuzzer/FuzzerOptions.h =================================================================== --- lib/fuzzer/FuzzerOptions.h +++ lib/fuzzer/FuzzerOptions.h @@ -56,6 +56,7 @@ bool PrintCorpusStats = false; bool PrintCoverage = false; bool PrintUnstableStats = false; + int HandleUnstable = 0; bool DumpCoverage = false; bool DetectLeaks = true; int PurgeAllocatorIntervalSec = 1; Index: lib/fuzzer/FuzzerTracePC.h =================================================================== --- lib/fuzzer/FuzzerTracePC.h +++ lib/fuzzer/FuzzerTracePC.h @@ -138,15 +138,16 @@ bool ObservedFocusFunction(); void InitializeUnstableCounters(); - void UpdateUnstableCounters(); + void UpdateUnstableCounters(int UnstableMode); + void ApplyUnstableCounters(); private: - // Value used to represent unstable edge. - static constexpr int16_t kUnstableCounter = -1; + struct UnstableEdge { + uint8_t Counter; + bool IsUnstable; + }; - // Uses 16-bit signed type to be able to accommodate any possible value from - // uint8_t counter and -1 constant as well. - int16_t UnstableCounters[kNumPCs]; + UnstableEdge UnstableCounters[kNumPCs]; bool UseCounters = false; uint32_t UseValueProfileMask = false; Index: lib/fuzzer/FuzzerTracePC.cpp =================================================================== --- lib/fuzzer/FuzzerTracePC.cpp +++ lib/fuzzer/FuzzerTracePC.cpp @@ -75,17 +75,26 @@ // counters. void TracePC::InitializeUnstableCounters() { IterateInline8bitCounters([&](int i, int j, int UnstableIdx) { - if (UnstableCounters[UnstableIdx] != kUnstableCounter) - UnstableCounters[UnstableIdx] = ModuleCounters[i].Start[j]; + UnstableCounters[UnstableIdx].Counter = ModuleCounters[i].Start[j]; }); } // Compares the current counters with counters from previous runs // and records differences as unstable edges. -void TracePC::UpdateUnstableCounters() { +void TracePC::UpdateUnstableCounters(int UnstableMode) { IterateInline8bitCounters([&](int i, int j, int UnstableIdx) { - if (ModuleCounters[i].Start[j] != UnstableCounters[UnstableIdx]) - UnstableCounters[UnstableIdx] = kUnstableCounter; + if (ModuleCounters[i].Start[j] != UnstableCounters[UnstableIdx].Counter) + UnstableCounters[UnstableIdx].IsUnstable = true; + if (UnstableMode && + ModuleCounters[i].Start[j] < UnstableCounters[UnstableIdx].Counter) + UnstableCounters[UnstableIdx].Counter = ModuleCounters[i].Start[j]; + }); +} + +// Moves the minimum hit counts to ModuleCounters. +void TracePC::ApplyUnstableCounters() { + IterateInline8bitCounters([&](int i, int j, int UnstableIdx) { + ModuleCounters[i].Start[j] = UnstableCounters[UnstableIdx].Counter; }); } @@ -340,7 +349,7 @@ void TracePC::PrintUnstableStats() { size_t count = 0; for (size_t i = 0; i < NumInline8bitCounters; i++) - if (UnstableCounters[i] == kUnstableCounter) + if (UnstableCounters[i].IsUnstable) count++; Printf("stat::stability_rate: %.2f\n", 100 - static_cast(count * 100) / NumInline8bitCounters); Index: test/fuzzer/handle_unstable_minunstable.test =================================================================== --- test/fuzzer/handle_unstable_minunstable.test +++ test/fuzzer/handle_unstable_minunstable.test @@ -0,0 +1,12 @@ +RUN: %cpp_compiler %S/PrintUnstableStatsTest.cpp -o %t-HandleUnstableMinUnstableTest +RUN: %run %t-HandleUnstableMinUnstableTest -print_coverage=1 -handle_unstable=1 -runs=100000 2>&1 | FileCheck %s --check-prefix=UNSTABLE +UNSTABLE-NOT: ini0() +UNSTABLE-NOT: ini1() +UNSTABLE-NOT: ini2() + +RUN: %run %t-HandleUnstableMinUnstableTest -print_coverage=1 -runs=100000 2>&1 | FileCheck %s --check-prefix=NORMAL +NORMAL-DAG: ini0() +NORMAL-DAG: ini1() +NORMAL-DAG: ini2() + +