//===- FuzzerLoop.cpp - Fuzzer's main loop --------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // Fuzzer's main loop. //===----------------------------------------------------------------------===//
// 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;
if (Options.Verbosity)
TPC.PrintModuleInfo(); if (!Options.OutputCorpus.empty() && Options.ReloadIntervalSec)
EpochOfLastReadOfOutputCorpus = GetEpoch(Options.OutputCorpus);
MaxInputLen = MaxMutationLen = Options.MaxLen;
TmpMaxMutationLen = 0; // Will be set once we load the corpus.
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::CrashCallback() { if (EF->__sanitizer_acquire_crash_state &&
!EF->__sanitizer_acquire_crash_state()) return;
Printf("==%lu== ERROR: libFuzzer: deadly signal\n", GetPid());
PrintStackTrace();
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); // Stop right now.
}
void Fuzzer::ExitCallback() { if (!RunningUserCallback) return; // This exit did not come from the user callback if (EF->__sanitizer_acquire_crash_state &&
!EF->__sanitizer_acquire_crash_state()) return;
Printf("==%lu== ERROR: libFuzzer: fuzz target exited\n", GetPid());
PrintStackTrace();
Printf("SUMMARY: libFuzzer: fuzz target exited\n");
DumpCurrentUnit("crash-");
PrintFinalStats();
_Exit(Options.ErrorExitCode);
}
bool Fuzzer::MaybeExitGracefully() { if (!F->GracefulExitRequested) returnfalse;
Printf("==%lu== INFO: libFuzzer: exiting as requested\n", GetPid());
RmDirRecursive(TempPath("FuzzWithFork", ".dir"));
F->PrintFinalStats(); returntrue;
}
void Fuzzer::InterruptCallback() {
Printf("==%lu== libFuzzer: run interrupted; exiting\n", GetPid());
PrintFinalStats();
ScopedDisableMsanInterceptorChecks S; // RmDirRecursive may call opendir().
RmDirRecursive(TempPath("FuzzWithFork", ".dir")); // Stop right now, don't perform any at-exit actions.
_Exit(Options.InterruptExitCode);
}
NO_SANITIZE_MEMORY void Fuzzer::AlarmCallback() {
assert(Options.UnitTimeoutSec > 0); // In Windows and Fuchsia, Alarm callback is executed by a different thread. // NetBSD's current behavior needs this change too. #if !LIBFUZZER_WINDOWS && !LIBFUZZER_NETBSD && !LIBFUZZER_FUCHSIA if (!InFuzzingThread()) return; #endif if (!RunningUserCallback) 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) { if (EF->__sanitizer_acquire_crash_state &&
!EF->__sanitizer_acquire_crash_state()) return;
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);
PrintStackTrace();
Printf("SUMMARY: libFuzzer: timeout\n");
PrintFinalStats();
_Exit(Options.TimeoutExitCode); // Stop right now.
}
}
void Fuzzer::RssLimitCallback() { if (EF->__sanitizer_acquire_crash_state &&
!EF->__sanitizer_acquire_crash_state()) return;
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");
PrintMemoryProfile();
DumpCurrentUnit("oom-");
Printf("SUMMARY: libFuzzer: out-of-memory\n");
PrintFinalStats();
_Exit(Options.OOMExitCode); // Stop right now.
}
void Fuzzer::PrintStats(constchar *Where, constchar *End, size_t Units,
size_t Features) {
size_t ExecPerSec = execPerSec(); if (!Options.Verbosity) return;
Printf("#%zd\t%s", TotalNumberOfRuns, Where); if (size_t N = TPC.GetTotalPCCoverage())
Printf(" cov: %zd", N); if (size_t N = Features ? Features : Corpus.NumFeatures())
Printf(" ft: %zd", N); if (!Corpus.empty()) {
Printf(" corp: %zd", Corpus.NumActiveUnits()); if (size_t N = Corpus.SizeInBytes()) { if (N < (1 << 14))
Printf("/%zdb", N); elseif (N < (1 << 24))
Printf("/%zdKb", N >> 10); else
Printf("/%zdMb", N >> 20);
} if (size_t FF = Corpus.NumInputsThatTouchFocusFunction())
Printf(" focus: %zd", FF);
} if (TmpMaxMutationLen)
Printf(" lim: %zd", TmpMaxMutationLen); if (Units)
Printf(" units: %zd", Units);
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; " "libFuzzer will not generate inputs larger than %zd bytes\n",
MaxInputLen);
}
// Compare two arrays, but not all bytes if the arrays are large. staticbool LooseMemeq(const uint8_t *A, const uint8_t *B, size_t Size) { const size_t Limit = 64; if (Size <= 64) return !memcmp(A, B, Size); // Compare first and last Limit/2 bytes. return !memcmp(A, B, Limit / 2) &&
!memcmp(A + Size - Limit / 2, B + Size - Limit / 2, Limit / 2);
}
int Fuzzer::ExecuteCallback(const uint8_t *Data, size_t Size) {
TPC.RecordInitialStack();
TotalNumberOfRuns++;
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 (EF->__msan_unpoison)
EF->__msan_unpoison(DataCopy, Size); if (EF->__msan_unpoison_param)
EF->__msan_unpoison_param(2); if (CurrentUnitData && CurrentUnitData != Data)
memcpy(CurrentUnitData, Data, Size);
CurrentUnitSize = Size; int Res = 0;
{
ScopedEnableMsanInterceptorChecks S;
AllocTracer.Start(Options.TraceMalloc);
UnitStartTime = system_clock::now();
TPC.ResetMaps();
RunningUserCallback = true;
Res = CB(DataCopy, Size);
RunningUserCallback = false;
UnitStopTime = system_clock::now();
HasMoreMallocsThanFrees = AllocTracer.Stop();
} if (!LooseMemeq(DataCopy, Data, Size))
CrashOnOverwrittenData();
CurrentUnitSize = 0; delete[] DataCopy; return Res;
}
std::string 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 %zd bytes to %s\n", U.size(), Path.c_str()); return Path;
}
void Fuzzer::WriteUnitToFileWithPrefix(const Unit &U, constchar *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, constchar *Text) { if (!Options.PrintNEW) return;
PrintStats(Text, ""); if (Options.Verbosity) {
Printf(" L: %zd/%zd ", U.size(), Corpus.MaxInputSize());
MD.PrintMutationSequence();
Printf("\n");
}
}
void Fuzzer::ReportNewCoverage(InputInfo *II, const Unit &U) {
II->NumSuccessfullMutations++;
MD.RecordSuccessfulMutationSequence();
PrintStatusForNewUnit(U, II->Reduced ? "REDUCE" : "NEW ");
WriteToOutputCorpus(U);
NumberOfNewUnitsAdded++;
CheckExitOnSrcPosOrItem(); // Check only after the unit is saved to corpus.
LastCorpusUpdateRun = TotalNumberOfRuns;
}
// 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 (!DuringInitialCorpusExecution &&
TotalNumberOfRuns >= Options.MaxNumberOfRuns) 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.
}
}
for (int i = 0; i < Options.MutateDepth; i++) { if (TotalNumberOfRuns >= Options.MaxNumberOfRuns) break; if (MaybeExitGracefully()) returntrue;
size_t NewSize = 0; if (II.HasFocusFunction && !II.DataFlowTraceForFocusFunction.empty() &&
Size <= CurrentMaxMutationLen)
NewSize = MD.MutateWithMask(CurrentUnitData, Size, Size,
II.DataFlowTraceForFocusFunction);
// If MutateWithMask either failed or wasn't called, call default Mutate. if (!NewSize)
NewSize = MD.Mutate(CurrentUnitData, Size, CurrentMaxMutationLen);
bool FoundUniqFeatures = false; bool NewCov = RunOne(CurrentUnitData, Size, /*MayDeleteFile=*/true, &II,
&FoundUniqFeatures);
TryDetectingAMemoryLeak(CurrentUnitData, Size, /*DuringInitialCorpusExecution*/ false); if (NewCov) {
ReportNewCoverage(&II, {CurrentUnitData, CurrentUnitData + Size}); break; // We will mutate this input more in the next rounds.
} if (Options.ReduceDepth && !FoundUniqFeatures) break;
}
II.NeedsEnergyUpdate = true; returnfalse;
}
void Fuzzer::PurgeAllocator() { if (Options.PurgeAllocatorIntervalSec < 0 || !EF->__sanitizer_purge_allocator) return; if (duration_cast<seconds>(system_clock::now() -
LastAllocatorPurgeAttemptTime)
.count() < Options.PurgeAllocatorIntervalSec) return;
// Test the callback with empty input and never try it again.
uint8_t dummy = 0;
ExecuteCallback(&dummy, 0);
if (CorporaFiles.empty()) {
Printf("INFO: A corpus is not provided, starting from an empty corpus\n");
Unit U({'\n'}); // Valid ASCII input.
RunOne(U.data(), U.size());
} else {
Printf("INFO: seed corpus: files: %zd min: %zdb max: %zdb total: %zdb" " rss: %zdMb\n",
CorporaFiles.size(), MinSize, MaxSize, TotalSize, GetPeakRSSMb()); if (Options.ShuffleAtStartUp)
std::shuffle(CorporaFiles.begin(), CorporaFiles.end(), MD.GetRand());
if (Options.PreferSmall) {
std::stable_sort(CorporaFiles.begin(), CorporaFiles.end());
assert(CorporaFiles.front().Size <= CorporaFiles.back().Size);
}
// Load and execute inputs one by one. for (auto &SF : CorporaFiles) { auto U = FileToVector(SF.File, MaxInputLen, /*ExitOnError=*/false);
assert(U.size() <= MaxInputLen);
RunOne(U.data(), U.size());
CheckExitOnSrcPosOrItem();
TryDetectingAMemoryLeak(U.data(), U.size(), /*DuringInitialCorpusExecution*/ true);
}
}
PrintStats("INITED"); if (!Options.FocusFunction.empty()) {
Printf("INFO: %zd/%zd inputs touch the focus function\n",
Corpus.NumInputsThatTouchFocusFunction(), Corpus.size()); if (!Options.DataFlowTrace.empty())
Printf("INFO: %zd/%zd inputs have the Data Flow Trace\n",
Corpus.NumInputsWithDataFlowTrace(),
Corpus.NumInputsThatTouchFocusFunction());
}
if (Corpus.empty() && Options.MaxNumberOfRuns) {
Printf("ERROR: no interesting inputs were found. " "Is the code instrumented for coverage? Exiting.\n"); return 1;
} return 0;
}
int Fuzzer::Loop(Vector<SizedFile> &CorporaFiles) { auto FocusFunctionOrAuto = Options.FocusFunction; int Res = DFT.Init(Options.DataFlowTrace, &FocusFunctionOrAuto, CorporaFiles,
MD.GetRand()); if (Res != 0) return Res;
Res = TPC.SetFocusFunction(FocusFunctionOrAuto); if (Res != 0) return Res;
Res = ReadAndExecuteSeedCorpora(CorporaFiles); if (Res != 0) return Res;
DFT.Clear(); // No need for DFT any more.
TPC.SetPrintNewPCs(Options.PrintNewCovPcs);
TPC.SetPrintNewFuncs(Options.PrintNewCovFuncs);
system_clock::time_point LastCorpusReload = system_clock::now();
while (true) { auto Now = system_clock::now(); if (!Options.StopFile.empty() &&
!FileToVector(Options.StopFile, 1, false).empty()) break; if (duration_cast<seconds>(Now - LastCorpusReload).count() >=
Options.ReloadIntervalSec) {
RereadOutputCorpus(MaxInputLen);
LastCorpusReload = system_clock::now();
} if (TotalNumberOfRuns >= Options.MaxNumberOfRuns) break; if (TimedOut()) break;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.