947 lines · cpp
1//===- FuzzerLoop.cpp - Fuzzer's main loop --------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8// Fuzzer's main loop.9//===----------------------------------------------------------------------===//10 11#include "FuzzerCorpus.h"12#include "FuzzerIO.h"13#include "FuzzerInternal.h"14#include "FuzzerMutate.h"15#include "FuzzerPlatform.h"16#include "FuzzerRandom.h"17#include "FuzzerTracePC.h"18#include <algorithm>19#include <cstring>20#include <memory>21#include <mutex>22#include <set>23 24#if defined(__has_include)25#if __has_include(<sanitizer / lsan_interface.h>)26#include <sanitizer/lsan_interface.h>27#endif28#endif29 30#define NO_SANITIZE_MEMORY31#if defined(__has_feature)32#if __has_feature(memory_sanitizer)33#undef NO_SANITIZE_MEMORY34#define NO_SANITIZE_MEMORY __attribute__((no_sanitize_memory))35#endif36#endif37 38namespace fuzzer {39static const size_t kMaxUnitSizeToPrint = 256;40 41thread_local bool Fuzzer::IsMyThread;42 43bool RunningUserCallback = false;44 45// Only one Fuzzer per process.46static Fuzzer *F;47 48// Leak detection is expensive, so we first check if there were more mallocs49// than frees (using the sanitizer malloc hooks) and only then try to call lsan.50struct MallocFreeTracer {51 void Start(int TraceLevel) {52 this->TraceLevel = TraceLevel;53 if (TraceLevel)54 Printf("MallocFreeTracer: START\n");55 Mallocs = 0;56 Frees = 0;57 }58 // Returns true if there were more mallocs than frees.59 bool Stop() {60 if (TraceLevel)61 Printf("MallocFreeTracer: STOP %zd %zd (%s)\n", Mallocs.load(),62 Frees.load(), Mallocs == Frees ? "same" : "DIFFERENT");63 bool Result = Mallocs > Frees;64 Mallocs = 0;65 Frees = 0;66 TraceLevel = 0;67 return Result;68 }69 std::atomic<size_t> Mallocs;70 std::atomic<size_t> Frees;71 int TraceLevel = 0;72 73 std::recursive_mutex TraceMutex;74 bool TraceDisabled = false;75};76 77static MallocFreeTracer AllocTracer;78 79// Locks printing and avoids nested hooks triggered from mallocs/frees in80// sanitizer.81class TraceLock {82public:83 TraceLock() : Lock(AllocTracer.TraceMutex) {84 AllocTracer.TraceDisabled = !AllocTracer.TraceDisabled;85 }86 ~TraceLock() { AllocTracer.TraceDisabled = !AllocTracer.TraceDisabled; }87 88 bool IsDisabled() const {89 // This is already inverted value.90 return !AllocTracer.TraceDisabled;91 }92 93private:94 std::lock_guard<std::recursive_mutex> Lock;95};96 97ATTRIBUTE_NO_SANITIZE_MEMORY98void MallocHook(const volatile void *ptr, size_t size) {99 size_t N = AllocTracer.Mallocs++;100 F->HandleMalloc(size);101 if (int TraceLevel = AllocTracer.TraceLevel) {102 TraceLock Lock;103 if (Lock.IsDisabled())104 return;105 Printf("MALLOC[%zd] %p %zd\n", N, ptr, size);106 if (TraceLevel >= 2 && EF)107 PrintStackTrace();108 }109}110 111ATTRIBUTE_NO_SANITIZE_MEMORY112void FreeHook(const volatile void *ptr) {113 size_t N = AllocTracer.Frees++;114 if (int TraceLevel = AllocTracer.TraceLevel) {115 TraceLock Lock;116 if (Lock.IsDisabled())117 return;118 Printf("FREE[%zd] %p\n", N, ptr);119 if (TraceLevel >= 2 && EF)120 PrintStackTrace();121 }122}123 124// Crash on a single malloc that exceeds the rss limit.125void Fuzzer::HandleMalloc(size_t Size) {126 if (!Options.MallocLimitMb || (Size >> 20) < (size_t)Options.MallocLimitMb)127 return;128 Printf("==%d== ERROR: libFuzzer: out-of-memory (malloc(%zd))\n",129 (int)GetPid(), Size);130 Printf(" To change the out-of-memory limit use -rss_limit_mb=<N>\n\n");131 PrintStackTrace();132 DumpCurrentUnit("oom-");133 Printf("SUMMARY: libFuzzer: out-of-memory\n");134 PrintFinalStats();135 _Exit(Options.OOMExitCode); // Stop right now.136}137 138Fuzzer::Fuzzer(UserCallback CB, InputCorpus &Corpus, MutationDispatcher &MD,139 const FuzzingOptions &Options)140 : CB(CB), Corpus(Corpus), MD(MD), Options(Options) {141 if (EF->__sanitizer_set_death_callback)142 EF->__sanitizer_set_death_callback(StaticDeathCallback);143 assert(!F);144 F = this;145 TPC.ResetMaps();146 IsMyThread = true;147 if (Options.DetectLeaks && EF->__sanitizer_install_malloc_and_free_hooks)148 EF->__sanitizer_install_malloc_and_free_hooks(MallocHook, FreeHook);149 TPC.SetUseCounters(Options.UseCounters);150 TPC.SetUseValueProfileMask(Options.UseValueProfile);151 152 if (Options.Verbosity)153 TPC.PrintModuleInfo();154 if (!Options.OutputCorpus.empty() && Options.ReloadIntervalSec)155 EpochOfLastReadOfOutputCorpus = GetEpoch(Options.OutputCorpus);156 MaxInputLen = MaxMutationLen = Options.MaxLen;157 TmpMaxMutationLen = 0; // Will be set once we load the corpus.158 AllocateCurrentUnitData();159 CurrentUnitSize = 0;160 memset(BaseSha1, 0, sizeof(BaseSha1));161}162 163void Fuzzer::AllocateCurrentUnitData() {164 if (CurrentUnitData || MaxInputLen == 0)165 return;166 CurrentUnitData = new uint8_t[MaxInputLen];167}168 169void Fuzzer::StaticDeathCallback() {170 assert(F);171 F->DeathCallback();172}173 174void Fuzzer::DumpCurrentUnit(const char *Prefix) {175 if (!CurrentUnitData)176 return; // Happens when running individual inputs.177 ScopedDisableMsanInterceptorChecks S;178 MD.PrintMutationSequence();179 Printf("; base unit: %s\n", Sha1ToString(BaseSha1).c_str());180 size_t UnitSize = CurrentUnitSize;181 if (UnitSize <= kMaxUnitSizeToPrint) {182 PrintHexArray(CurrentUnitData, UnitSize, "\n");183 PrintASCII(CurrentUnitData, UnitSize, "\n");184 }185 WriteUnitToFileWithPrefix({CurrentUnitData, CurrentUnitData + UnitSize},186 Prefix);187}188 189NO_SANITIZE_MEMORY190void Fuzzer::DeathCallback() {191 DumpCurrentUnit("crash-");192 PrintFinalStats();193}194 195void Fuzzer::StaticAlarmCallback() {196 assert(F);197 F->AlarmCallback();198}199 200void Fuzzer::StaticCrashSignalCallback() {201 assert(F);202 F->CrashCallback();203}204 205void Fuzzer::StaticExitCallback() {206 assert(F);207 F->ExitCallback();208}209 210void Fuzzer::StaticInterruptCallback() {211 assert(F);212 F->InterruptCallback();213}214 215void Fuzzer::StaticGracefulExitCallback() {216 assert(F);217 F->GracefulExitRequested = true;218 Printf("INFO: signal received, trying to exit gracefully\n");219}220 221void Fuzzer::StaticFileSizeExceedCallback() {222 Printf("==%lu== ERROR: libFuzzer: file size exceeded\n", GetPid());223 exit(1);224}225 226void Fuzzer::CrashCallback() {227 if (EF->__sanitizer_acquire_crash_state &&228 !EF->__sanitizer_acquire_crash_state())229 return;230 Printf("==%lu== ERROR: libFuzzer: deadly signal\n", GetPid());231 PrintStackTrace();232 Printf("NOTE: libFuzzer has rudimentary signal handlers.\n"233 " Combine libFuzzer with AddressSanitizer or similar for better "234 "crash reports.\n");235 Printf("SUMMARY: libFuzzer: deadly signal\n");236 DumpCurrentUnit("crash-");237 PrintFinalStats();238 _Exit(Options.ErrorExitCode); // Stop right now.239}240 241void Fuzzer::ExitCallback() {242 if (!RunningUserCallback)243 return; // This exit did not come from the user callback244 if (EF->__sanitizer_acquire_crash_state &&245 !EF->__sanitizer_acquire_crash_state())246 return;247 Printf("==%lu== ERROR: libFuzzer: fuzz target exited\n", GetPid());248 PrintStackTrace();249 Printf("SUMMARY: libFuzzer: fuzz target exited\n");250 DumpCurrentUnit("crash-");251 PrintFinalStats();252 _Exit(Options.ErrorExitCode);253}254 255void Fuzzer::MaybeExitGracefully() {256 if (!F->GracefulExitRequested) return;257 Printf("==%lu== INFO: libFuzzer: exiting as requested\n", GetPid());258 RmDirRecursive(TempPath("FuzzWithFork", ".dir"));259 F->PrintFinalStats();260 _Exit(0);261}262 263int Fuzzer::InterruptExitCode() {264 assert(F);265 return F->Options.InterruptExitCode;266}267 268void Fuzzer::InterruptCallback() {269 Printf("==%lu== libFuzzer: run interrupted; exiting\n", GetPid());270 PrintFinalStats();271 ScopedDisableMsanInterceptorChecks S; // RmDirRecursive may call opendir().272 RmDirRecursive(TempPath("FuzzWithFork", ".dir"));273 // Stop right now, don't perform any at-exit actions.274 _Exit(Options.InterruptExitCode);275}276 277NO_SANITIZE_MEMORY278void Fuzzer::AlarmCallback() {279 assert(Options.UnitTimeoutSec > 0);280 // In Windows and Fuchsia, Alarm callback is executed by a different thread.281 // NetBSD's current behavior needs this change too.282#if !LIBFUZZER_WINDOWS && !LIBFUZZER_NETBSD && !LIBFUZZER_FUCHSIA283 if (!InFuzzingThread())284 return;285#endif286 if (!RunningUserCallback)287 return; // We have not started running units yet.288 size_t Seconds =289 duration_cast<seconds>(system_clock::now() - UnitStartTime).count();290 if (Seconds == 0)291 return;292 if (Options.Verbosity >= 2)293 Printf("AlarmCallback %zd\n", Seconds);294 if (Seconds >= (size_t)Options.UnitTimeoutSec) {295 if (EF->__sanitizer_acquire_crash_state &&296 !EF->__sanitizer_acquire_crash_state())297 return;298 Printf("ALARM: working on the last Unit for %zd seconds\n", Seconds);299 Printf(" and the timeout value is %d (use -timeout=N to change)\n",300 Options.UnitTimeoutSec);301 DumpCurrentUnit("timeout-");302 Printf("==%lu== ERROR: libFuzzer: timeout after %zu seconds\n", GetPid(),303 Seconds);304 PrintStackTrace();305 Printf("SUMMARY: libFuzzer: timeout\n");306 PrintFinalStats();307 _Exit(Options.TimeoutExitCode); // Stop right now.308 }309}310 311void Fuzzer::RssLimitCallback() {312 if (EF->__sanitizer_acquire_crash_state &&313 !EF->__sanitizer_acquire_crash_state())314 return;315 Printf("==%lu== ERROR: libFuzzer: out-of-memory (used: %zdMb; limit: %dMb)\n",316 GetPid(), GetPeakRSSMb(), Options.RssLimitMb);317 Printf(" To change the out-of-memory limit use -rss_limit_mb=<N>\n\n");318 PrintMemoryProfile();319 DumpCurrentUnit("oom-");320 Printf("SUMMARY: libFuzzer: out-of-memory\n");321 PrintFinalStats();322 _Exit(Options.OOMExitCode); // Stop right now.323}324 325void Fuzzer::PrintStats(const char *Where, const char *End, size_t Units,326 size_t Features) {327 size_t ExecPerSec = execPerSec();328 if (!Options.Verbosity)329 return;330 Printf("#%zd\t%s", TotalNumberOfRuns, Where);331 if (size_t N = TPC.GetTotalPCCoverage())332 Printf(" cov: %zd", N);333 if (size_t N = Features ? Features : Corpus.NumFeatures())334 Printf(" ft: %zd", N);335 if (!Corpus.empty()) {336 Printf(" corp: %zd", Corpus.NumActiveUnits());337 if (size_t N = Corpus.SizeInBytes()) {338 if (N < (1 << 14))339 Printf("/%zdb", N);340 else if (N < (1 << 24))341 Printf("/%zdKb", N >> 10);342 else343 Printf("/%zdMb", N >> 20);344 }345 if (size_t FF = Corpus.NumInputsThatTouchFocusFunction())346 Printf(" focus: %zd", FF);347 }348 if (TmpMaxMutationLen)349 Printf(" lim: %zd", TmpMaxMutationLen);350 if (Units)351 Printf(" units: %zd", Units);352 353 Printf(" exec/s: %zd", ExecPerSec);354 Printf(" rss: %zdMb", GetPeakRSSMb());355 Printf("%s", End);356}357 358void Fuzzer::PrintFinalStats() {359 if (Options.PrintFullCoverage)360 TPC.PrintCoverage(/*PrintAllCounters=*/true);361 if (Options.PrintCoverage)362 TPC.PrintCoverage(/*PrintAllCounters=*/false);363 if (Options.PrintCorpusStats)364 Corpus.PrintStats();365 if (!Options.PrintFinalStats)366 return;367 size_t ExecPerSec = execPerSec();368 Printf("stat::number_of_executed_units: %zd\n", TotalNumberOfRuns);369 Printf("stat::average_exec_per_sec: %zd\n", ExecPerSec);370 Printf("stat::new_units_added: %zd\n", NumberOfNewUnitsAdded);371 Printf("stat::slowest_unit_time_sec: %ld\n", TimeOfLongestUnitInSeconds);372 Printf("stat::peak_rss_mb: %zd\n", GetPeakRSSMb());373}374 375void Fuzzer::SetMaxInputLen(size_t MaxInputLen) {376 assert(this->MaxInputLen == 0); // Can only reset MaxInputLen from 0 to non-0.377 assert(MaxInputLen);378 this->MaxInputLen = MaxInputLen;379 this->MaxMutationLen = MaxInputLen;380 AllocateCurrentUnitData();381 Printf("INFO: -max_len is not provided; "382 "libFuzzer will not generate inputs larger than %zd bytes\n",383 MaxInputLen);384}385 386void Fuzzer::SetMaxMutationLen(size_t MaxMutationLen) {387 assert(MaxMutationLen && MaxMutationLen <= MaxInputLen);388 this->MaxMutationLen = MaxMutationLen;389}390 391void Fuzzer::CheckExitOnSrcPosOrItem() {392 if (!Options.ExitOnSrcPos.empty()) {393 static auto *PCsSet = new std::set<uintptr_t>;394 auto HandlePC = [&](const TracePC::PCTableEntry *TE) {395 if (!PCsSet->insert(TE->PC).second)396 return;397 std::string Descr = DescribePC("%F %L", TE->PC + 1);398 if (Descr.find(Options.ExitOnSrcPos) != std::string::npos) {399 Printf("INFO: found line matching '%s', exiting.\n",400 Options.ExitOnSrcPos.c_str());401 _Exit(0);402 }403 };404 TPC.ForEachObservedPC(HandlePC);405 }406 if (!Options.ExitOnItem.empty()) {407 if (Corpus.HasUnit(Options.ExitOnItem)) {408 Printf("INFO: found item with checksum '%s', exiting.\n",409 Options.ExitOnItem.c_str());410 _Exit(0);411 }412 }413}414 415void Fuzzer::RereadOutputCorpus(size_t MaxSize) {416 if (Options.OutputCorpus.empty() || !Options.ReloadIntervalSec)417 return;418 std::vector<Unit> AdditionalCorpus;419 std::vector<std::string> AdditionalCorpusPaths;420 ReadDirToVectorOfUnits(421 Options.OutputCorpus.c_str(), &AdditionalCorpus,422 &EpochOfLastReadOfOutputCorpus, MaxSize,423 /*ExitOnError*/ false,424 (Options.Verbosity >= 2 ? &AdditionalCorpusPaths : nullptr));425 if (Options.Verbosity >= 2)426 Printf("Reload: read %zd new units.\n", AdditionalCorpus.size());427 bool Reloaded = false;428 for (size_t i = 0; i != AdditionalCorpus.size(); ++i) {429 auto &U = AdditionalCorpus[i];430 if (U.size() > MaxSize)431 U.resize(MaxSize);432 if (!Corpus.HasUnit(U)) {433 if (RunOne(U.data(), U.size())) {434 CheckExitOnSrcPosOrItem();435 Reloaded = true;436 if (Options.Verbosity >= 2)437 Printf("Reloaded %s\n", AdditionalCorpusPaths[i].c_str());438 }439 }440 }441 if (Reloaded)442 PrintStats("RELOAD");443}444 445void Fuzzer::PrintPulseAndReportSlowInput(const uint8_t *Data, size_t Size) {446 auto TimeOfUnit =447 duration_cast<seconds>(UnitStopTime - UnitStartTime).count();448 if (!(TotalNumberOfRuns & (TotalNumberOfRuns - 1)) &&449 secondsSinceProcessStartUp() >= 2)450 PrintStats("pulse ");451 auto Threshold =452 static_cast<long>(static_cast<double>(TimeOfLongestUnitInSeconds) * 1.1);453 if (TimeOfUnit > Threshold && TimeOfUnit >= Options.ReportSlowUnits) {454 TimeOfLongestUnitInSeconds = TimeOfUnit;455 Printf("Slowest unit: %ld s:\n", TimeOfLongestUnitInSeconds);456 WriteUnitToFileWithPrefix({Data, Data + Size}, "slow-unit-");457 }458}459 460static void WriteFeatureSetToFile(const std::string &FeaturesDir,461 const std::string &FileName,462 const std::vector<uint32_t> &FeatureSet) {463 if (FeaturesDir.empty() || FeatureSet.empty()) return;464 WriteToFile(reinterpret_cast<const uint8_t *>(FeatureSet.data()),465 FeatureSet.size() * sizeof(FeatureSet[0]),466 DirPlusFile(FeaturesDir, FileName));467}468 469static void RenameFeatureSetFile(const std::string &FeaturesDir,470 const std::string &OldFile,471 const std::string &NewFile) {472 if (FeaturesDir.empty()) return;473 RenameFile(DirPlusFile(FeaturesDir, OldFile),474 DirPlusFile(FeaturesDir, NewFile));475}476 477static void WriteEdgeToMutationGraphFile(const std::string &MutationGraphFile,478 const InputInfo *II,479 const InputInfo *BaseII,480 const std::string &MS) {481 if (MutationGraphFile.empty())482 return;483 484 std::string Sha1 = Sha1ToString(II->Sha1);485 486 std::string OutputString;487 488 // Add a new vertex.489 OutputString.append("\"");490 OutputString.append(Sha1);491 OutputString.append("\"\n");492 493 // Add a new edge if there is base input.494 if (BaseII) {495 std::string BaseSha1 = Sha1ToString(BaseII->Sha1);496 OutputString.append("\"");497 OutputString.append(BaseSha1);498 OutputString.append("\" -> \"");499 OutputString.append(Sha1);500 OutputString.append("\" [label=\"");501 OutputString.append(MS);502 OutputString.append("\"];\n");503 }504 505 AppendToFile(OutputString, MutationGraphFile);506}507 508bool Fuzzer::RunOne(const uint8_t *Data, size_t Size, bool MayDeleteFile,509 InputInfo *II, bool ForceAddToCorpus,510 bool *FoundUniqFeatures) {511 if (!Size)512 return false;513 // Largest input length should be INT_MAX.514 assert(Size < std::numeric_limits<uint32_t>::max());515 516 if(!ExecuteCallback(Data, Size)) return false;517 auto TimeOfUnit = duration_cast<microseconds>(UnitStopTime - UnitStartTime);518 519 UniqFeatureSetTmp.clear();520 size_t FoundUniqFeaturesOfII = 0;521 size_t NumUpdatesBefore = Corpus.NumFeatureUpdates();522 TPC.CollectFeatures([&](uint32_t Feature) {523 if (Corpus.AddFeature(Feature, static_cast<uint32_t>(Size), Options.Shrink))524 UniqFeatureSetTmp.push_back(Feature);525 if (Options.Entropic)526 Corpus.UpdateFeatureFrequency(II, Feature);527 if (Options.ReduceInputs && II && !II->NeverReduce)528 if (std::binary_search(II->UniqFeatureSet.begin(),529 II->UniqFeatureSet.end(), Feature))530 FoundUniqFeaturesOfII++;531 });532 if (FoundUniqFeatures)533 *FoundUniqFeatures = FoundUniqFeaturesOfII;534 PrintPulseAndReportSlowInput(Data, Size);535 size_t NumNewFeatures = Corpus.NumFeatureUpdates() - NumUpdatesBefore;536 if (NumNewFeatures || ForceAddToCorpus) {537 TPC.UpdateObservedPCs();538 auto NewII =539 Corpus.AddToCorpus({Data, Data + Size}, NumNewFeatures, MayDeleteFile,540 TPC.ObservedFocusFunction(), ForceAddToCorpus,541 TimeOfUnit, UniqFeatureSetTmp, DFT, II);542 WriteFeatureSetToFile(Options.FeaturesDir, Sha1ToString(NewII->Sha1),543 NewII->UniqFeatureSet);544 WriteEdgeToMutationGraphFile(Options.MutationGraphFile, NewII, II,545 MD.MutationSequence());546 return true;547 }548 if (II && FoundUniqFeaturesOfII &&549 II->DataFlowTraceForFocusFunction.empty() &&550 FoundUniqFeaturesOfII == II->UniqFeatureSet.size() &&551 II->U.size() > Size) {552 auto OldFeaturesFile = Sha1ToString(II->Sha1);553 Corpus.Replace(II, {Data, Data + Size}, TimeOfUnit);554 RenameFeatureSetFile(Options.FeaturesDir, OldFeaturesFile,555 Sha1ToString(II->Sha1));556 return true;557 }558 return false;559}560 561void Fuzzer::TPCUpdateObservedPCs() { TPC.UpdateObservedPCs(); }562 563size_t Fuzzer::GetCurrentUnitInFuzzingThead(const uint8_t **Data) const {564 assert(InFuzzingThread());565 *Data = CurrentUnitData;566 return CurrentUnitSize;567}568 569void Fuzzer::CrashOnOverwrittenData() {570 Printf("==%d== ERROR: libFuzzer: fuzz target overwrites its const input\n",571 (int)GetPid());572 PrintStackTrace();573 Printf("SUMMARY: libFuzzer: overwrites-const-input\n");574 DumpCurrentUnit("crash-");575 PrintFinalStats();576 _Exit(Options.ErrorExitCode); // Stop right now.577}578 579// Compare two arrays, but not all bytes if the arrays are large.580static bool LooseMemeq(const uint8_t *A, const uint8_t *B, size_t Size) {581 const size_t Limit = 64;582 // memcmp cannot take null pointer arguments even if Size is 0.583 if (!Size)584 return true;585 if (Size <= 64)586 return !memcmp(A, B, Size);587 // Compare first and last Limit/2 bytes.588 return !memcmp(A, B, Limit / 2) &&589 !memcmp(A + Size - Limit / 2, B + Size - Limit / 2, Limit / 2);590}591 592// This method is not inlined because it would cause a test to fail where it593// is part of the stack unwinding. See D97975 for details.594ATTRIBUTE_NOINLINE bool Fuzzer::ExecuteCallback(const uint8_t *Data,595 size_t Size) {596 TPC.RecordInitialStack();597 TotalNumberOfRuns++;598 assert(InFuzzingThread());599 // We copy the contents of Unit into a separate heap buffer600 // so that we reliably find buffer overflows in it.601 uint8_t *DataCopy = new uint8_t[Size];602 // memcpy cannot take null pointer arguments even if Size is 0.603 if (Size)604 memcpy(DataCopy, Data, Size);605 if (EF->__msan_unpoison)606 EF->__msan_unpoison(DataCopy, Size);607 if (EF->__msan_unpoison_param)608 EF->__msan_unpoison_param(2);609 if (CurrentUnitData && CurrentUnitData != Data)610 memcpy(CurrentUnitData, Data, Size);611 CurrentUnitSize = Size;612 int CBRes = 0;613 {614 ScopedEnableMsanInterceptorChecks S;615 AllocTracer.Start(Options.TraceMalloc);616 UnitStartTime = system_clock::now();617 TPC.ResetMaps();618 RunningUserCallback = true;619 CBRes = CB(DataCopy, Size);620 RunningUserCallback = false;621 UnitStopTime = system_clock::now();622 assert(CBRes == 0 || CBRes == -1);623 HasMoreMallocsThanFrees = AllocTracer.Stop();624 }625 if (!LooseMemeq(DataCopy, Data, Size))626 CrashOnOverwrittenData();627 CurrentUnitSize = 0;628 delete[] DataCopy;629 return CBRes == 0;630}631 632std::string Fuzzer::WriteToOutputCorpus(const Unit &U) {633 if (Options.OnlyASCII)634 assert(IsASCII(U));635 if (Options.OutputCorpus.empty())636 return "";637 std::string Path = DirPlusFile(Options.OutputCorpus, Hash(U));638 WriteToFile(U, Path);639 if (Options.Verbosity >= 2)640 Printf("Written %zd bytes to %s\n", U.size(), Path.c_str());641 return Path;642}643 644void Fuzzer::WriteUnitToFileWithPrefix(const Unit &U, const char *Prefix) {645 if (!Options.SaveArtifacts)646 return;647 std::string Path = Options.ArtifactPrefix + Prefix + Hash(U);648 if (!Options.ExactArtifactPath.empty())649 Path = Options.ExactArtifactPath; // Overrides ArtifactPrefix.650 WriteToFile(U, Path);651 Printf("artifact_prefix='%s'; Test unit written to %s\n",652 Options.ArtifactPrefix.c_str(), Path.c_str());653 if (U.size() <= kMaxUnitSizeToPrint)654 Printf("Base64: %s\n", Base64(U).c_str());655}656 657void Fuzzer::PrintStatusForNewUnit(const Unit &U, const char *Text) {658 if (!Options.PrintNEW)659 return;660 PrintStats(Text, "");661 if (Options.Verbosity) {662 Printf(" L: %zd/%zd ", U.size(), Corpus.MaxInputSize());663 MD.PrintMutationSequence(Options.Verbosity >= 2);664 Printf("\n");665 }666}667 668void Fuzzer::ReportNewCoverage(InputInfo *II, const Unit &U) {669 II->NumSuccessfulMutations++;670 MD.RecordSuccessfulMutationSequence();671 PrintStatusForNewUnit(U, II->Reduced ? "REDUCE" : "NEW ");672 WriteToOutputCorpus(U);673 NumberOfNewUnitsAdded++;674 CheckExitOnSrcPosOrItem(); // Check only after the unit is saved to corpus.675 LastCorpusUpdateRun = TotalNumberOfRuns;676}677 678// Tries detecting a memory leak on the particular input that we have just679// executed before calling this function.680void Fuzzer::TryDetectingAMemoryLeak(const uint8_t *Data, size_t Size,681 bool DuringInitialCorpusExecution) {682 if (!HasMoreMallocsThanFrees)683 return; // mallocs==frees, a leak is unlikely.684 if (!Options.DetectLeaks)685 return;686 if (!DuringInitialCorpusExecution &&687 TotalNumberOfRuns >= Options.MaxNumberOfRuns)688 return;689 if (!&(EF->__lsan_enable) || !&(EF->__lsan_disable) ||690 !(EF->__lsan_do_recoverable_leak_check))691 return; // No lsan.692 // Run the target once again, but with lsan disabled so that if there is693 // a real leak we do not report it twice.694 EF->__lsan_disable();695 ExecuteCallback(Data, Size);696 EF->__lsan_enable();697 if (!HasMoreMallocsThanFrees)698 return; // a leak is unlikely.699 if (NumberOfLeakDetectionAttempts++ > 1000) {700 Options.DetectLeaks = false;701 Printf("INFO: libFuzzer disabled leak detection after every mutation.\n"702 " Most likely the target function accumulates allocated\n"703 " memory in a global state w/o actually leaking it.\n"704 " You may try running this binary with -trace_malloc=[12]"705 " to get a trace of mallocs and frees.\n"706 " If LeakSanitizer is enabled in this process it will still\n"707 " run on the process shutdown.\n");708 return;709 }710 // Now perform the actual lsan pass. This is expensive and we must ensure711 // we don't call it too often.712 if (EF->__lsan_do_recoverable_leak_check()) { // Leak is found, report it.713 if (DuringInitialCorpusExecution)714 Printf("\nINFO: a leak has been found in the initial corpus.\n\n");715 Printf("INFO: to ignore leaks on libFuzzer side use -detect_leaks=0.\n\n");716 CurrentUnitSize = Size;717 DumpCurrentUnit("leak-");718 PrintFinalStats();719 _Exit(Options.ErrorExitCode); // not exit() to disable lsan further on.720 }721}722 723void Fuzzer::MutateAndTestOne() {724 MD.StartMutationSequence();725 726 auto &II = Corpus.ChooseUnitToMutate(MD.GetRand());727 if (Options.DoCrossOver) {728 auto &CrossOverII = Corpus.ChooseUnitToCrossOverWith(729 MD.GetRand(), Options.CrossOverUniformDist);730 MD.SetCrossOverWith(&CrossOverII.U);731 }732 const auto &U = II.U;733 memcpy(BaseSha1, II.Sha1, sizeof(BaseSha1));734 assert(CurrentUnitData);735 size_t Size = U.size();736 assert(Size <= MaxInputLen && "Oversized Unit");737 memcpy(CurrentUnitData, U.data(), Size);738 739 assert(MaxMutationLen > 0);740 741 size_t CurrentMaxMutationLen =742 Min(MaxMutationLen, Max(U.size(), TmpMaxMutationLen));743 assert(CurrentMaxMutationLen > 0);744 745 for (int i = 0; i < Options.MutateDepth; i++) {746 if (TotalNumberOfRuns >= Options.MaxNumberOfRuns)747 break;748 MaybeExitGracefully();749 size_t NewSize = 0;750 if (II.HasFocusFunction && !II.DataFlowTraceForFocusFunction.empty() &&751 Size <= CurrentMaxMutationLen)752 NewSize = MD.MutateWithMask(CurrentUnitData, Size, Size,753 II.DataFlowTraceForFocusFunction);754 755 // If MutateWithMask either failed or wasn't called, call default Mutate.756 if (!NewSize)757 NewSize = MD.Mutate(CurrentUnitData, Size, CurrentMaxMutationLen);758 assert(NewSize > 0 && "Mutator returned empty unit");759 assert(NewSize <= CurrentMaxMutationLen && "Mutator return oversized unit");760 Size = NewSize;761 II.NumExecutedMutations++;762 Corpus.IncrementNumExecutedMutations();763 764 bool FoundUniqFeatures = false;765 bool NewCov = RunOne(CurrentUnitData, Size, /*MayDeleteFile=*/true, &II,766 /*ForceAddToCorpus*/ false, &FoundUniqFeatures);767 TryDetectingAMemoryLeak(CurrentUnitData, Size,768 /*DuringInitialCorpusExecution*/ false);769 if (NewCov) {770 ReportNewCoverage(&II, {CurrentUnitData, CurrentUnitData + Size});771 break; // We will mutate this input more in the next rounds.772 }773 if (Options.ReduceDepth && !FoundUniqFeatures)774 break;775 }776 777 II.NeedsEnergyUpdate = true;778}779 780void Fuzzer::PurgeAllocator() {781 if (Options.PurgeAllocatorIntervalSec < 0 || !EF->__sanitizer_purge_allocator)782 return;783 if (duration_cast<seconds>(system_clock::now() -784 LastAllocatorPurgeAttemptTime)785 .count() < Options.PurgeAllocatorIntervalSec)786 return;787 788 if (Options.RssLimitMb <= 0 ||789 GetPeakRSSMb() > static_cast<size_t>(Options.RssLimitMb) / 2)790 EF->__sanitizer_purge_allocator();791 792 LastAllocatorPurgeAttemptTime = system_clock::now();793}794 795void Fuzzer::ReadAndExecuteSeedCorpora(std::vector<SizedFile> &CorporaFiles) {796 const size_t kMaxSaneLen = 1 << 20;797 const size_t kMinDefaultLen = 4096;798 size_t MaxSize = 0;799 size_t MinSize = -1;800 size_t TotalSize = 0;801 for (auto &File : CorporaFiles) {802 MaxSize = Max(File.Size, MaxSize);803 MinSize = Min(File.Size, MinSize);804 TotalSize += File.Size;805 }806 if (Options.MaxLen == 0)807 SetMaxInputLen(std::clamp(MaxSize, kMinDefaultLen, kMaxSaneLen));808 assert(MaxInputLen > 0);809 810 // Test the callback with empty input and never try it again.811 uint8_t dummy = 0;812 ExecuteCallback(&dummy, 0);813 814 if (CorporaFiles.empty()) {815 Printf("INFO: A corpus is not provided, starting from an empty corpus\n");816 Unit U({'\n'}); // Valid ASCII input.817 RunOne(U.data(), U.size());818 } else {819 Printf("INFO: seed corpus: files: %zd min: %zdb max: %zdb total: %zdb"820 " rss: %zdMb\n",821 CorporaFiles.size(), MinSize, MaxSize, TotalSize, GetPeakRSSMb());822 if (Options.ShuffleAtStartUp)823 std::shuffle(CorporaFiles.begin(), CorporaFiles.end(), MD.GetRand());824 825 if (Options.PreferSmall) {826 std::stable_sort(CorporaFiles.begin(), CorporaFiles.end());827 assert(CorporaFiles.front().Size <= CorporaFiles.back().Size);828 }829 830 // Load and execute inputs one by one.831 for (auto &SF : CorporaFiles) {832 auto U = FileToVector(SF.File, MaxInputLen, /*ExitOnError=*/false);833 assert(U.size() <= MaxInputLen);834 RunOne(U.data(), U.size(), /*MayDeleteFile*/ false, /*II*/ nullptr,835 /*ForceAddToCorpus*/ Options.KeepSeed,836 /*FoundUniqFeatures*/ nullptr);837 CheckExitOnSrcPosOrItem();838 TryDetectingAMemoryLeak(U.data(), U.size(),839 /*DuringInitialCorpusExecution*/ true);840 }841 }842 843 PrintStats("INITED");844 if (!Options.FocusFunction.empty()) {845 Printf("INFO: %zd/%zd inputs touch the focus function\n",846 Corpus.NumInputsThatTouchFocusFunction(), Corpus.size());847 if (!Options.DataFlowTrace.empty())848 Printf("INFO: %zd/%zd inputs have the Data Flow Trace\n",849 Corpus.NumInputsWithDataFlowTrace(),850 Corpus.NumInputsThatTouchFocusFunction());851 }852 853 if (Corpus.empty() && Options.MaxNumberOfRuns) {854 Printf("WARNING: no interesting inputs were found so far. "855 "Is the code instrumented for coverage?\n"856 "This may also happen if the target rejected all inputs we tried so "857 "far\n");858 // The remaining logic requires that the corpus is not empty,859 // so we add one fake input to the in-memory corpus.860 Corpus.AddToCorpus({'\n'}, /*NumFeatures=*/1, /*MayDeleteFile=*/true,861 /*HasFocusFunction=*/false, /*NeverReduce=*/false,862 /*TimeOfUnit=*/duration_cast<microseconds>(0s), {0}, DFT,863 /*BaseII*/ nullptr);864 }865}866 867void Fuzzer::Loop(std::vector<SizedFile> &CorporaFiles) {868 auto FocusFunctionOrAuto = Options.FocusFunction;869 DFT.Init(Options.DataFlowTrace, &FocusFunctionOrAuto, CorporaFiles,870 MD.GetRand());871 TPC.SetFocusFunction(FocusFunctionOrAuto);872 ReadAndExecuteSeedCorpora(CorporaFiles);873 DFT.Clear(); // No need for DFT any more.874 TPC.SetPrintNewPCs(Options.PrintNewCovPcs);875 TPC.SetPrintNewFuncs(Options.PrintNewCovFuncs);876 system_clock::time_point LastCorpusReload = system_clock::now();877 878 TmpMaxMutationLen =879 Min(MaxMutationLen, Max(size_t(4), Corpus.MaxInputSize()));880 881 while (true) {882 auto Now = system_clock::now();883 if (!Options.StopFile.empty() &&884 !FileToVector(Options.StopFile, 1, false).empty())885 break;886 if (duration_cast<seconds>(Now - LastCorpusReload).count() >=887 Options.ReloadIntervalSec) {888 RereadOutputCorpus(MaxInputLen);889 LastCorpusReload = system_clock::now();890 }891 if (TotalNumberOfRuns >= Options.MaxNumberOfRuns)892 break;893 if (TimedOut())894 break;895 896 // Update TmpMaxMutationLen897 if (Options.LenControl) {898 if (TmpMaxMutationLen < MaxMutationLen &&899 TotalNumberOfRuns - LastCorpusUpdateRun >900 Options.LenControl * Log(TmpMaxMutationLen)) {901 TmpMaxMutationLen =902 Min(MaxMutationLen, TmpMaxMutationLen + Log(TmpMaxMutationLen));903 LastCorpusUpdateRun = TotalNumberOfRuns;904 }905 } else {906 TmpMaxMutationLen = MaxMutationLen;907 }908 909 // Perform several mutations and runs.910 MutateAndTestOne();911 912 PurgeAllocator();913 }914 915 PrintStats("DONE ", "\n");916 MD.PrintRecommendedDictionary();917}918 919void Fuzzer::MinimizeCrashLoop(const Unit &U) {920 if (U.size() <= 1)921 return;922 while (!TimedOut() && TotalNumberOfRuns < Options.MaxNumberOfRuns) {923 MD.StartMutationSequence();924 memcpy(CurrentUnitData, U.data(), U.size());925 for (int i = 0; i < Options.MutateDepth; i++) {926 size_t NewSize = MD.Mutate(CurrentUnitData, U.size(), MaxMutationLen);927 assert(NewSize > 0 && NewSize <= MaxMutationLen);928 ExecuteCallback(CurrentUnitData, NewSize);929 PrintPulseAndReportSlowInput(CurrentUnitData, NewSize);930 TryDetectingAMemoryLeak(CurrentUnitData, NewSize,931 /*DuringInitialCorpusExecution*/ false);932 }933 }934}935 936} // namespace fuzzer937 938extern "C" {939 940ATTRIBUTE_INTERFACE size_t941LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize) {942 assert(fuzzer::F);943 return fuzzer::F->GetMD().DefaultMutate(Data, Size, MaxSize);944}945 946} // extern "C"947