llvm-project/llvm/lib/Fuzzer/FuzzerLoop.cpp
Marcos Pividori 463f8bdd0b [libFuzzer] Properly use unsigned for Process ID.
Use unsigned for PID instead of signed int. GetCurrentProcessId() returns
an unsigned (DWORD) so we must be sure we can deal with all possible values.
I use a long unsigned to be sure it can hold a 32 bit unsigned (DWORD).

Differential Revision: https://reviews.llvm.org/D27281

llvm-svn: 289558
2016-12-13 17:45:44 +00:00

787 lines
25 KiB
C++

//===- 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;
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();
}
}
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 (!CurrentUnitSize)
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.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();
int Res = CB(DataCopy, Size);
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"