Index: lib/fuzzer/FuzzerCorpus.h =================================================================== --- lib/fuzzer/FuzzerCorpus.h +++ lib/fuzzer/FuzzerCorpus.h @@ -233,6 +233,15 @@ return false; } + bool CheckFeature(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 @@ -615,6 +615,13 @@ Options.PrintFinalStats = Flags.print_final_stats; Options.PrintCorpusStats = Flags.print_corpus_stats; Options.PrintCoverage = Flags.print_coverage; + if (Flags.dump_unstable_coverage && Flags.dump_coverage) { + Printf("Can not use dump_unstable_coverage and dump_coverage options together\n"); + _Exit(1); + } + if (Flags.dump_unstable_coverage == TracePC::AllUnstable || + Flags.dump_unstable_coverage == TracePC::IgnoreInitUnstable) + Options.DumpUnstableCoverage = Flags.dump_unstable_coverage; Options.DumpCoverage = Flags.dump_coverage; if (Flags.exit_on_src_pos) Options.ExitOnSrcPos = Flags.exit_on_src_pos; Index: lib/fuzzer/FuzzerExtFunctions.def =================================================================== --- lib/fuzzer/FuzzerExtFunctions.def +++ lib/fuzzer/FuzzerExtFunctions.def @@ -45,4 +45,4 @@ EXT_FUNC(__sanitizer_set_death_callback, void, (void (*)(void)), true); EXT_FUNC(__sanitizer_set_report_fd, void, (void*), false); EXT_FUNC(__sanitizer_dump_coverage, void, (const uintptr_t *, uintptr_t), - false); + true); Index: lib/fuzzer/FuzzerFlags.def =================================================================== --- lib/fuzzer/FuzzerFlags.def +++ lib/fuzzer/FuzzerFlags.def @@ -110,6 +110,15 @@ FUZZER_FLAG_INT(dump_coverage, 0, "Deprecated." " If 1, dump coverage information as a" " .sancov file at exit.") +FUZZER_FLAG_INT(dump_unstable_coverage, 0, "Experimental." + " Is not compatible with dump_coverage flag." + " Dumps unstable edge coverage information" + " as a .sancov file at exit." + " Executes every input 3 times in total if a unique feature" + " is found during the first execution." + " If 1, compares all 3 runs for unstable edges" + " If 2, compares run 2 and 3 only taking into account" + " that run 1 is initialization run.") FUZZER_FLAG_INT(handle_segv, 1, "If 1, try to intercept SIGSEGV.") FUZZER_FLAG_INT(handle_bus, 1, "If 1, try to intercept SIGBUS.") FUZZER_FLAG_INT(handle_abrt, 1, "If 1, try to intercept SIGABRT.") Index: lib/fuzzer/FuzzerInternal.h =================================================================== --- lib/fuzzer/FuzzerInternal.h +++ lib/fuzzer/FuzzerInternal.h @@ -67,6 +67,7 @@ static void StaticGracefulExitCallback(); void ExecuteCallback(const uint8_t *Data, size_t Size); + void 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 @@ -351,6 +351,8 @@ void Fuzzer::PrintFinalStats() { if (Options.PrintCoverage) TPC.PrintCoverage(); + if (Options.DumpUnstableCoverage) + TPC.DumpUnstableCoverage(); if (Options.DumpCoverage) TPC.DumpCoverage(); if (Options.PrintCorpusStats) @@ -443,6 +445,36 @@ } } +void Fuzzer::CheckForUnstableCounters(const uint8_t *Data, size_t Size) { + auto CBSetupAndRun = [&]() { + UnitStartTime = system_clock::now(); + TPC.ResetMaps(); + RunningCB = true; + CB(Data, Size); + RunningCB = false; + UnitStopTime = system_clock::now(); + }; + + // Copy original run counters into our unstable counters + if (Options.DumpUnstableCoverage == TPC.AllUnstable) + TPC.InitializeUnstableCounters(); + + // First Rerun + CBSetupAndRun(); + + if (Options.DumpUnstableCoverage == TPC.AllUnstable) + TPC.UpdateUnstableCounters(); + else + TPC.InitializeUnstableCounters(); + + // Second Rerun + CBSetupAndRun(); + + TPC.UpdateUnstableCounters(); + + TPC.ApplyUnstableCounters(); +} + bool Fuzzer::RunOne(const uint8_t *Data, size_t Size, bool MayDeleteFile, InputInfo *II, bool *FoundUniqFeatures) { if (!Size) @@ -453,18 +485,51 @@ UniqFeatureSetTmp.clear(); size_t FoundUniqFeaturesOfII = 0; size_t NumUpdatesBefore = Corpus.NumFeatureUpdates(); - TPC.CollectFeatures([&](size_t Feature) { - if (Corpus.AddFeature(Feature, Size, Options.Shrink)) - UniqFeatureSetTmp.push_back(Feature); - if (Options.ReduceInputs && II) - if (std::binary_search(II->UniqFeatureSet.begin(), - II->UniqFeatureSet.end(), Feature)) - FoundUniqFeaturesOfII++; - }); + int NumNewFeaturesForUnstable = 0; + + // If DumpUnstableCoverage, we don't add the features now + // since more checks must be done. + if (Options.DumpUnstableCoverage) + TPC.CollectFeatures([&](size_t Feature) { + if (Corpus.CheckFeature(Feature, Size)) + NumNewFeaturesForUnstable++; + }); + else + TPC.CollectFeatures([&](size_t Feature) { + if (Corpus.AddFeature(Feature, Size, Options.Shrink)) + UniqFeatureSetTmp.push_back(Feature); + if (Options.ReduceInputs && II) + if (std::binary_search(II->UniqFeatureSet.begin(), + II->UniqFeatureSet.end(), Feature)) + FoundUniqFeaturesOfII++; + }); + + // If dump_unstable_coveragem and found new features, execute + // the same input two more times to detect unstable edges. + if (Options.DumpUnstableCoverage && NumNewFeaturesForUnstable) + CheckForUnstableCounters(Data, Size); + + // CollectFeatures for dump_unstable_coverage now that the checks are done. + if (Options.DumpUnstableCoverage) + TPC.CollectFeatures([&](size_t Feature) { + if (Corpus.AddFeature(Feature, Size, Options.Shrink)) + UniqFeatureSetTmp.push_back(Feature); + if (Options.ReduceInputs && II) + if (std::binary_search(II->UniqFeatureSet.begin(), + II->UniqFeatureSet.end(), Feature)) + FoundUniqFeaturesOfII++; + }); + if (FoundUniqFeatures) *FoundUniqFeatures = FoundUniqFeaturesOfII; PrintPulseAndReportSlowInput(Data, Size); size_t NumNewFeatures = Corpus.NumFeatureUpdates() - NumUpdatesBefore; + + // If dump_unstable_coverage, execute the same input two more times to detect + // unstable edges. + if (NumNewFeatures && Options.DumpUnstableCoverage) + 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 @@ -55,6 +55,7 @@ bool PrintCorpusStats = false; bool PrintCoverage = false; bool DumpCoverage = false; + int DumpUnstableCoverage = 0; bool DetectLeaks = true; int PurgeAllocatorIntervalSec = 1; int TraceMalloc = 0; 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 DumpUnstableCoverageOptions { + AllUnstable = 1, + IgnoreInitUnstable = 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); @@ -103,6 +108,7 @@ void PrintCoverage(); void DumpCoverage(); + void DumpUnstableCoverage(); template void IterateCoveredFunctions(CallBack CB); @@ -135,7 +141,18 @@ void SetFocusFunction(const std::string &FuncName); bool ObservedFocusFunction(); + void InitializeUnstableCounters(); + void UpdateUnstableCounters(); + 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]; + bool UseCounters = false; bool UseValueProfile = false; bool DoPrintNewPCs = false; @@ -161,9 +178,6 @@ size_t NumPCTables; size_t NumPCsInPCTables; - uint8_t *Counters() const; - uintptr_t *PCs() const; - Set ObservedPCs; Set ObservedFuncs; @@ -172,6 +186,9 @@ ValueBitMap ValueProfileMap; uintptr_t InitialStack; + + uintptr_t *PCs() const; + uint8_t *Counters() const; }; template Index: lib/fuzzer/FuzzerTracePC.cpp =================================================================== --- lib/fuzzer/FuzzerTracePC.cpp +++ lib/fuzzer/FuzzerTracePC.cpp @@ -59,6 +59,52 @@ return Res; } +// Initializes unstable counters by copying Inline8bitCounters to unstable counters. +void TracePC::InitializeUnstableCounters() { + if (NumInline8bitCounters == NumPCsInPCTables) { + size_t UnstableIdx = 1; + for (size_t i = 0; i < NumModulesWithInline8bitCounters; i++) { + uint8_t *Beg = ModuleCounters[i].Start; + size_t Size = ModuleCounters[i].Stop - Beg; + assert(Size == + (size_t)(ModulePCTable[i].Stop - ModulePCTable[i].Start)); + for (size_t j = 0; j < Size; j++, UnstableIdx++) + UnstableCounters[UnstableIdx] = Beg[j]; + } + } +} + +// Compares the current counters with counters from previous runs +// and records differences as unstable edges. +void TracePC::UpdateUnstableCounters() { + if (NumInline8bitCounters == NumPCsInPCTables) { + size_t UnstableIdx = 1; + for (size_t i = 0; i < NumModulesWithInline8bitCounters; i++) { + uint8_t *Beg = ModuleCounters[i].Start; + size_t Size = ModuleCounters[i].Stop - Beg; + assert(Size == + (size_t)(ModulePCTable[i].Stop - ModulePCTable[i].Start)); + for (size_t j = 0; j < Size; j++, UnstableIdx++) + if (Beg[j] < UnstableCounters[UnstableIdx]) + UnstableCounters[UnstableIdx] = Beg[j]; + } + } +} + +// Move UnstableCounters to Inline8bitCounters to collect the features. +void TracePC::ApplyUnstableCounters() { + if (NumInline8bitCounters == NumPCsInPCTables) { + size_t UnstableIdx = 1; + for (size_t i = 0; i < NumModulesWithInline8bitCounters; i++) { + uint8_t *Beg = ModuleCounters[i].Start; + size_t Size = ModuleCounters[i].Stop - Beg; + assert(Size == + (size_t)(ModulePCTable[i].Stop - ModulePCTable[i].Start)); + for (size_t j = 0; j < Size; j++, UnstableIdx++) + Beg[j] = UnstableCounters[UnstableIdx]; + } + } +} void TracePC::HandleInline8bitCountersInit(uint8_t *Start, uint8_t *Stop) { if (Start == Stop) return; @@ -299,12 +345,36 @@ } void TracePC::DumpCoverage() { - if (EF->__sanitizer_dump_coverage) { - Vector PCsCopy(GetNumPCs()); - for (size_t i = 0; i < GetNumPCs(); i++) - PCsCopy[i] = PCs()[i] ? GetPreviousInstructionPc(PCs()[i]) : 0; - EF->__sanitizer_dump_coverage(PCsCopy.data(), PCsCopy.size()); + if (!EF->__sanitizer_dump_coverage) + return; + + Vector PCsCopy(GetNumPCs()); + for (size_t i = 0; i < GetNumPCs(); i++) + PCsCopy[i] = PCs()[i] ? GetPreviousInstructionPc(PCs()[i]) : 0; + EF->__sanitizer_dump_coverage(PCsCopy.data(), PCsCopy.size()); +} + +void TracePC::DumpUnstableCoverage() { + if (!EF->__sanitizer_dump_coverage) + return; + + Vector PCsCopy(GetNumPCs()); + for (size_t i = 0; i < GetNumPCs(); i++) + PCsCopy[i] = 0; + + size_t UnstableIdx = 1; + if (NumInline8bitCounters == NumPCsInPCTables) { + for (size_t i = 0; i < NumModulesWithInline8bitCounters; i++) { + uint8_t *Beg = ModuleCounters[i].Start; + size_t Size = ModuleCounters[i].Stop - Beg; + assert(Size == + (size_t)(ModulePCTable[i].Stop - ModulePCTable[i].Start)); + for (size_t j = 0; j < Size; j++, UnstableIdx++) + if (UnstableCounters[UnstableIdx] == kUnstableCounter) + PCsCopy[UnstableIdx] = ModulePCTable[i].Start[j].PC; + } } + EF->__sanitizer_dump_coverage(PCsCopy.data(), PCsCopy.size()); } // Value profile.