diff options
| author | Mehmet Samet Duman <yongdohyun@projecttick.org> | 2026-04-02 18:42:50 +0300 |
|---|---|---|
| committer | Mehmet Samet Duman <yongdohyun@projecttick.org> | 2026-04-02 18:42:50 +0300 |
| commit | 5fad10f89c485cfdc7b99011f07609f8871160d4 (patch) | |
| tree | 1860b39753b652dfe54d3cbbc80c875f40198d1f /json4cpp/tests/thirdparty/Fuzzer/FuzzerLoop.cpp | |
| parent | 292baed7ac0cf84263263966ed32ed113cae857f (diff) | |
| parent | 9a737481aed085fd289f82dff1fa8c3c66627a7e (diff) | |
| download | Project-Tick-5fad10f89c485cfdc7b99011f07609f8871160d4.tar.gz Project-Tick-5fad10f89c485cfdc7b99011f07609f8871160d4.zip | |
Add 'json4cpp/' from commit '9a737481aed085fd289f82dff1fa8c3c66627a7e'
git-subtree-dir: json4cpp
git-subtree-mainline: 292baed7ac0cf84263263966ed32ed113cae857f
git-subtree-split: 9a737481aed085fd289f82dff1fa8c3c66627a7e
Diffstat (limited to 'json4cpp/tests/thirdparty/Fuzzer/FuzzerLoop.cpp')
| -rw-r--r-- | json4cpp/tests/thirdparty/Fuzzer/FuzzerLoop.cpp | 792 |
1 files changed, 792 insertions, 0 deletions
diff --git a/json4cpp/tests/thirdparty/Fuzzer/FuzzerLoop.cpp b/json4cpp/tests/thirdparty/Fuzzer/FuzzerLoop.cpp new file mode 100644 index 0000000000..73e058ff34 --- /dev/null +++ b/json4cpp/tests/thirdparty/Fuzzer/FuzzerLoop.cpp @@ -0,0 +1,792 @@ +//===- FuzzerLoop.cpp - Fuzzer's main loop --------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// Fuzzer's main loop. +//===----------------------------------------------------------------------===// + +#include "FuzzerCorpus.h" +#include "FuzzerInternal.h" +#include "FuzzerIO.h" +#include "FuzzerMutate.h" +#include "FuzzerRandom.h" +#include "FuzzerTracePC.h" +#include <algorithm> +#include <cstring> +#include <memory> +#include <set> + +#if defined(__has_include) +#if __has_include(<sanitizer / coverage_interface.h>) +#include <sanitizer/coverage_interface.h> +#endif +#if __has_include(<sanitizer / lsan_interface.h>) +#include <sanitizer/lsan_interface.h> +#endif +#endif + +#define NO_SANITIZE_MEMORY +#if defined(__has_feature) +#if __has_feature(memory_sanitizer) +#undef NO_SANITIZE_MEMORY +#define NO_SANITIZE_MEMORY __attribute__((no_sanitize_memory)) +#endif +#endif + +namespace fuzzer { +static const size_t kMaxUnitSizeToPrint = 256; + +thread_local bool Fuzzer::IsMyThread; + +static void MissingExternalApiFunction(const char *FnName) { + Printf("ERROR: %s is not defined. Exiting.\n" + "Did you use -fsanitize-coverage=... to build your code?\n", + FnName); + exit(1); +} + +#define CHECK_EXTERNAL_FUNCTION(fn) \ + do { \ + if (!(EF->fn)) \ + MissingExternalApiFunction(#fn); \ + } while (false) + +// Only one Fuzzer per process. +static Fuzzer *F; + +void Fuzzer::ResetEdgeCoverage() { + CHECK_EXTERNAL_FUNCTION(__sanitizer_reset_coverage); + EF->__sanitizer_reset_coverage(); +} + +void Fuzzer::ResetCounters() { + if (Options.UseCounters) + EF->__sanitizer_update_counter_bitset_and_clear_counters(0); +} + +void Fuzzer::PrepareCounters(Fuzzer::Coverage *C) { + if (Options.UseCounters) { + size_t NumCounters = EF->__sanitizer_get_number_of_counters(); + C->CounterBitmap.resize(NumCounters); + } +} + +// Records data to a maximum coverage tracker. Returns true if additional +// coverage was discovered. +bool Fuzzer::RecordMaxCoverage(Fuzzer::Coverage *C) { + bool Res = false; + + uint64_t NewBlockCoverage = EF->__sanitizer_get_total_unique_coverage(); + if (NewBlockCoverage > C->BlockCoverage) { + Res = true; + C->BlockCoverage = NewBlockCoverage; + } + + if (Options.UseIndirCalls && + EF->__sanitizer_get_total_unique_caller_callee_pairs) { + uint64_t NewCallerCalleeCoverage = + EF->__sanitizer_get_total_unique_caller_callee_pairs(); + if (NewCallerCalleeCoverage > C->CallerCalleeCoverage) { + Res = true; + C->CallerCalleeCoverage = NewCallerCalleeCoverage; + } + } + + if (Options.UseCounters) { + uint64_t CounterDelta = + EF->__sanitizer_update_counter_bitset_and_clear_counters( + C->CounterBitmap.data()); + if (CounterDelta > 0) { + Res = true; + C->CounterBitmapBits += CounterDelta; + } + } + + return Res; +} + +// Leak detection is expensive, so we first check if there were more mallocs +// than frees (using the sanitizer malloc hooks) and only then try to call lsan. +struct MallocFreeTracer { + void Start(int TraceLevel) { + this->TraceLevel = TraceLevel; + if (TraceLevel) + Printf("MallocFreeTracer: START\n"); + Mallocs = 0; + Frees = 0; + } + // Returns true if there were more mallocs than frees. + bool Stop() { + if (TraceLevel) + Printf("MallocFreeTracer: STOP %zd %zd (%s)\n", Mallocs.load(), + Frees.load(), Mallocs == Frees ? "same" : "DIFFERENT"); + bool Result = Mallocs > Frees; + Mallocs = 0; + Frees = 0; + TraceLevel = 0; + return Result; + } + std::atomic<size_t> Mallocs; + std::atomic<size_t> Frees; + int TraceLevel = 0; +}; + +static MallocFreeTracer AllocTracer; + +ATTRIBUTE_NO_SANITIZE_MEMORY +void MallocHook(const volatile void *ptr, size_t size) { + size_t N = AllocTracer.Mallocs++; + F->HandleMalloc(size); + if (int TraceLevel = AllocTracer.TraceLevel) { + Printf("MALLOC[%zd] %p %zd\n", N, ptr, size); + if (TraceLevel >= 2 && EF) + EF->__sanitizer_print_stack_trace(); + } +} + +ATTRIBUTE_NO_SANITIZE_MEMORY +void FreeHook(const volatile void *ptr) { + size_t N = AllocTracer.Frees++; + if (int TraceLevel = AllocTracer.TraceLevel) { + Printf("FREE[%zd] %p\n", N, ptr); + if (TraceLevel >= 2 && EF) + EF->__sanitizer_print_stack_trace(); + } +} + +// Crash on a single malloc that exceeds the rss limit. +void Fuzzer::HandleMalloc(size_t Size) { + if (!Options.RssLimitMb || (Size >> 20) < (size_t)Options.RssLimitMb) + return; + Printf("==%d== ERROR: libFuzzer: out-of-memory (malloc(%zd))\n", GetPid(), + Size); + Printf(" To change the out-of-memory limit use -rss_limit_mb=<N>\n\n"); + if (EF->__sanitizer_print_stack_trace) + EF->__sanitizer_print_stack_trace(); + DumpCurrentUnit("oom-"); + Printf("SUMMARY: libFuzzer: out-of-memory\n"); + PrintFinalStats(); + _Exit(Options.ErrorExitCode); // Stop right now. +} + +Fuzzer::Fuzzer(UserCallback CB, InputCorpus &Corpus, MutationDispatcher &MD, + FuzzingOptions Options) + : CB(CB), Corpus(Corpus), MD(MD), Options(Options) { + SetDeathCallback(); + InitializeTraceState(); + assert(!F); + F = this; + TPC.ResetMaps(); + ResetCoverage(); + IsMyThread = true; + if (Options.DetectLeaks && EF->__sanitizer_install_malloc_and_free_hooks) + EF->__sanitizer_install_malloc_and_free_hooks(MallocHook, FreeHook); + TPC.SetUseCounters(Options.UseCounters); + TPC.SetUseValueProfile(Options.UseValueProfile); + TPC.SetPrintNewPCs(Options.PrintNewCovPcs); + + if (Options.Verbosity) + TPC.PrintModuleInfo(); + if (!Options.OutputCorpus.empty() && Options.ReloadIntervalSec) + EpochOfLastReadOfOutputCorpus = GetEpoch(Options.OutputCorpus); + MaxInputLen = MaxMutationLen = Options.MaxLen; + AllocateCurrentUnitData(); + CurrentUnitSize = 0; + memset(BaseSha1, 0, sizeof(BaseSha1)); +} + +Fuzzer::~Fuzzer() { } + +void Fuzzer::AllocateCurrentUnitData() { + if (CurrentUnitData || MaxInputLen == 0) return; + CurrentUnitData = new uint8_t[MaxInputLen]; +} + +void Fuzzer::SetDeathCallback() { + CHECK_EXTERNAL_FUNCTION(__sanitizer_set_death_callback); + EF->__sanitizer_set_death_callback(StaticDeathCallback); +} + +void Fuzzer::StaticDeathCallback() { + assert(F); + F->DeathCallback(); +} + +static void WarnOnUnsuccessfullMerge(bool DoWarn) { + if (!DoWarn) return; + Printf( + "***\n" + "***\n" + "***\n" + "*** NOTE: merge did not succeed due to a failure on one of the inputs.\n" + "*** You will need to filter out crashes from the corpus, e.g. like this:\n" + "*** for f in WITH_CRASHES/*; do ./fuzzer $f && cp $f NO_CRASHES; done\n" + "*** Future versions may have crash-resistant merge, stay tuned.\n" + "***\n" + "***\n" + "***\n"); +} + +void Fuzzer::DumpCurrentUnit(const char *Prefix) { + WarnOnUnsuccessfullMerge(InMergeMode); + if (!CurrentUnitData) return; // Happens when running individual inputs. + MD.PrintMutationSequence(); + Printf("; base unit: %s\n", Sha1ToString(BaseSha1).c_str()); + size_t UnitSize = CurrentUnitSize; + if (UnitSize <= kMaxUnitSizeToPrint) { + PrintHexArray(CurrentUnitData, UnitSize, "\n"); + PrintASCII(CurrentUnitData, UnitSize, "\n"); + } + WriteUnitToFileWithPrefix({CurrentUnitData, CurrentUnitData + UnitSize}, + Prefix); +} + +NO_SANITIZE_MEMORY +void Fuzzer::DeathCallback() { + DumpCurrentUnit("crash-"); + PrintFinalStats(); +} + +void Fuzzer::StaticAlarmCallback() { + assert(F); + F->AlarmCallback(); +} + +void Fuzzer::StaticCrashSignalCallback() { + assert(F); + F->CrashCallback(); +} + +void Fuzzer::StaticInterruptCallback() { + assert(F); + F->InterruptCallback(); +} + +void Fuzzer::CrashCallback() { + Printf("==%lu== ERROR: libFuzzer: deadly signal\n", GetPid()); + if (EF->__sanitizer_print_stack_trace) + EF->__sanitizer_print_stack_trace(); + Printf("NOTE: libFuzzer has rudimentary signal handlers.\n" + " Combine libFuzzer with AddressSanitizer or similar for better " + "crash reports.\n"); + Printf("SUMMARY: libFuzzer: deadly signal\n"); + DumpCurrentUnit("crash-"); + PrintFinalStats(); + exit(Options.ErrorExitCode); +} + +void Fuzzer::InterruptCallback() { + Printf("==%lu== libFuzzer: run interrupted; exiting\n", GetPid()); + PrintFinalStats(); + _Exit(0); // Stop right now, don't perform any at-exit actions. +} + +NO_SANITIZE_MEMORY +void Fuzzer::AlarmCallback() { + assert(Options.UnitTimeoutSec > 0); + if (!InFuzzingThread()) return; + if (!RunningCB) + return; // We have not started running units yet. + size_t Seconds = + duration_cast<seconds>(system_clock::now() - UnitStartTime).count(); + if (Seconds == 0) + return; + if (Options.Verbosity >= 2) + Printf("AlarmCallback %zd\n", Seconds); + if (Seconds >= (size_t)Options.UnitTimeoutSec) { + Printf("ALARM: working on the last Unit for %zd seconds\n", Seconds); + Printf(" and the timeout value is %d (use -timeout=N to change)\n", + Options.UnitTimeoutSec); + DumpCurrentUnit("timeout-"); + Printf("==%lu== ERROR: libFuzzer: timeout after %d seconds\n", GetPid(), + Seconds); + if (EF->__sanitizer_print_stack_trace) + EF->__sanitizer_print_stack_trace(); + Printf("SUMMARY: libFuzzer: timeout\n"); + PrintFinalStats(); + _Exit(Options.TimeoutExitCode); // Stop right now. + } +} + +void Fuzzer::RssLimitCallback() { + Printf( + "==%lu== ERROR: libFuzzer: out-of-memory (used: %zdMb; limit: %zdMb)\n", + GetPid(), GetPeakRSSMb(), Options.RssLimitMb); + Printf(" To change the out-of-memory limit use -rss_limit_mb=<N>\n\n"); + if (EF->__sanitizer_print_memory_profile) + EF->__sanitizer_print_memory_profile(95); + DumpCurrentUnit("oom-"); + Printf("SUMMARY: libFuzzer: out-of-memory\n"); + PrintFinalStats(); + _Exit(Options.ErrorExitCode); // Stop right now. +} + +void Fuzzer::PrintStats(const char *Where, const char *End, size_t Units) { + size_t ExecPerSec = execPerSec(); + if (Options.OutputCSV) { + static bool csvHeaderPrinted = false; + if (!csvHeaderPrinted) { + csvHeaderPrinted = true; + Printf("runs,block_cov,bits,cc_cov,corpus,execs_per_sec,tbms,reason\n"); + } + Printf("%zd,%zd,%zd,%zd,%zd,%zd,%s\n", TotalNumberOfRuns, + MaxCoverage.BlockCoverage, MaxCoverage.CounterBitmapBits, + MaxCoverage.CallerCalleeCoverage, Corpus.size(), ExecPerSec, Where); + } + + if (!Options.Verbosity) + return; + Printf("#%zd\t%s", TotalNumberOfRuns, Where); + if (MaxCoverage.BlockCoverage) + Printf(" cov: %zd", MaxCoverage.BlockCoverage); + if (size_t N = MaxCoverage.VPMap.GetNumBitsSinceLastMerge()) + Printf(" vp: %zd", N); + if (size_t N = TPC.GetTotalPCCoverage()) + Printf(" cov: %zd", N); + if (auto TB = MaxCoverage.CounterBitmapBits) + Printf(" bits: %zd", TB); + if (size_t N = Corpus.NumFeatures()) + Printf( " ft: %zd", N); + if (MaxCoverage.CallerCalleeCoverage) + Printf(" indir: %zd", MaxCoverage.CallerCalleeCoverage); + if (!Corpus.empty()) { + Printf(" corp: %zd", Corpus.NumActiveUnits()); + if (size_t N = Corpus.SizeInBytes()) { + if (N < (1<<14)) + Printf("/%zdb", N); + else if (N < (1 << 24)) + Printf("/%zdKb", N >> 10); + else + Printf("/%zdMb", N >> 20); + } + } + if (Units) + Printf(" units: %zd", Units); + + Printf(" exec/s: %zd", ExecPerSec); + Printf(" rss: %zdMb", GetPeakRSSMb()); + Printf("%s", End); +} + +void Fuzzer::PrintFinalStats() { + if (Options.PrintCoverage) + TPC.PrintCoverage(); + if (Options.DumpCoverage) + TPC.DumpCoverage(); + if (Options.PrintCorpusStats) + Corpus.PrintStats(); + if (!Options.PrintFinalStats) return; + size_t ExecPerSec = execPerSec(); + Printf("stat::number_of_executed_units: %zd\n", TotalNumberOfRuns); + Printf("stat::average_exec_per_sec: %zd\n", ExecPerSec); + Printf("stat::new_units_added: %zd\n", NumberOfNewUnitsAdded); + Printf("stat::slowest_unit_time_sec: %zd\n", TimeOfLongestUnitInSeconds); + Printf("stat::peak_rss_mb: %zd\n", GetPeakRSSMb()); +} + +void Fuzzer::SetMaxInputLen(size_t MaxInputLen) { + assert(this->MaxInputLen == 0); // Can only reset MaxInputLen from 0 to non-0. + assert(MaxInputLen); + this->MaxInputLen = MaxInputLen; + this->MaxMutationLen = MaxInputLen; + AllocateCurrentUnitData(); + Printf("INFO: -max_len is not provided, using %zd\n", MaxInputLen); +} + +void Fuzzer::SetMaxMutationLen(size_t MaxMutationLen) { + assert(MaxMutationLen && MaxMutationLen <= MaxInputLen); + this->MaxMutationLen = MaxMutationLen; +} + +void Fuzzer::CheckExitOnSrcPosOrItem() { + if (!Options.ExitOnSrcPos.empty()) { + static auto *PCsSet = new std::set<uintptr_t>; + for (size_t i = 1, N = TPC.GetNumPCs(); i < N; i++) { + uintptr_t PC = TPC.GetPC(i); + if (!PC) continue; + if (!PCsSet->insert(PC).second) continue; + std::string Descr = DescribePC("%L", PC); + if (Descr.find(Options.ExitOnSrcPos) != std::string::npos) { + Printf("INFO: found line matching '%s', exiting.\n", + Options.ExitOnSrcPos.c_str()); + _Exit(0); + } + } + } + if (!Options.ExitOnItem.empty()) { + if (Corpus.HasUnit(Options.ExitOnItem)) { + Printf("INFO: found item with checksum '%s', exiting.\n", + Options.ExitOnItem.c_str()); + _Exit(0); + } + } +} + +void Fuzzer::RereadOutputCorpus(size_t MaxSize) { + if (Options.OutputCorpus.empty() || !Options.ReloadIntervalSec) return; + std::vector<Unit> AdditionalCorpus; + ReadDirToVectorOfUnits(Options.OutputCorpus.c_str(), &AdditionalCorpus, + &EpochOfLastReadOfOutputCorpus, MaxSize, + /*ExitOnError*/ false); + if (Options.Verbosity >= 2) + Printf("Reload: read %zd new units.\n", AdditionalCorpus.size()); + bool Reloaded = false; + for (auto &U : AdditionalCorpus) { + if (U.size() > MaxSize) + U.resize(MaxSize); + if (!Corpus.HasUnit(U)) { + if (size_t NumFeatures = RunOne(U)) { + CheckExitOnSrcPosOrItem(); + Corpus.AddToCorpus(U, NumFeatures); + Reloaded = true; + } + } + } + if (Reloaded) + PrintStats("RELOAD"); +} + +void Fuzzer::ShuffleCorpus(UnitVector *V) { + std::random_shuffle(V->begin(), V->end(), MD.GetRand()); + if (Options.PreferSmall) + std::stable_sort(V->begin(), V->end(), [](const Unit &A, const Unit &B) { + return A.size() < B.size(); + }); +} + +void Fuzzer::ShuffleAndMinimize(UnitVector *InitialCorpus) { + Printf("#0\tREAD units: %zd\n", InitialCorpus->size()); + if (Options.ShuffleAtStartUp) + ShuffleCorpus(InitialCorpus); + + // Test the callback with empty input and never try it again. + uint8_t dummy; + ExecuteCallback(&dummy, 0); + + for (const auto &U : *InitialCorpus) { + if (size_t NumFeatures = RunOne(U)) { + CheckExitOnSrcPosOrItem(); + Corpus.AddToCorpus(U, NumFeatures); + if (Options.Verbosity >= 2) + Printf("NEW0: %zd L %zd\n", MaxCoverage.BlockCoverage, U.size()); + } + TryDetectingAMemoryLeak(U.data(), U.size(), + /*DuringInitialCorpusExecution*/ true); + } + PrintStats("INITED"); + if (Corpus.empty()) { + Printf("ERROR: no interesting inputs were found. " + "Is the code instrumented for coverage? Exiting.\n"); + exit(1); + } +} + +size_t Fuzzer::RunOne(const uint8_t *Data, size_t Size) { + if (!Size) return 0; + TotalNumberOfRuns++; + + ExecuteCallback(Data, Size); + + size_t Res = 0; + if (size_t NumFeatures = TPC.CollectFeatures([&](size_t Feature) -> bool { + return Corpus.AddFeature(Feature, Size, Options.Shrink); + })) + Res = NumFeatures; + + if (!TPC.UsingTracePcGuard()) { + if (TPC.UpdateValueProfileMap(&MaxCoverage.VPMap)) + Res = 1; + if (!Res && RecordMaxCoverage(&MaxCoverage)) + Res = 1; + } + + auto TimeOfUnit = + duration_cast<seconds>(UnitStopTime - UnitStartTime).count(); + if (!(TotalNumberOfRuns & (TotalNumberOfRuns - 1)) && + secondsSinceProcessStartUp() >= 2) + PrintStats("pulse "); + if (TimeOfUnit > TimeOfLongestUnitInSeconds * 1.1 && + TimeOfUnit >= Options.ReportSlowUnits) { + TimeOfLongestUnitInSeconds = TimeOfUnit; + Printf("Slowest unit: %zd s:\n", TimeOfLongestUnitInSeconds); + WriteUnitToFileWithPrefix({Data, Data + Size}, "slow-unit-"); + } + return Res; +} + +size_t Fuzzer::GetCurrentUnitInFuzzingThead(const uint8_t **Data) const { + assert(InFuzzingThread()); + *Data = CurrentUnitData; + return CurrentUnitSize; +} + +void Fuzzer::ExecuteCallback(const uint8_t *Data, size_t Size) { + assert(InFuzzingThread()); + // We copy the contents of Unit into a separate heap buffer + // so that we reliably find buffer overflows in it. + uint8_t *DataCopy = new uint8_t[Size]; + memcpy(DataCopy, Data, Size); + if (CurrentUnitData && CurrentUnitData != Data) + memcpy(CurrentUnitData, Data, Size); + CurrentUnitSize = Size; + AllocTracer.Start(Options.TraceMalloc); + UnitStartTime = system_clock::now(); + ResetCounters(); // Reset coverage right before the callback. + TPC.ResetMaps(); + RunningCB = true; + int Res = CB(DataCopy, Size); + RunningCB = false; + UnitStopTime = system_clock::now(); + (void)Res; + assert(Res == 0); + HasMoreMallocsThanFrees = AllocTracer.Stop(); + CurrentUnitSize = 0; + delete[] DataCopy; +} + +void Fuzzer::WriteToOutputCorpus(const Unit &U) { + if (Options.OnlyASCII) + assert(IsASCII(U)); + if (Options.OutputCorpus.empty()) + return; + std::string Path = DirPlusFile(Options.OutputCorpus, Hash(U)); + WriteToFile(U, Path); + if (Options.Verbosity >= 2) + Printf("Written to %s\n", Path.c_str()); +} + +void Fuzzer::WriteUnitToFileWithPrefix(const Unit &U, const char *Prefix) { + if (!Options.SaveArtifacts) + return; + std::string Path = Options.ArtifactPrefix + Prefix + Hash(U); + if (!Options.ExactArtifactPath.empty()) + Path = Options.ExactArtifactPath; // Overrides ArtifactPrefix. + WriteToFile(U, Path); + Printf("artifact_prefix='%s'; Test unit written to %s\n", + Options.ArtifactPrefix.c_str(), Path.c_str()); + if (U.size() <= kMaxUnitSizeToPrint) + Printf("Base64: %s\n", Base64(U).c_str()); +} + +void Fuzzer::PrintStatusForNewUnit(const Unit &U) { + if (!Options.PrintNEW) + return; + PrintStats("NEW ", ""); + if (Options.Verbosity) { + Printf(" L: %zd ", U.size()); + MD.PrintMutationSequence(); + Printf("\n"); + } +} + +void Fuzzer::ReportNewCoverage(InputInfo *II, const Unit &U) { + II->NumSuccessfullMutations++; + MD.RecordSuccessfulMutationSequence(); + PrintStatusForNewUnit(U); + WriteToOutputCorpus(U); + NumberOfNewUnitsAdded++; + TPC.PrintNewPCs(); +} + +// Finds minimal number of units in 'Extra' that add coverage to 'Initial'. +// We do it by actually executing the units, sometimes more than once, +// because we may be using different coverage-like signals and the only +// common thing between them is that we can say "this unit found new stuff". +UnitVector Fuzzer::FindExtraUnits(const UnitVector &Initial, + const UnitVector &Extra) { + UnitVector Res = Extra; + UnitVector Tmp; + size_t OldSize = Res.size(); + for (int Iter = 0; Iter < 10; Iter++) { + ShuffleCorpus(&Res); + TPC.ResetMaps(); + Corpus.ResetFeatureSet(); + ResetCoverage(); + + for (auto &U : Initial) { + TPC.ResetMaps(); + RunOne(U); + } + + Tmp.clear(); + for (auto &U : Res) { + TPC.ResetMaps(); + if (RunOne(U)) + Tmp.push_back(U); + } + + char Stat[7] = "MIN "; + Stat[3] = '0' + Iter; + PrintStats(Stat, "\n", Tmp.size()); + + size_t NewSize = Tmp.size(); + assert(NewSize <= OldSize); + Res.swap(Tmp); + + if (NewSize + 5 >= OldSize) + break; + OldSize = NewSize; + } + return Res; +} + +void Fuzzer::Merge(const std::vector<std::string> &Corpora) { + if (Corpora.size() <= 1) { + Printf("Merge requires two or more corpus dirs\n"); + return; + } + InMergeMode = true; + std::vector<std::string> ExtraCorpora(Corpora.begin() + 1, Corpora.end()); + + assert(MaxInputLen > 0); + UnitVector Initial, Extra; + ReadDirToVectorOfUnits(Corpora[0].c_str(), &Initial, nullptr, MaxInputLen, + true); + for (auto &C : ExtraCorpora) + ReadDirToVectorOfUnits(C.c_str(), &Extra, nullptr, MaxInputLen, true); + + if (!Initial.empty()) { + Printf("=== Minimizing the initial corpus of %zd units\n", Initial.size()); + Initial = FindExtraUnits({}, Initial); + } + + Printf("=== Merging extra %zd units\n", Extra.size()); + auto Res = FindExtraUnits(Initial, Extra); + + for (auto &U: Res) + WriteToOutputCorpus(U); + + Printf("=== Merge: written %zd units\n", Res.size()); +} + +// Tries detecting a memory leak on the particular input that we have just +// executed before calling this function. +void Fuzzer::TryDetectingAMemoryLeak(const uint8_t *Data, size_t Size, + bool DuringInitialCorpusExecution) { + if (!HasMoreMallocsThanFrees) return; // mallocs==frees, a leak is unlikely. + if (!Options.DetectLeaks) return; + if (!&(EF->__lsan_enable) || !&(EF->__lsan_disable) || + !(EF->__lsan_do_recoverable_leak_check)) + return; // No lsan. + // Run the target once again, but with lsan disabled so that if there is + // a real leak we do not report it twice. + EF->__lsan_disable(); + ExecuteCallback(Data, Size); + EF->__lsan_enable(); + if (!HasMoreMallocsThanFrees) return; // a leak is unlikely. + if (NumberOfLeakDetectionAttempts++ > 1000) { + Options.DetectLeaks = false; + Printf("INFO: libFuzzer disabled leak detection after every mutation.\n" + " Most likely the target function accumulates allocated\n" + " memory in a global state w/o actually leaking it.\n" + " You may try running this binary with -trace_malloc=[12]" + " to get a trace of mallocs and frees.\n" + " If LeakSanitizer is enabled in this process it will still\n" + " run on the process shutdown.\n"); + return; + } + // Now perform the actual lsan pass. This is expensive and we must ensure + // we don't call it too often. + if (EF->__lsan_do_recoverable_leak_check()) { // Leak is found, report it. + if (DuringInitialCorpusExecution) + Printf("\nINFO: a leak has been found in the initial corpus.\n\n"); + Printf("INFO: to ignore leaks on libFuzzer side use -detect_leaks=0.\n\n"); + CurrentUnitSize = Size; + DumpCurrentUnit("leak-"); + PrintFinalStats(); + _Exit(Options.ErrorExitCode); // not exit() to disable lsan further on. + } +} + +void Fuzzer::MutateAndTestOne() { + MD.StartMutationSequence(); + + auto &II = Corpus.ChooseUnitToMutate(MD.GetRand()); + const auto &U = II.U; + memcpy(BaseSha1, II.Sha1, sizeof(BaseSha1)); + assert(CurrentUnitData); + size_t Size = U.size(); + assert(Size <= MaxInputLen && "Oversized Unit"); + memcpy(CurrentUnitData, U.data(), Size); + + assert(MaxMutationLen > 0); + + for (int i = 0; i < Options.MutateDepth; i++) { + if (TotalNumberOfRuns >= Options.MaxNumberOfRuns) + break; + size_t NewSize = 0; + NewSize = MD.Mutate(CurrentUnitData, Size, MaxMutationLen); + assert(NewSize > 0 && "Mutator returned empty unit"); + assert(NewSize <= MaxMutationLen && "Mutator return overisized unit"); + Size = NewSize; + if (i == 0) + StartTraceRecording(); + II.NumExecutedMutations++; + if (size_t NumFeatures = RunOne(CurrentUnitData, Size)) { + Corpus.AddToCorpus({CurrentUnitData, CurrentUnitData + Size}, NumFeatures, + /*MayDeleteFile=*/true); + ReportNewCoverage(&II, {CurrentUnitData, CurrentUnitData + Size}); + CheckExitOnSrcPosOrItem(); + } + StopTraceRecording(); + TryDetectingAMemoryLeak(CurrentUnitData, Size, + /*DuringInitialCorpusExecution*/ false); + } +} + +void Fuzzer::ResetCoverage() { + ResetEdgeCoverage(); + MaxCoverage.Reset(); + PrepareCounters(&MaxCoverage); +} + +void Fuzzer::Loop() { + system_clock::time_point LastCorpusReload = system_clock::now(); + if (Options.DoCrossOver) + MD.SetCorpus(&Corpus); + while (true) { + auto Now = system_clock::now(); + if (duration_cast<seconds>(Now - LastCorpusReload).count() >= + Options.ReloadIntervalSec) { + RereadOutputCorpus(MaxInputLen); + LastCorpusReload = system_clock::now(); + } + if (TotalNumberOfRuns >= Options.MaxNumberOfRuns) + break; + if (TimedOut()) break; + // Perform several mutations and runs. + MutateAndTestOne(); + } + + PrintStats("DONE ", "\n"); + MD.PrintRecommendedDictionary(); +} + +void Fuzzer::MinimizeCrashLoop(const Unit &U) { + if (U.size() <= 2) return; + while (!TimedOut() && TotalNumberOfRuns < Options.MaxNumberOfRuns) { + MD.StartMutationSequence(); + memcpy(CurrentUnitData, U.data(), U.size()); + for (int i = 0; i < Options.MutateDepth; i++) { + size_t NewSize = MD.Mutate(CurrentUnitData, U.size(), MaxMutationLen); + assert(NewSize > 0 && NewSize <= MaxMutationLen); + RunOne(CurrentUnitData, NewSize); + TryDetectingAMemoryLeak(CurrentUnitData, NewSize, + /*DuringInitialCorpusExecution*/ false); + } + } +} + +} // namespace fuzzer + +extern "C" { + +size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize) { + assert(fuzzer::F); + return fuzzer::F->GetMD().DefaultMutate(Data, Size, MaxSize); +} +} // extern "C" |
