Index: lib/fuzzer/FuzzerDriver.cpp =================================================================== --- lib/fuzzer/FuzzerDriver.cpp +++ lib/fuzzer/FuzzerDriver.cpp @@ -620,7 +620,8 @@ Options.PrintCoverage = Flags.print_coverage; Options.PrintUnstableStats = Flags.print_unstable_stats; if (Flags.handle_unstable == TracePC::MinUnstable || - Flags.handle_unstable == TracePC::ZeroUnstable) + Flags.handle_unstable == TracePC::ZeroUnstable || + Flags.handle_unstable == TracePC::PoisonUnstable) Options.HandleUnstable = Flags.handle_unstable; Options.DumpCoverage = Flags.dump_coverage; if (Flags.exit_on_src_pos) Index: lib/fuzzer/FuzzerInternal.h =================================================================== --- lib/fuzzer/FuzzerInternal.h +++ lib/fuzzer/FuzzerInternal.h @@ -67,7 +67,7 @@ static void StaticGracefulExitCallback(); void ExecuteCallback(const uint8_t *Data, size_t Size); - void CheckForUnstableCounters(const uint8_t *Data, size_t Size); + int CheckForUnstableCounters(const uint8_t *Data, size_t Size); 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,7 @@ } } -void Fuzzer::CheckForUnstableCounters(const uint8_t *Data, size_t Size) { +int Fuzzer::CheckForUnstableCounters(const uint8_t *Data, size_t Size) { auto CBSetupAndRun = [&]() { ScopedEnableMsanInterceptorChecks S; UnitStartTime = system_clock::now(); @@ -460,21 +460,25 @@ UnitStopTime = system_clock::now(); }; + int NumNewUnstableEdges = 0; + // Copy original run counters into our unstable counters TPC.InitializeUnstableCounters(); // First Rerun CBSetupAndRun(); - TPC.UpdateUnstableCounters(Options.HandleUnstable); + NumNewUnstableEdges += TPC.UpdateUnstableCounters(Options.HandleUnstable); // Second Rerun CBSetupAndRun(); - TPC.UpdateUnstableCounters(Options.HandleUnstable); + NumNewUnstableEdges += TPC.UpdateUnstableCounters(Options.HandleUnstable); // Move minimum hit counts back to ModuleInline8bitCounters if (Options.HandleUnstable == TracePC::MinUnstable || Options.HandleUnstable == TracePC::ZeroUnstable) TPC.ApplyUnstableCounters(); + + return NumNewUnstableEdges; } bool Fuzzer::RunOne(const uint8_t *Data, size_t Size, bool MayDeleteFile, @@ -488,6 +492,7 @@ size_t FoundUniqFeaturesOfII = 0; size_t NumUpdatesBefore = Corpus.NumFeatureUpdates(); bool NewFeaturesUnstable = false; + int NumNewUnstableEdges = 0; if (Options.HandleUnstable || Options.PrintUnstableStats) { TPC.CollectFeatures([&](size_t Feature) { @@ -495,7 +500,7 @@ NewFeaturesUnstable = true; }); if (NewFeaturesUnstable) - CheckForUnstableCounters(Data, Size); + NumNewUnstableEdges = CheckForUnstableCounters(Data, Size); } TPC.CollectFeatures([&](size_t Feature) { @@ -512,6 +517,11 @@ PrintPulseAndReportSlowInput(Data, Size); size_t NumNewFeatures = Corpus.NumFeatureUpdates() - NumUpdatesBefore; + if (Options.HandleUnstable == TPC.PoisonUnstable) { + assert(NumNewFeatures >= NumNewUnstableEdges); + NumNewFeatures -= NumNewUnstableEdges; + } + if (NumNewFeatures) { TPC.UpdateObservedPCs(); Corpus.AddToCorpus({Data, Data + Size}, NumNewFeatures, MayDeleteFile, Index: lib/fuzzer/FuzzerTracePC.h =================================================================== --- lib/fuzzer/FuzzerTracePC.h +++ lib/fuzzer/FuzzerTracePC.h @@ -76,6 +76,7 @@ enum HandleUnstableOptions { MinUnstable = 1, ZeroUnstable = 2, + PoisonUnstable = 3, }; void HandleInit(uint32_t *Start, uint32_t *Stop); @@ -142,7 +143,7 @@ bool ObservedFocusFunction(); void InitializeUnstableCounters(); - void UpdateUnstableCounters(int UnstableMode); + int UpdateUnstableCounters(int UnstableMode); void ApplyUnstableCounters(); private: Index: lib/fuzzer/FuzzerTracePC.cpp =================================================================== --- lib/fuzzer/FuzzerTracePC.cpp +++ lib/fuzzer/FuzzerTracePC.cpp @@ -81,17 +81,22 @@ // Compares the current counters with counters from previous runs // and records differences as unstable edges. -void TracePC::UpdateUnstableCounters(int UnstableMode) { +int TracePC::UpdateUnstableCounters(int UnstableMode) { + int NumNewUnstableEdges = 0; IterateInline8bitCounters([&](int i, int j, int UnstableIdx) { if (ModuleCounters[i].Start[j] != UnstableCounters[UnstableIdx].Counter) { + if (!UnstableCounters[UnstableIdx].IsUnstable) + NumNewUnstableEdges++; UnstableCounters[UnstableIdx].IsUnstable = true; if (UnstableMode == ZeroUnstable) UnstableCounters[UnstableIdx].Counter = 0; - if (UnstableMode == MinUnstable && - ModuleCounters[i].Start[j] < UnstableCounters[UnstableIdx].Counter) + else if (UnstableMode == MinUnstable && + ModuleCounters[i].Start[j] < + UnstableCounters[UnstableIdx].Counter) UnstableCounters[UnstableIdx].Counter = ModuleCounters[i].Start[j]; } }); + return NumNewUnstableEdges; } // Moves the minimum hit counts to ModuleCounters. Index: test/fuzzer/HandleUnstableTest.cpp =================================================================== --- /dev/null +++ test/fuzzer/HandleUnstableTest.cpp @@ -0,0 +1,83 @@ +#include +#include +#include +#include + +int x = 0; +bool skip0 = false; +bool skip1 = false; +bool skip2 = false; + +__attribute__((noinline)) void det0() { x++; } +__attribute__((noinline)) void det1() { x++; } +__attribute__((noinline)) void det2() { x++; } +__attribute__((noinline)) void det3() { x++; } +__attribute__((noinline)) void det4() { x++; } + +__attribute__((noinline)) void ini0() { x++; } +__attribute__((noinline)) void ini1() { x++; } +__attribute__((noinline)) void ini2() { x++; } + +__attribute__((noinline)) void poison0() { x++; } +__attribute__((noinline)) void poison1() { x++; } +__attribute__((noinline)) void poison2() { x++; } + +__attribute__((noinline)) void t0() { x++; } +__attribute__((noinline)) void t1() { x++; } +__attribute__((noinline)) void t2() { x++; } +__attribute__((noinline)) void t3() { x++; } +__attribute__((noinline)) void t4() { x++; } + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + if (Size == 1 && Data[0] == 'A' && !skip0) { + skip0 = true; + ini0(); + poison0(); + } + if (Size == 1 && Data[0] == 'B' && !skip1) { + skip1 = true; + ini1(); + poison1(); + } + if (Size == 1 && Data[0] == 'C' && !skip2) { + skip2 = true; + ini2(); + poison2(); + } + + if (skip0) + poison0(); + if (skip1) + poison1(); + if (skip2) + poison2(); + + det0(); + det1(); + int a = rand(); + det2(); + + switch (a % 5) { + case 0: + t0(); + break; + case 1: + t1(); + break; + case 2: + t2(); + break; + case 3: + t3(); + break; + case 4: + t4(); + break; + default: + assert(false); + } + + det3(); + det4(); + return 0; +} Index: test/fuzzer/handle_unstable_poisonunstable.test =================================================================== --- /dev/null +++ test/fuzzer/handle_unstable_poisonunstable.test @@ -0,0 +1,30 @@ +RUN: %cpp_compiler %S/HandleUnstableTest.cpp -o %t-HandleUnstablePoisonUnstableTest +RUN: %run %t-HandleUnstablePoisonUnstableTest -print_coverage=1 -handle_unstable=3 -runs=10000 2>&1 | FileCheck %s --check-prefix=UNSTABLE +UNSTABLE: det0() +UNSTABLE: det1() +UNSTABLE: det2() +UNSTABLE: det3() +UNSTABLE: det4() +UNSTABLE: poison0() +UNSTABLE: poison1() +UNSTABLE: poison2() +UNSTABLE: t0() +UNSTABLE: t1() +UNSTABLE: t2() +UNSTABLE: t3() +UNSTABLE: t4() + +RUN: %run %t-HandleUnstablePoisonUnstableTest -print_coverage=1 -runs=100000 2>&1 | FileCheck %s --check-prefix=NORMAL +NORMAL-DAG: det0() +NORMAL-DAG: det1() +NORMAL-DAG: det2() +NORMAL-DAG: det3() +NORMAL-DAG: det4() +NORMAL-DAG: ini0() +NORMAL-DAG: ini1() +NORMAL-DAG: ini2() +NORMAL-DAG: t0() +NORMAL-DAG: t1() +NORMAL-DAG: t2() +NORMAL-DAG: t3() +NORMAL-DAG: t4()