Path: blob/main/contrib/llvm-project/compiler-rt/lib/fuzzer/FuzzerLoop.cpp
35262 views
//===- FuzzerLoop.cpp - Fuzzer's main loop --------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7// Fuzzer's main loop.8//===----------------------------------------------------------------------===//910#include "FuzzerCorpus.h"11#include "FuzzerIO.h"12#include "FuzzerInternal.h"13#include "FuzzerMutate.h"14#include "FuzzerPlatform.h"15#include "FuzzerRandom.h"16#include "FuzzerTracePC.h"17#include <algorithm>18#include <cstring>19#include <memory>20#include <mutex>21#include <set>2223#if defined(__has_include)24#if __has_include(<sanitizer / lsan_interface.h>)25#include <sanitizer/lsan_interface.h>26#endif27#endif2829#define NO_SANITIZE_MEMORY30#if defined(__has_feature)31#if __has_feature(memory_sanitizer)32#undef NO_SANITIZE_MEMORY33#define NO_SANITIZE_MEMORY __attribute__((no_sanitize_memory))34#endif35#endif3637namespace fuzzer {38static const size_t kMaxUnitSizeToPrint = 256;3940thread_local bool Fuzzer::IsMyThread;4142bool RunningUserCallback = false;4344// Only one Fuzzer per process.45static Fuzzer *F;4647// Leak detection is expensive, so we first check if there were more mallocs48// than frees (using the sanitizer malloc hooks) and only then try to call lsan.49struct MallocFreeTracer {50void Start(int TraceLevel) {51this->TraceLevel = TraceLevel;52if (TraceLevel)53Printf("MallocFreeTracer: START\n");54Mallocs = 0;55Frees = 0;56}57// Returns true if there were more mallocs than frees.58bool Stop() {59if (TraceLevel)60Printf("MallocFreeTracer: STOP %zd %zd (%s)\n", Mallocs.load(),61Frees.load(), Mallocs == Frees ? "same" : "DIFFERENT");62bool Result = Mallocs > Frees;63Mallocs = 0;64Frees = 0;65TraceLevel = 0;66return Result;67}68std::atomic<size_t> Mallocs;69std::atomic<size_t> Frees;70int TraceLevel = 0;7172std::recursive_mutex TraceMutex;73bool TraceDisabled = false;74};7576static MallocFreeTracer AllocTracer;7778// Locks printing and avoids nested hooks triggered from mallocs/frees in79// sanitizer.80class TraceLock {81public:82TraceLock() : Lock(AllocTracer.TraceMutex) {83AllocTracer.TraceDisabled = !AllocTracer.TraceDisabled;84}85~TraceLock() { AllocTracer.TraceDisabled = !AllocTracer.TraceDisabled; }8687bool IsDisabled() const {88// This is already inverted value.89return !AllocTracer.TraceDisabled;90}9192private:93std::lock_guard<std::recursive_mutex> Lock;94};9596ATTRIBUTE_NO_SANITIZE_MEMORY97void MallocHook(const volatile void *ptr, size_t size) {98size_t N = AllocTracer.Mallocs++;99F->HandleMalloc(size);100if (int TraceLevel = AllocTracer.TraceLevel) {101TraceLock Lock;102if (Lock.IsDisabled())103return;104Printf("MALLOC[%zd] %p %zd\n", N, ptr, size);105if (TraceLevel >= 2 && EF)106PrintStackTrace();107}108}109110ATTRIBUTE_NO_SANITIZE_MEMORY111void FreeHook(const volatile void *ptr) {112size_t N = AllocTracer.Frees++;113if (int TraceLevel = AllocTracer.TraceLevel) {114TraceLock Lock;115if (Lock.IsDisabled())116return;117Printf("FREE[%zd] %p\n", N, ptr);118if (TraceLevel >= 2 && EF)119PrintStackTrace();120}121}122123// Crash on a single malloc that exceeds the rss limit.124void Fuzzer::HandleMalloc(size_t Size) {125if (!Options.MallocLimitMb || (Size >> 20) < (size_t)Options.MallocLimitMb)126return;127Printf("==%d== ERROR: libFuzzer: out-of-memory (malloc(%zd))\n", GetPid(),128Size);129Printf(" To change the out-of-memory limit use -rss_limit_mb=<N>\n\n");130PrintStackTrace();131DumpCurrentUnit("oom-");132Printf("SUMMARY: libFuzzer: out-of-memory\n");133PrintFinalStats();134_Exit(Options.OOMExitCode); // Stop right now.135}136137Fuzzer::Fuzzer(UserCallback CB, InputCorpus &Corpus, MutationDispatcher &MD,138const FuzzingOptions &Options)139: CB(CB), Corpus(Corpus), MD(MD), Options(Options) {140if (EF->__sanitizer_set_death_callback)141EF->__sanitizer_set_death_callback(StaticDeathCallback);142assert(!F);143F = this;144TPC.ResetMaps();145IsMyThread = true;146if (Options.DetectLeaks && EF->__sanitizer_install_malloc_and_free_hooks)147EF->__sanitizer_install_malloc_and_free_hooks(MallocHook, FreeHook);148TPC.SetUseCounters(Options.UseCounters);149TPC.SetUseValueProfileMask(Options.UseValueProfile);150151if (Options.Verbosity)152TPC.PrintModuleInfo();153if (!Options.OutputCorpus.empty() && Options.ReloadIntervalSec)154EpochOfLastReadOfOutputCorpus = GetEpoch(Options.OutputCorpus);155MaxInputLen = MaxMutationLen = Options.MaxLen;156TmpMaxMutationLen = 0; // Will be set once we load the corpus.157AllocateCurrentUnitData();158CurrentUnitSize = 0;159memset(BaseSha1, 0, sizeof(BaseSha1));160}161162void Fuzzer::AllocateCurrentUnitData() {163if (CurrentUnitData || MaxInputLen == 0)164return;165CurrentUnitData = new uint8_t[MaxInputLen];166}167168void Fuzzer::StaticDeathCallback() {169assert(F);170F->DeathCallback();171}172173void Fuzzer::DumpCurrentUnit(const char *Prefix) {174if (!CurrentUnitData)175return; // Happens when running individual inputs.176ScopedDisableMsanInterceptorChecks S;177MD.PrintMutationSequence();178Printf("; base unit: %s\n", Sha1ToString(BaseSha1).c_str());179size_t UnitSize = CurrentUnitSize;180if (UnitSize <= kMaxUnitSizeToPrint) {181PrintHexArray(CurrentUnitData, UnitSize, "\n");182PrintASCII(CurrentUnitData, UnitSize, "\n");183}184WriteUnitToFileWithPrefix({CurrentUnitData, CurrentUnitData + UnitSize},185Prefix);186}187188NO_SANITIZE_MEMORY189void Fuzzer::DeathCallback() {190DumpCurrentUnit("crash-");191PrintFinalStats();192}193194void Fuzzer::StaticAlarmCallback() {195assert(F);196F->AlarmCallback();197}198199void Fuzzer::StaticCrashSignalCallback() {200assert(F);201F->CrashCallback();202}203204void Fuzzer::StaticExitCallback() {205assert(F);206F->ExitCallback();207}208209void Fuzzer::StaticInterruptCallback() {210assert(F);211F->InterruptCallback();212}213214void Fuzzer::StaticGracefulExitCallback() {215assert(F);216F->GracefulExitRequested = true;217Printf("INFO: signal received, trying to exit gracefully\n");218}219220void Fuzzer::StaticFileSizeExceedCallback() {221Printf("==%lu== ERROR: libFuzzer: file size exceeded\n", GetPid());222exit(1);223}224225void Fuzzer::CrashCallback() {226if (EF->__sanitizer_acquire_crash_state &&227!EF->__sanitizer_acquire_crash_state())228return;229Printf("==%lu== ERROR: libFuzzer: deadly signal\n", GetPid());230PrintStackTrace();231Printf("NOTE: libFuzzer has rudimentary signal handlers.\n"232" Combine libFuzzer with AddressSanitizer or similar for better "233"crash reports.\n");234Printf("SUMMARY: libFuzzer: deadly signal\n");235DumpCurrentUnit("crash-");236PrintFinalStats();237_Exit(Options.ErrorExitCode); // Stop right now.238}239240void Fuzzer::ExitCallback() {241if (!RunningUserCallback)242return; // This exit did not come from the user callback243if (EF->__sanitizer_acquire_crash_state &&244!EF->__sanitizer_acquire_crash_state())245return;246Printf("==%lu== ERROR: libFuzzer: fuzz target exited\n", GetPid());247PrintStackTrace();248Printf("SUMMARY: libFuzzer: fuzz target exited\n");249DumpCurrentUnit("crash-");250PrintFinalStats();251_Exit(Options.ErrorExitCode);252}253254void Fuzzer::MaybeExitGracefully() {255if (!F->GracefulExitRequested) return;256Printf("==%lu== INFO: libFuzzer: exiting as requested\n", GetPid());257RmDirRecursive(TempPath("FuzzWithFork", ".dir"));258F->PrintFinalStats();259_Exit(0);260}261262int Fuzzer::InterruptExitCode() {263assert(F);264return F->Options.InterruptExitCode;265}266267void Fuzzer::InterruptCallback() {268Printf("==%lu== libFuzzer: run interrupted; exiting\n", GetPid());269PrintFinalStats();270ScopedDisableMsanInterceptorChecks S; // RmDirRecursive may call opendir().271RmDirRecursive(TempPath("FuzzWithFork", ".dir"));272// Stop right now, don't perform any at-exit actions.273_Exit(Options.InterruptExitCode);274}275276NO_SANITIZE_MEMORY277void Fuzzer::AlarmCallback() {278assert(Options.UnitTimeoutSec > 0);279// In Windows and Fuchsia, Alarm callback is executed by a different thread.280// NetBSD's current behavior needs this change too.281#if !LIBFUZZER_WINDOWS && !LIBFUZZER_NETBSD && !LIBFUZZER_FUCHSIA282if (!InFuzzingThread())283return;284#endif285if (!RunningUserCallback)286return; // We have not started running units yet.287size_t Seconds =288duration_cast<seconds>(system_clock::now() - UnitStartTime).count();289if (Seconds == 0)290return;291if (Options.Verbosity >= 2)292Printf("AlarmCallback %zd\n", Seconds);293if (Seconds >= (size_t)Options.UnitTimeoutSec) {294if (EF->__sanitizer_acquire_crash_state &&295!EF->__sanitizer_acquire_crash_state())296return;297Printf("ALARM: working on the last Unit for %zd seconds\n", Seconds);298Printf(" and the timeout value is %d (use -timeout=N to change)\n",299Options.UnitTimeoutSec);300DumpCurrentUnit("timeout-");301Printf("==%lu== ERROR: libFuzzer: timeout after %zu seconds\n", GetPid(),302Seconds);303PrintStackTrace();304Printf("SUMMARY: libFuzzer: timeout\n");305PrintFinalStats();306_Exit(Options.TimeoutExitCode); // Stop right now.307}308}309310void Fuzzer::RssLimitCallback() {311if (EF->__sanitizer_acquire_crash_state &&312!EF->__sanitizer_acquire_crash_state())313return;314Printf("==%lu== ERROR: libFuzzer: out-of-memory (used: %zdMb; limit: %dMb)\n",315GetPid(), GetPeakRSSMb(), Options.RssLimitMb);316Printf(" To change the out-of-memory limit use -rss_limit_mb=<N>\n\n");317PrintMemoryProfile();318DumpCurrentUnit("oom-");319Printf("SUMMARY: libFuzzer: out-of-memory\n");320PrintFinalStats();321_Exit(Options.OOMExitCode); // Stop right now.322}323324void Fuzzer::PrintStats(const char *Where, const char *End, size_t Units,325size_t Features) {326size_t ExecPerSec = execPerSec();327if (!Options.Verbosity)328return;329Printf("#%zd\t%s", TotalNumberOfRuns, Where);330if (size_t N = TPC.GetTotalPCCoverage())331Printf(" cov: %zd", N);332if (size_t N = Features ? Features : Corpus.NumFeatures())333Printf(" ft: %zd", N);334if (!Corpus.empty()) {335Printf(" corp: %zd", Corpus.NumActiveUnits());336if (size_t N = Corpus.SizeInBytes()) {337if (N < (1 << 14))338Printf("/%zdb", N);339else if (N < (1 << 24))340Printf("/%zdKb", N >> 10);341else342Printf("/%zdMb", N >> 20);343}344if (size_t FF = Corpus.NumInputsThatTouchFocusFunction())345Printf(" focus: %zd", FF);346}347if (TmpMaxMutationLen)348Printf(" lim: %zd", TmpMaxMutationLen);349if (Units)350Printf(" units: %zd", Units);351352Printf(" exec/s: %zd", ExecPerSec);353Printf(" rss: %zdMb", GetPeakRSSMb());354Printf("%s", End);355}356357void Fuzzer::PrintFinalStats() {358if (Options.PrintFullCoverage)359TPC.PrintCoverage(/*PrintAllCounters=*/true);360if (Options.PrintCoverage)361TPC.PrintCoverage(/*PrintAllCounters=*/false);362if (Options.PrintCorpusStats)363Corpus.PrintStats();364if (!Options.PrintFinalStats)365return;366size_t ExecPerSec = execPerSec();367Printf("stat::number_of_executed_units: %zd\n", TotalNumberOfRuns);368Printf("stat::average_exec_per_sec: %zd\n", ExecPerSec);369Printf("stat::new_units_added: %zd\n", NumberOfNewUnitsAdded);370Printf("stat::slowest_unit_time_sec: %ld\n", TimeOfLongestUnitInSeconds);371Printf("stat::peak_rss_mb: %zd\n", GetPeakRSSMb());372}373374void Fuzzer::SetMaxInputLen(size_t MaxInputLen) {375assert(this->MaxInputLen == 0); // Can only reset MaxInputLen from 0 to non-0.376assert(MaxInputLen);377this->MaxInputLen = MaxInputLen;378this->MaxMutationLen = MaxInputLen;379AllocateCurrentUnitData();380Printf("INFO: -max_len is not provided; "381"libFuzzer will not generate inputs larger than %zd bytes\n",382MaxInputLen);383}384385void Fuzzer::SetMaxMutationLen(size_t MaxMutationLen) {386assert(MaxMutationLen && MaxMutationLen <= MaxInputLen);387this->MaxMutationLen = MaxMutationLen;388}389390void Fuzzer::CheckExitOnSrcPosOrItem() {391if (!Options.ExitOnSrcPos.empty()) {392static auto *PCsSet = new std::set<uintptr_t>;393auto HandlePC = [&](const TracePC::PCTableEntry *TE) {394if (!PCsSet->insert(TE->PC).second)395return;396std::string Descr = DescribePC("%F %L", TE->PC + 1);397if (Descr.find(Options.ExitOnSrcPos) != std::string::npos) {398Printf("INFO: found line matching '%s', exiting.\n",399Options.ExitOnSrcPos.c_str());400_Exit(0);401}402};403TPC.ForEachObservedPC(HandlePC);404}405if (!Options.ExitOnItem.empty()) {406if (Corpus.HasUnit(Options.ExitOnItem)) {407Printf("INFO: found item with checksum '%s', exiting.\n",408Options.ExitOnItem.c_str());409_Exit(0);410}411}412}413414void Fuzzer::RereadOutputCorpus(size_t MaxSize) {415if (Options.OutputCorpus.empty() || !Options.ReloadIntervalSec)416return;417std::vector<Unit> AdditionalCorpus;418std::vector<std::string> AdditionalCorpusPaths;419ReadDirToVectorOfUnits(420Options.OutputCorpus.c_str(), &AdditionalCorpus,421&EpochOfLastReadOfOutputCorpus, MaxSize,422/*ExitOnError*/ false,423(Options.Verbosity >= 2 ? &AdditionalCorpusPaths : nullptr));424if (Options.Verbosity >= 2)425Printf("Reload: read %zd new units.\n", AdditionalCorpus.size());426bool Reloaded = false;427for (size_t i = 0; i != AdditionalCorpus.size(); ++i) {428auto &U = AdditionalCorpus[i];429if (U.size() > MaxSize)430U.resize(MaxSize);431if (!Corpus.HasUnit(U)) {432if (RunOne(U.data(), U.size())) {433CheckExitOnSrcPosOrItem();434Reloaded = true;435if (Options.Verbosity >= 2)436Printf("Reloaded %s\n", AdditionalCorpusPaths[i].c_str());437}438}439}440if (Reloaded)441PrintStats("RELOAD");442}443444void Fuzzer::PrintPulseAndReportSlowInput(const uint8_t *Data, size_t Size) {445auto TimeOfUnit =446duration_cast<seconds>(UnitStopTime - UnitStartTime).count();447if (!(TotalNumberOfRuns & (TotalNumberOfRuns - 1)) &&448secondsSinceProcessStartUp() >= 2)449PrintStats("pulse ");450auto Threshhold =451static_cast<long>(static_cast<double>(TimeOfLongestUnitInSeconds) * 1.1);452if (TimeOfUnit > Threshhold && TimeOfUnit >= Options.ReportSlowUnits) {453TimeOfLongestUnitInSeconds = TimeOfUnit;454Printf("Slowest unit: %ld s:\n", TimeOfLongestUnitInSeconds);455WriteUnitToFileWithPrefix({Data, Data + Size}, "slow-unit-");456}457}458459static void WriteFeatureSetToFile(const std::string &FeaturesDir,460const std::string &FileName,461const std::vector<uint32_t> &FeatureSet) {462if (FeaturesDir.empty() || FeatureSet.empty()) return;463WriteToFile(reinterpret_cast<const uint8_t *>(FeatureSet.data()),464FeatureSet.size() * sizeof(FeatureSet[0]),465DirPlusFile(FeaturesDir, FileName));466}467468static void RenameFeatureSetFile(const std::string &FeaturesDir,469const std::string &OldFile,470const std::string &NewFile) {471if (FeaturesDir.empty()) return;472RenameFile(DirPlusFile(FeaturesDir, OldFile),473DirPlusFile(FeaturesDir, NewFile));474}475476static void WriteEdgeToMutationGraphFile(const std::string &MutationGraphFile,477const InputInfo *II,478const InputInfo *BaseII,479const std::string &MS) {480if (MutationGraphFile.empty())481return;482483std::string Sha1 = Sha1ToString(II->Sha1);484485std::string OutputString;486487// Add a new vertex.488OutputString.append("\"");489OutputString.append(Sha1);490OutputString.append("\"\n");491492// Add a new edge if there is base input.493if (BaseII) {494std::string BaseSha1 = Sha1ToString(BaseII->Sha1);495OutputString.append("\"");496OutputString.append(BaseSha1);497OutputString.append("\" -> \"");498OutputString.append(Sha1);499OutputString.append("\" [label=\"");500OutputString.append(MS);501OutputString.append("\"];\n");502}503504AppendToFile(OutputString, MutationGraphFile);505}506507bool Fuzzer::RunOne(const uint8_t *Data, size_t Size, bool MayDeleteFile,508InputInfo *II, bool ForceAddToCorpus,509bool *FoundUniqFeatures) {510if (!Size)511return false;512// Largest input length should be INT_MAX.513assert(Size < std::numeric_limits<uint32_t>::max());514515if(!ExecuteCallback(Data, Size)) return false;516auto TimeOfUnit = duration_cast<microseconds>(UnitStopTime - UnitStartTime);517518UniqFeatureSetTmp.clear();519size_t FoundUniqFeaturesOfII = 0;520size_t NumUpdatesBefore = Corpus.NumFeatureUpdates();521TPC.CollectFeatures([&](uint32_t Feature) {522if (Corpus.AddFeature(Feature, static_cast<uint32_t>(Size), Options.Shrink))523UniqFeatureSetTmp.push_back(Feature);524if (Options.Entropic)525Corpus.UpdateFeatureFrequency(II, Feature);526if (Options.ReduceInputs && II && !II->NeverReduce)527if (std::binary_search(II->UniqFeatureSet.begin(),528II->UniqFeatureSet.end(), Feature))529FoundUniqFeaturesOfII++;530});531if (FoundUniqFeatures)532*FoundUniqFeatures = FoundUniqFeaturesOfII;533PrintPulseAndReportSlowInput(Data, Size);534size_t NumNewFeatures = Corpus.NumFeatureUpdates() - NumUpdatesBefore;535if (NumNewFeatures || ForceAddToCorpus) {536TPC.UpdateObservedPCs();537auto NewII =538Corpus.AddToCorpus({Data, Data + Size}, NumNewFeatures, MayDeleteFile,539TPC.ObservedFocusFunction(), ForceAddToCorpus,540TimeOfUnit, UniqFeatureSetTmp, DFT, II);541WriteFeatureSetToFile(Options.FeaturesDir, Sha1ToString(NewII->Sha1),542NewII->UniqFeatureSet);543WriteEdgeToMutationGraphFile(Options.MutationGraphFile, NewII, II,544MD.MutationSequence());545return true;546}547if (II && FoundUniqFeaturesOfII &&548II->DataFlowTraceForFocusFunction.empty() &&549FoundUniqFeaturesOfII == II->UniqFeatureSet.size() &&550II->U.size() > Size) {551auto OldFeaturesFile = Sha1ToString(II->Sha1);552Corpus.Replace(II, {Data, Data + Size}, TimeOfUnit);553RenameFeatureSetFile(Options.FeaturesDir, OldFeaturesFile,554Sha1ToString(II->Sha1));555return true;556}557return false;558}559560void Fuzzer::TPCUpdateObservedPCs() { TPC.UpdateObservedPCs(); }561562size_t Fuzzer::GetCurrentUnitInFuzzingThead(const uint8_t **Data) const {563assert(InFuzzingThread());564*Data = CurrentUnitData;565return CurrentUnitSize;566}567568void Fuzzer::CrashOnOverwrittenData() {569Printf("==%d== ERROR: libFuzzer: fuzz target overwrites its const input\n",570GetPid());571PrintStackTrace();572Printf("SUMMARY: libFuzzer: overwrites-const-input\n");573DumpCurrentUnit("crash-");574PrintFinalStats();575_Exit(Options.ErrorExitCode); // Stop right now.576}577578// Compare two arrays, but not all bytes if the arrays are large.579static bool LooseMemeq(const uint8_t *A, const uint8_t *B, size_t Size) {580const size_t Limit = 64;581if (Size <= 64)582return !memcmp(A, B, Size);583// Compare first and last Limit/2 bytes.584return !memcmp(A, B, Limit / 2) &&585!memcmp(A + Size - Limit / 2, B + Size - Limit / 2, Limit / 2);586}587588// This method is not inlined because it would cause a test to fail where it589// is part of the stack unwinding. See D97975 for details.590ATTRIBUTE_NOINLINE bool Fuzzer::ExecuteCallback(const uint8_t *Data,591size_t Size) {592TPC.RecordInitialStack();593TotalNumberOfRuns++;594assert(InFuzzingThread());595// We copy the contents of Unit into a separate heap buffer596// so that we reliably find buffer overflows in it.597uint8_t *DataCopy = new uint8_t[Size];598memcpy(DataCopy, Data, Size);599if (EF->__msan_unpoison)600EF->__msan_unpoison(DataCopy, Size);601if (EF->__msan_unpoison_param)602EF->__msan_unpoison_param(2);603if (CurrentUnitData && CurrentUnitData != Data)604memcpy(CurrentUnitData, Data, Size);605CurrentUnitSize = Size;606int CBRes = 0;607{608ScopedEnableMsanInterceptorChecks S;609AllocTracer.Start(Options.TraceMalloc);610UnitStartTime = system_clock::now();611TPC.ResetMaps();612RunningUserCallback = true;613CBRes = CB(DataCopy, Size);614RunningUserCallback = false;615UnitStopTime = system_clock::now();616assert(CBRes == 0 || CBRes == -1);617HasMoreMallocsThanFrees = AllocTracer.Stop();618}619if (!LooseMemeq(DataCopy, Data, Size))620CrashOnOverwrittenData();621CurrentUnitSize = 0;622delete[] DataCopy;623return CBRes == 0;624}625626std::string Fuzzer::WriteToOutputCorpus(const Unit &U) {627if (Options.OnlyASCII)628assert(IsASCII(U));629if (Options.OutputCorpus.empty())630return "";631std::string Path = DirPlusFile(Options.OutputCorpus, Hash(U));632WriteToFile(U, Path);633if (Options.Verbosity >= 2)634Printf("Written %zd bytes to %s\n", U.size(), Path.c_str());635return Path;636}637638void Fuzzer::WriteUnitToFileWithPrefix(const Unit &U, const char *Prefix) {639if (!Options.SaveArtifacts)640return;641std::string Path = Options.ArtifactPrefix + Prefix + Hash(U);642if (!Options.ExactArtifactPath.empty())643Path = Options.ExactArtifactPath; // Overrides ArtifactPrefix.644WriteToFile(U, Path);645Printf("artifact_prefix='%s'; Test unit written to %s\n",646Options.ArtifactPrefix.c_str(), Path.c_str());647if (U.size() <= kMaxUnitSizeToPrint)648Printf("Base64: %s\n", Base64(U).c_str());649}650651void Fuzzer::PrintStatusForNewUnit(const Unit &U, const char *Text) {652if (!Options.PrintNEW)653return;654PrintStats(Text, "");655if (Options.Verbosity) {656Printf(" L: %zd/%zd ", U.size(), Corpus.MaxInputSize());657MD.PrintMutationSequence(Options.Verbosity >= 2);658Printf("\n");659}660}661662void Fuzzer::ReportNewCoverage(InputInfo *II, const Unit &U) {663II->NumSuccessfullMutations++;664MD.RecordSuccessfulMutationSequence();665PrintStatusForNewUnit(U, II->Reduced ? "REDUCE" : "NEW ");666WriteToOutputCorpus(U);667NumberOfNewUnitsAdded++;668CheckExitOnSrcPosOrItem(); // Check only after the unit is saved to corpus.669LastCorpusUpdateRun = TotalNumberOfRuns;670}671672// Tries detecting a memory leak on the particular input that we have just673// executed before calling this function.674void Fuzzer::TryDetectingAMemoryLeak(const uint8_t *Data, size_t Size,675bool DuringInitialCorpusExecution) {676if (!HasMoreMallocsThanFrees)677return; // mallocs==frees, a leak is unlikely.678if (!Options.DetectLeaks)679return;680if (!DuringInitialCorpusExecution &&681TotalNumberOfRuns >= Options.MaxNumberOfRuns)682return;683if (!&(EF->__lsan_enable) || !&(EF->__lsan_disable) ||684!(EF->__lsan_do_recoverable_leak_check))685return; // No lsan.686// Run the target once again, but with lsan disabled so that if there is687// a real leak we do not report it twice.688EF->__lsan_disable();689ExecuteCallback(Data, Size);690EF->__lsan_enable();691if (!HasMoreMallocsThanFrees)692return; // a leak is unlikely.693if (NumberOfLeakDetectionAttempts++ > 1000) {694Options.DetectLeaks = false;695Printf("INFO: libFuzzer disabled leak detection after every mutation.\n"696" Most likely the target function accumulates allocated\n"697" memory in a global state w/o actually leaking it.\n"698" You may try running this binary with -trace_malloc=[12]"699" to get a trace of mallocs and frees.\n"700" If LeakSanitizer is enabled in this process it will still\n"701" run on the process shutdown.\n");702return;703}704// Now perform the actual lsan pass. This is expensive and we must ensure705// we don't call it too often.706if (EF->__lsan_do_recoverable_leak_check()) { // Leak is found, report it.707if (DuringInitialCorpusExecution)708Printf("\nINFO: a leak has been found in the initial corpus.\n\n");709Printf("INFO: to ignore leaks on libFuzzer side use -detect_leaks=0.\n\n");710CurrentUnitSize = Size;711DumpCurrentUnit("leak-");712PrintFinalStats();713_Exit(Options.ErrorExitCode); // not exit() to disable lsan further on.714}715}716717void Fuzzer::MutateAndTestOne() {718MD.StartMutationSequence();719720auto &II = Corpus.ChooseUnitToMutate(MD.GetRand());721if (Options.DoCrossOver) {722auto &CrossOverII = Corpus.ChooseUnitToCrossOverWith(723MD.GetRand(), Options.CrossOverUniformDist);724MD.SetCrossOverWith(&CrossOverII.U);725}726const auto &U = II.U;727memcpy(BaseSha1, II.Sha1, sizeof(BaseSha1));728assert(CurrentUnitData);729size_t Size = U.size();730assert(Size <= MaxInputLen && "Oversized Unit");731memcpy(CurrentUnitData, U.data(), Size);732733assert(MaxMutationLen > 0);734735size_t CurrentMaxMutationLen =736Min(MaxMutationLen, Max(U.size(), TmpMaxMutationLen));737assert(CurrentMaxMutationLen > 0);738739for (int i = 0; i < Options.MutateDepth; i++) {740if (TotalNumberOfRuns >= Options.MaxNumberOfRuns)741break;742MaybeExitGracefully();743size_t NewSize = 0;744if (II.HasFocusFunction && !II.DataFlowTraceForFocusFunction.empty() &&745Size <= CurrentMaxMutationLen)746NewSize = MD.MutateWithMask(CurrentUnitData, Size, Size,747II.DataFlowTraceForFocusFunction);748749// If MutateWithMask either failed or wasn't called, call default Mutate.750if (!NewSize)751NewSize = MD.Mutate(CurrentUnitData, Size, CurrentMaxMutationLen);752assert(NewSize > 0 && "Mutator returned empty unit");753assert(NewSize <= CurrentMaxMutationLen && "Mutator return oversized unit");754Size = NewSize;755II.NumExecutedMutations++;756Corpus.IncrementNumExecutedMutations();757758bool FoundUniqFeatures = false;759bool NewCov = RunOne(CurrentUnitData, Size, /*MayDeleteFile=*/true, &II,760/*ForceAddToCorpus*/ false, &FoundUniqFeatures);761TryDetectingAMemoryLeak(CurrentUnitData, Size,762/*DuringInitialCorpusExecution*/ false);763if (NewCov) {764ReportNewCoverage(&II, {CurrentUnitData, CurrentUnitData + Size});765break; // We will mutate this input more in the next rounds.766}767if (Options.ReduceDepth && !FoundUniqFeatures)768break;769}770771II.NeedsEnergyUpdate = true;772}773774void Fuzzer::PurgeAllocator() {775if (Options.PurgeAllocatorIntervalSec < 0 || !EF->__sanitizer_purge_allocator)776return;777if (duration_cast<seconds>(system_clock::now() -778LastAllocatorPurgeAttemptTime)779.count() < Options.PurgeAllocatorIntervalSec)780return;781782if (Options.RssLimitMb <= 0 ||783GetPeakRSSMb() > static_cast<size_t>(Options.RssLimitMb) / 2)784EF->__sanitizer_purge_allocator();785786LastAllocatorPurgeAttemptTime = system_clock::now();787}788789void Fuzzer::ReadAndExecuteSeedCorpora(std::vector<SizedFile> &CorporaFiles) {790const size_t kMaxSaneLen = 1 << 20;791const size_t kMinDefaultLen = 4096;792size_t MaxSize = 0;793size_t MinSize = -1;794size_t TotalSize = 0;795for (auto &File : CorporaFiles) {796MaxSize = Max(File.Size, MaxSize);797MinSize = Min(File.Size, MinSize);798TotalSize += File.Size;799}800if (Options.MaxLen == 0)801SetMaxInputLen(std::clamp(MaxSize, kMinDefaultLen, kMaxSaneLen));802assert(MaxInputLen > 0);803804// Test the callback with empty input and never try it again.805uint8_t dummy = 0;806ExecuteCallback(&dummy, 0);807808if (CorporaFiles.empty()) {809Printf("INFO: A corpus is not provided, starting from an empty corpus\n");810Unit U({'\n'}); // Valid ASCII input.811RunOne(U.data(), U.size());812} else {813Printf("INFO: seed corpus: files: %zd min: %zdb max: %zdb total: %zdb"814" rss: %zdMb\n",815CorporaFiles.size(), MinSize, MaxSize, TotalSize, GetPeakRSSMb());816if (Options.ShuffleAtStartUp)817std::shuffle(CorporaFiles.begin(), CorporaFiles.end(), MD.GetRand());818819if (Options.PreferSmall) {820std::stable_sort(CorporaFiles.begin(), CorporaFiles.end());821assert(CorporaFiles.front().Size <= CorporaFiles.back().Size);822}823824// Load and execute inputs one by one.825for (auto &SF : CorporaFiles) {826auto U = FileToVector(SF.File, MaxInputLen, /*ExitOnError=*/false);827assert(U.size() <= MaxInputLen);828RunOne(U.data(), U.size(), /*MayDeleteFile*/ false, /*II*/ nullptr,829/*ForceAddToCorpus*/ Options.KeepSeed,830/*FoundUniqFeatures*/ nullptr);831CheckExitOnSrcPosOrItem();832TryDetectingAMemoryLeak(U.data(), U.size(),833/*DuringInitialCorpusExecution*/ true);834}835}836837PrintStats("INITED");838if (!Options.FocusFunction.empty()) {839Printf("INFO: %zd/%zd inputs touch the focus function\n",840Corpus.NumInputsThatTouchFocusFunction(), Corpus.size());841if (!Options.DataFlowTrace.empty())842Printf("INFO: %zd/%zd inputs have the Data Flow Trace\n",843Corpus.NumInputsWithDataFlowTrace(),844Corpus.NumInputsThatTouchFocusFunction());845}846847if (Corpus.empty() && Options.MaxNumberOfRuns) {848Printf("WARNING: no interesting inputs were found so far. "849"Is the code instrumented for coverage?\n"850"This may also happen if the target rejected all inputs we tried so "851"far\n");852// The remaining logic requires that the corpus is not empty,853// so we add one fake input to the in-memory corpus.854Corpus.AddToCorpus({'\n'}, /*NumFeatures=*/1, /*MayDeleteFile=*/true,855/*HasFocusFunction=*/false, /*NeverReduce=*/false,856/*TimeOfUnit=*/duration_cast<microseconds>(0s), {0}, DFT,857/*BaseII*/ nullptr);858}859}860861void Fuzzer::Loop(std::vector<SizedFile> &CorporaFiles) {862auto FocusFunctionOrAuto = Options.FocusFunction;863DFT.Init(Options.DataFlowTrace, &FocusFunctionOrAuto, CorporaFiles,864MD.GetRand());865TPC.SetFocusFunction(FocusFunctionOrAuto);866ReadAndExecuteSeedCorpora(CorporaFiles);867DFT.Clear(); // No need for DFT any more.868TPC.SetPrintNewPCs(Options.PrintNewCovPcs);869TPC.SetPrintNewFuncs(Options.PrintNewCovFuncs);870system_clock::time_point LastCorpusReload = system_clock::now();871872TmpMaxMutationLen =873Min(MaxMutationLen, Max(size_t(4), Corpus.MaxInputSize()));874875while (true) {876auto Now = system_clock::now();877if (!Options.StopFile.empty() &&878!FileToVector(Options.StopFile, 1, false).empty())879break;880if (duration_cast<seconds>(Now - LastCorpusReload).count() >=881Options.ReloadIntervalSec) {882RereadOutputCorpus(MaxInputLen);883LastCorpusReload = system_clock::now();884}885if (TotalNumberOfRuns >= Options.MaxNumberOfRuns)886break;887if (TimedOut())888break;889890// Update TmpMaxMutationLen891if (Options.LenControl) {892if (TmpMaxMutationLen < MaxMutationLen &&893TotalNumberOfRuns - LastCorpusUpdateRun >894Options.LenControl * Log(TmpMaxMutationLen)) {895TmpMaxMutationLen =896Min(MaxMutationLen, TmpMaxMutationLen + Log(TmpMaxMutationLen));897LastCorpusUpdateRun = TotalNumberOfRuns;898}899} else {900TmpMaxMutationLen = MaxMutationLen;901}902903// Perform several mutations and runs.904MutateAndTestOne();905906PurgeAllocator();907}908909PrintStats("DONE ", "\n");910MD.PrintRecommendedDictionary();911}912913void Fuzzer::MinimizeCrashLoop(const Unit &U) {914if (U.size() <= 1)915return;916while (!TimedOut() && TotalNumberOfRuns < Options.MaxNumberOfRuns) {917MD.StartMutationSequence();918memcpy(CurrentUnitData, U.data(), U.size());919for (int i = 0; i < Options.MutateDepth; i++) {920size_t NewSize = MD.Mutate(CurrentUnitData, U.size(), MaxMutationLen);921assert(NewSize > 0 && NewSize <= MaxMutationLen);922ExecuteCallback(CurrentUnitData, NewSize);923PrintPulseAndReportSlowInput(CurrentUnitData, NewSize);924TryDetectingAMemoryLeak(CurrentUnitData, NewSize,925/*DuringInitialCorpusExecution*/ false);926}927}928}929930} // namespace fuzzer931932extern "C" {933934ATTRIBUTE_INTERFACE size_t935LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize) {936assert(fuzzer::F);937return fuzzer::F->GetMD().DefaultMutate(Data, Size, MaxSize);938}939940} // extern "C"941942943