Index: lib/fuzzer/FuzzerCorpus.h =================================================================== --- lib/fuzzer/FuzzerCorpus.h +++ lib/fuzzer/FuzzerCorpus.h @@ -233,6 +233,15 @@ return false; } + bool IsFeatureNew(size_t Idx, uint32_t NewSize) { + assert(NewSize); + Idx = Idx % kFeatureSetSize; + uint32_t OldSize = GetFeature(Idx); + if (OldSize == 0 || OldSize > NewSize) + return true; + return false; + } + 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,9 @@ Options.PrintCorpusStats = Flags.print_corpus_stats; Options.PrintCoverage = Flags.print_coverage; Options.PrintUnstableStats = Flags.print_unstable_stats; + if (Flags.handle_unstable == TracePC::MinUnstable || + Flags.handle_unstable == TracePC::PoisonUnstable) + 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,13 @@ 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." + " If 2, we only consider new features for edges observed to" + " be stable by poisoning an edge if found unstable.") 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/FuzzerInternal.h =================================================================== --- lib/fuzzer/FuzzerInternal.h +++ lib/fuzzer/FuzzerInternal.h @@ -67,7 +67,8 @@ static void StaticGracefulExitCallback(); void ExecuteCallback(const uint8_t *Data, size_t Size); - void CheckForUnstableCounters(const uint8_t *Data, size_t Size); + void CheckForUnstableCounters(const uint8_t *Data, size_t Size, + int UnstableMode); bool RunOne(const uint8_t *Data, size_t Size, bool MayDeleteFile = false, InputInfo *II = nullptr, bool *FoundUniqFeatures = nullptr); Index: lib/fuzzer/FuzzerLoop.cpp =================================================================== --- lib/fuzzer/FuzzerLoop.cpp +++ lib/fuzzer/FuzzerLoop.cpp @@ -449,7 +449,8 @@ } } -void Fuzzer::CheckForUnstableCounters(const uint8_t *Data, size_t Size) { +void Fuzzer::CheckForUnstableCounters(const uint8_t *Data, size_t Size, + int UnstableMode) { auto CBSetupAndRun = [&]() { ScopedEnableMsanInterceptorChecks S; UnitStartTime = system_clock::now(); @@ -465,11 +466,15 @@ // First Rerun CBSetupAndRun(); - TPC.UpdateUnstableCounters(); + TPC.UpdateUnstableCounters(UnstableMode); // Second Rerun CBSetupAndRun(); - TPC.UpdateUnstableCounters(); + TPC.UpdateUnstableCounters(UnstableMode); + + // Move minimum hit counts back to ModuleInline8bitCounters + if (UnstableMode == TPC.MinUnstable) + TPC.ApplyUnstableCounters(); } bool Fuzzer::RunOne(const uint8_t *Data, size_t Size, bool MayDeleteFile, @@ -482,6 +487,17 @@ UniqFeatureSetTmp.clear(); size_t FoundUniqFeaturesOfII = 0; size_t NumUpdatesBefore = Corpus.NumFeatureUpdates(); + int NumNewFeaturesForUnstable = 0; + + if (Options.HandleUnstable || Options.PrintUnstableStats) { + TPC.CollectFeatures([&](size_t Feature) { + if (Corpus.IsFeatureNew(Feature, Size)) + NumNewFeaturesForUnstable++; + }); + if (NumNewFeaturesForUnstable) + CheckForUnstableCounters(Data, Size, Options.HandleUnstable); + } + TPC.CollectFeatures([&](size_t Feature) { if (Corpus.AddFeature(Feature, Size, Options.Shrink)) UniqFeatureSetTmp.push_back(Feature); @@ -490,16 +506,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 @@ -73,6 +73,11 @@ // How many bits of PC are used from __sanitizer_cov_trace_pc. static const size_t kTracePcBits = 18; + enum HandleUnstableOptions { + MinUnstable = 1, + PoisonUnstable = 2, + }; + void HandleInit(uint32_t *Start, uint32_t *Stop); void HandleInline8bitCountersInit(uint8_t *Start, uint8_t *Stop); void HandlePCsInit(const uintptr_t *Start, const uintptr_t *Stop); @@ -137,15 +142,15 @@ 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; - - // 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]; + struct UnstableEdge { + uint8_t Counter; + bool IsUnstable; + UnstableEdge() : Counter(0), IsUnstable(0) {} + } 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 == MinUnstable && + 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; }); } @@ -337,7 +346,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);