summaryrefslogtreecommitdiff
path: root/lib/Fuzzer/FuzzerLoop.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/Fuzzer/FuzzerLoop.cpp')
-rw-r--r--lib/Fuzzer/FuzzerLoop.cpp747
1 files changed, 513 insertions, 234 deletions
diff --git a/lib/Fuzzer/FuzzerLoop.cpp b/lib/Fuzzer/FuzzerLoop.cpp
index 5237682ff24d5..89db5e0ac0a1e 100644
--- a/lib/Fuzzer/FuzzerLoop.cpp
+++ b/lib/Fuzzer/FuzzerLoop.cpp
@@ -11,63 +11,159 @@
#include "FuzzerInternal.h"
#include <algorithm>
+#include <cstring>
+#include <memory>
#if defined(__has_include)
-# if __has_include(<sanitizer/coverage_interface.h>)
-# include <sanitizer/coverage_interface.h>
-# endif
+#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
-extern "C" {
-// Re-declare some of the sanitizer functions as "weak" so that
-// libFuzzer can be linked w/o the sanitizers and sanitizer-coverage
-// (in which case it will complain at start-up time).
-__attribute__((weak)) void __sanitizer_print_stack_trace();
-__attribute__((weak)) void __sanitizer_reset_coverage();
-__attribute__((weak)) size_t __sanitizer_get_total_unique_caller_callee_pairs();
-__attribute__((weak)) size_t __sanitizer_get_total_unique_coverage();
-__attribute__((weak))
-void __sanitizer_set_death_callback(void (*callback)(void));
-__attribute__((weak)) size_t __sanitizer_get_number_of_counters();
-__attribute__((weak))
-uintptr_t __sanitizer_update_counter_bitset_and_clear_counters(uint8_t *bitset);
-__attribute__((weak)) uintptr_t
-__sanitizer_get_coverage_pc_buffer(uintptr_t **data);
-}
+#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;
+static const size_t TruncateMaxRuns = 1000;
+
+thread_local bool Fuzzer::IsMyThread;
-static void MissingWeakApiFunction(const char *FnName) {
+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);
+ "Did you use -fsanitize-coverage=... to build your code?\n",
+ FnName);
exit(1);
}
-#define CHECK_WEAK_API_FUNCTION(fn) \
+#define CHECK_EXTERNAL_FUNCTION(fn) \
do { \
- if (!fn) \
- MissingWeakApiFunction(#fn); \
+ if (!(EF->fn)) \
+ MissingExternalApiFunction(#fn); \
} while (false)
// Only one Fuzzer per process.
static Fuzzer *F;
-Fuzzer::Fuzzer(UserSuppliedFuzzer &USF, FuzzingOptions Options)
- : USF(USF), Options(Options) {
+struct CoverageController {
+ static void Reset() {
+ CHECK_EXTERNAL_FUNCTION(__sanitizer_reset_coverage);
+ EF->__sanitizer_reset_coverage();
+ PcMapResetCurrent();
+ }
+
+ static void ResetCounters(const FuzzingOptions &Options) {
+ if (Options.UseCounters) {
+ EF->__sanitizer_update_counter_bitset_and_clear_counters(0);
+ }
+ }
+
+ static void Prepare(const FuzzingOptions &Options, 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.
+ static bool RecordMax(const FuzzingOptions &Options, 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;
+ }
+ }
+
+ uint64_t NewPcMapBits = PcMapMergeInto(&C->PCMap);
+ if (NewPcMapBits > C->PcMapBits) {
+ Res = true;
+ C->PcMapBits = NewPcMapBits;
+ }
+
+ uintptr_t *CoverageBuf;
+ uint64_t NewPcBufferLen =
+ EF->__sanitizer_get_coverage_pc_buffer(&CoverageBuf);
+ if (NewPcBufferLen > C->PcBufferLen) {
+ Res = true;
+ C->PcBufferLen = NewPcBufferLen;
+ }
+
+ 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() {
+ Mallocs = 0;
+ Frees = 0;
+ }
+ // Returns true if there were more mallocs than frees.
+ bool Stop() { return Mallocs > Frees; }
+ std::atomic<size_t> Mallocs;
+ std::atomic<size_t> Frees;
+};
+
+static MallocFreeTracer AllocTracer;
+
+void MallocHook(const volatile void *ptr, size_t size) {
+ AllocTracer.Mallocs++;
+}
+void FreeHook(const volatile void *ptr) {
+ AllocTracer.Frees++;
+}
+
+Fuzzer::Fuzzer(UserCallback CB, MutationDispatcher &MD, FuzzingOptions Options)
+ : CB(CB), MD(MD), Options(Options) {
SetDeathCallback();
InitializeTraceState();
assert(!F);
F = this;
+ ResetCoverage();
+ IsMyThread = true;
+ if (Options.DetectLeaks && EF->__sanitizer_install_malloc_and_free_hooks)
+ EF->__sanitizer_install_malloc_and_free_hooks(MallocHook, FreeHook);
}
-void Fuzzer::SetDeathCallback() {
- CHECK_WEAK_API_FUNCTION(__sanitizer_set_death_callback);
- __sanitizer_set_death_callback(StaticDeathCallback);
+void Fuzzer::LazyAllocateCurrentUnitData() {
+ if (CurrentUnitData || Options.MaxLen == 0) return;
+ CurrentUnitData = new uint8_t[Options.MaxLen];
}
-void Fuzzer::PrintUnitInASCII(const Unit &U, const char *PrintAfter) {
- PrintASCII(U, PrintAfter);
+void Fuzzer::SetDeathCallback() {
+ CHECK_EXTERNAL_FUNCTION(__sanitizer_set_death_callback);
+ EF->__sanitizer_set_death_callback(StaticDeathCallback);
}
void Fuzzer::StaticDeathCallback() {
@@ -75,13 +171,21 @@ void Fuzzer::StaticDeathCallback() {
F->DeathCallback();
}
-void Fuzzer::DeathCallback() {
- Printf("DEATH:\n");
- if (CurrentUnit.size() <= kMaxUnitSizeToPrint) {
- Print(CurrentUnit, "\n");
- PrintUnitInASCII(CurrentUnit, "\n");
+void Fuzzer::DumpCurrentUnit(const char *Prefix) {
+ if (!CurrentUnitData) return; // Happens when running individual inputs.
+ size_t UnitSize = CurrentUnitSize;
+ if (UnitSize <= kMaxUnitSizeToPrint) {
+ PrintHexArray(CurrentUnitData, UnitSize, "\n");
+ PrintASCII(CurrentUnitData, UnitSize, "\n");
}
- WriteUnitToFileWithPrefix(CurrentUnit, "crash-");
+ WriteUnitToFileWithPrefix({CurrentUnitData, CurrentUnitData + UnitSize},
+ Prefix);
+}
+
+NO_SANITIZE_MEMORY
+void Fuzzer::DeathCallback() {
+ DumpCurrentUnit("crash-");
+ PrintFinalStats();
}
void Fuzzer::StaticAlarmCallback() {
@@ -89,131 +193,256 @@ void Fuzzer::StaticAlarmCallback() {
F->AlarmCallback();
}
+void Fuzzer::StaticCrashSignalCallback() {
+ assert(F);
+ F->CrashCallback();
+}
+
+void Fuzzer::StaticInterruptCallback() {
+ assert(F);
+ F->InterruptCallback();
+}
+
+void Fuzzer::CrashCallback() {
+ Printf("==%d== 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("==%d== 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 (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);
- if (CurrentUnit.size() <= kMaxUnitSizeToPrint) {
- Print(CurrentUnit, "\n");
- PrintUnitInASCII(CurrentUnit, "\n");
- }
- WriteUnitToFileWithPrefix(CurrentUnit, "timeout-");
+ DumpCurrentUnit("timeout-");
Printf("==%d== ERROR: libFuzzer: timeout after %d seconds\n", GetPid(),
Seconds);
- if (__sanitizer_print_stack_trace)
- __sanitizer_print_stack_trace();
+ if (EF->__sanitizer_print_stack_trace)
+ EF->__sanitizer_print_stack_trace();
Printf("SUMMARY: libFuzzer: timeout\n");
- exit(1);
+ PrintFinalStats();
+ _Exit(Options.TimeoutExitCode); // Stop right now.
}
}
-void Fuzzer::PrintStats(const char *Where, const char *End) {
- size_t Seconds = secondsSinceProcessStartUp();
- size_t ExecPerSec = (Seconds ? TotalNumberOfRuns / Seconds : 0);
+void Fuzzer::RssLimitCallback() {
+ Printf(
+ "==%d== 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(50);
+ 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 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,%zd,%s\n", TotalNumberOfRuns,
- LastRecordedBlockCoverage, TotalBits(),
- LastRecordedCallerCalleeCoverage, Corpus.size(), ExecPerSec,
- TotalNumberOfExecutedTraceBasedMutations, Where);
+ 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 (LastRecordedBlockCoverage)
- Printf(" cov: %zd", LastRecordedBlockCoverage);
- if (auto TB = TotalBits())
+ if (MaxCoverage.BlockCoverage)
+ Printf(" cov: %zd", MaxCoverage.BlockCoverage);
+ if (MaxCoverage.PcMapBits)
+ Printf(" path: %zd", MaxCoverage.PcMapBits);
+ if (auto TB = MaxCoverage.CounterBitmapBits)
Printf(" bits: %zd", TB);
- if (LastRecordedCallerCalleeCoverage)
- Printf(" indir: %zd", LastRecordedCallerCalleeCoverage);
+ if (MaxCoverage.CallerCalleeCoverage)
+ Printf(" indir: %zd", MaxCoverage.CallerCalleeCoverage);
Printf(" units: %zd exec/s: %zd", Corpus.size(), ExecPerSec);
- if (TotalNumberOfExecutedTraceBasedMutations)
- Printf(" tbm: %zd", TotalNumberOfExecutedTraceBasedMutations);
Printf("%s", End);
}
-void Fuzzer::RereadOutputCorpus() {
- if (Options.OutputCorpus.empty()) return;
+void Fuzzer::PrintFinalStats() {
+ 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());
+}
+
+size_t Fuzzer::MaxUnitSizeInCorpus() const {
+ size_t Res = 0;
+ for (auto &X : Corpus)
+ Res = std::max(Res, X.size());
+ return Res;
+}
+
+void Fuzzer::SetMaxLen(size_t MaxLen) {
+ assert(Options.MaxLen == 0); // Can only reset MaxLen from 0 to non-0.
+ assert(MaxLen);
+ Options.MaxLen = MaxLen;
+ Printf("INFO: -max_len is not provided, using %zd\n", Options.MaxLen);
+}
+
+
+void Fuzzer::RereadOutputCorpus(size_t MaxSize) {
+ if (Options.OutputCorpus.empty())
+ return;
std::vector<Unit> AdditionalCorpus;
ReadDirToVectorOfUnits(Options.OutputCorpus.c_str(), &AdditionalCorpus,
- &EpochOfLastReadOfOutputCorpus);
+ &EpochOfLastReadOfOutputCorpus, MaxSize);
if (Corpus.empty()) {
Corpus = AdditionalCorpus;
return;
}
- if (!Options.Reload) return;
+ if (!Options.Reload)
+ return;
if (Options.Verbosity >= 2)
- Printf("Reload: read %zd new units.\n", AdditionalCorpus.size());
+ Printf("Reload: read %zd new units.\n", AdditionalCorpus.size());
for (auto &X : AdditionalCorpus) {
- if (X.size() > (size_t)Options.MaxLen)
- X.resize(Options.MaxLen);
+ if (X.size() > MaxSize)
+ X.resize(MaxSize);
if (UnitHashesAddedToCorpus.insert(Hash(X)).second) {
- CurrentUnit.clear();
- CurrentUnit.insert(CurrentUnit.begin(), X.begin(), X.end());
- if (RunOne(CurrentUnit)) {
+ if (RunOne(X)) {
Corpus.push_back(X);
+ UpdateCorpusDistribution();
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();
+ });
+}
+
+// Tries random prefixes of corpus items.
+// Prefix length is chosen according to exponential distribution
+// to sample short lengths much more heavily.
+void Fuzzer::TruncateUnits(std::vector<Unit> *NewCorpus) {
+ size_t MaxCorpusLen = 0;
+ for (const auto &U : Corpus)
+ MaxCorpusLen = std::max(MaxCorpusLen, U.size());
+
+ if (MaxCorpusLen <= 1)
+ return;
+
+ // 50% of exponential distribution is Log[2]/lambda.
+ // Choose lambda so that median is MaxCorpusLen / 2.
+ double Lambda = 2.0 * log(2.0) / static_cast<double>(MaxCorpusLen);
+ std::exponential_distribution<> Dist(Lambda);
+ std::vector<double> Sizes;
+ size_t TruncatePoints = std::max(1ul, TruncateMaxRuns / Corpus.size());
+ Sizes.reserve(TruncatePoints);
+ for (size_t I = 0; I < TruncatePoints; ++I) {
+ Sizes.push_back(Dist(MD.GetRand().Get_mt19937()) + 1);
+ }
+ std::sort(Sizes.begin(), Sizes.end());
+
+ for (size_t S : Sizes) {
+ for (const auto &U : Corpus) {
+ if (S < U.size() && RunOne(U.data(), S)) {
+ Unit U1(U.begin(), U.begin() + S);
+ NewCorpus->push_back(U1);
+ WriteToOutputCorpus(U1);
+ PrintStatusForNewUnit(U1);
+ }
+ }
+ }
+ PrintStats("TRUNC ");
+}
+
void Fuzzer::ShuffleAndMinimize() {
- bool PreferSmall = (Options.PreferSmallDuringInitialShuffle == 1 ||
- (Options.PreferSmallDuringInitialShuffle == -1 &&
- USF.GetRand().RandBool()));
- if (Options.Verbosity)
- Printf("PreferSmall: %d\n", PreferSmall);
PrintStats("READ ");
std::vector<Unit> NewCorpus;
- if (Options.ShuffleAtStartUp) {
- std::random_shuffle(Corpus.begin(), Corpus.end(), USF.GetRand());
- if (PreferSmall)
- std::stable_sort(
- Corpus.begin(), Corpus.end(),
- [](const Unit &A, const Unit &B) { return A.size() < B.size(); });
+ if (Options.ShuffleAtStartUp)
+ ShuffleCorpus(&Corpus);
+
+ if (Options.TruncateUnits) {
+ ResetCoverage();
+ TruncateUnits(&NewCorpus);
+ ResetCoverage();
}
- Unit &U = CurrentUnit;
- for (const auto &C : Corpus) {
- for (size_t First = 0; First < 1; First++) {
- U.clear();
- size_t Last = std::min(First + Options.MaxLen, C.size());
- U.insert(U.begin(), C.begin() + First, C.begin() + Last);
- if (Options.OnlyASCII)
- ToASCII(U);
- if (RunOne(U)) {
- NewCorpus.push_back(U);
- if (Options.Verbosity >= 2)
- Printf("NEW0: %zd L %zd\n", LastRecordedBlockCoverage, U.size());
- }
+
+ for (const auto &U : Corpus) {
+ bool NewCoverage = RunOne(U);
+ if (!Options.PruneCorpus || NewCoverage) {
+ NewCorpus.push_back(U);
+ if (Options.Verbosity >= 2)
+ Printf("NEW0: %zd L %zd\n", MaxCoverage.BlockCoverage, U.size());
}
+ TryDetectingAMemoryLeak(U.data(), U.size(),
+ /*DuringInitialCorpusExecution*/ true);
}
Corpus = NewCorpus;
+ UpdateCorpusDistribution();
for (auto &X : Corpus)
UnitHashesAddedToCorpus.insert(Hash(X));
PrintStats("INITED");
+ if (Corpus.empty()) {
+ Printf("ERROR: no interesting inputs were found. "
+ "Is the code instrumented for coverage? Exiting.\n");
+ exit(1);
+ }
}
-bool Fuzzer::RunOne(const Unit &U) {
- UnitStartTime = system_clock::now();
+bool Fuzzer::UpdateMaxCoverage() {
+ uintptr_t PrevBufferLen = MaxCoverage.PcBufferLen;
+ bool Res = CoverageController::RecordMax(Options, &MaxCoverage);
+
+ if (Options.PrintNewCovPcs && PrevBufferLen != MaxCoverage.PcBufferLen) {
+ uintptr_t *CoverageBuf;
+ EF->__sanitizer_get_coverage_pc_buffer(&CoverageBuf);
+ assert(CoverageBuf);
+ for (size_t I = PrevBufferLen; I < MaxCoverage.PcBufferLen; ++I) {
+ Printf("%p\n", CoverageBuf[I]);
+ }
+ }
+
+ return Res;
+}
+
+bool Fuzzer::RunOne(const uint8_t *Data, size_t Size) {
TotalNumberOfRuns++;
- PrepareCoverageBeforeRun();
- ExecuteCallback(U);
- bool Res = CheckCoverageAfterRun();
+ // TODO(aizatsky): this Reset call seems to be not needed.
+ CoverageController::ResetCounters(Options);
+ ExecuteCallback(Data, Size);
+ bool Res = UpdateMaxCoverage();
auto UnitStopTime = system_clock::now();
auto TimeOfUnit =
@@ -225,88 +454,63 @@ bool Fuzzer::RunOne(const Unit &U) {
TimeOfUnit >= Options.ReportSlowUnits) {
TimeOfLongestUnitInSeconds = TimeOfUnit;
Printf("Slowest unit: %zd s:\n", TimeOfLongestUnitInSeconds);
- WriteUnitToFileWithPrefix(U, "slow-unit-");
+ WriteUnitToFileWithPrefix({Data, Data + Size}, "slow-unit-");
}
return Res;
}
-void Fuzzer::RunOneAndUpdateCorpus(Unit &U) {
+void Fuzzer::RunOneAndUpdateCorpus(const uint8_t *Data, size_t Size) {
if (TotalNumberOfRuns >= Options.MaxNumberOfRuns)
return;
- if (Options.OnlyASCII)
- ToASCII(U);
- if (RunOne(U))
- ReportNewCoverage(U);
-}
-
-void Fuzzer::ExecuteCallback(const Unit &U) {
- const uint8_t *Data = U.data();
- uint8_t EmptyData;
- if (!Data)
- Data = &EmptyData;
- int Res = USF.TargetFunction(Data, U.size());
- (void)Res;
- assert(Res == 0);
-}
-
-size_t Fuzzer::RecordBlockCoverage() {
- CHECK_WEAK_API_FUNCTION(__sanitizer_get_total_unique_coverage);
- uintptr_t PrevCoverage = LastRecordedBlockCoverage;
- LastRecordedBlockCoverage = __sanitizer_get_total_unique_coverage();
-
- if (PrevCoverage == LastRecordedBlockCoverage || !Options.PrintNewCovPcs)
- return LastRecordedBlockCoverage;
-
- uintptr_t PrevBufferLen = LastCoveragePcBufferLen;
- uintptr_t *CoverageBuf;
- LastCoveragePcBufferLen = __sanitizer_get_coverage_pc_buffer(&CoverageBuf);
- assert(CoverageBuf);
- for (size_t i = PrevBufferLen; i < LastCoveragePcBufferLen; ++i) {
- Printf("0x%x\n", CoverageBuf[i]);
- }
-
- return LastRecordedBlockCoverage;
+ if (RunOne(Data, Size))
+ ReportNewCoverage({Data, Data + Size});
}
-size_t Fuzzer::RecordCallerCalleeCoverage() {
- if (!Options.UseIndirCalls)
- return 0;
- if (!__sanitizer_get_total_unique_caller_callee_pairs)
- return 0;
- return LastRecordedCallerCalleeCoverage =
- __sanitizer_get_total_unique_caller_callee_pairs();
+size_t Fuzzer::GetCurrentUnitInFuzzingThead(const uint8_t **Data) const {
+ assert(InFuzzingThread());
+ *Data = CurrentUnitData;
+ return CurrentUnitSize;
}
-void Fuzzer::PrepareCoverageBeforeRun() {
- if (Options.UseCounters) {
- size_t NumCounters = __sanitizer_get_number_of_counters();
- CounterBitmap.resize(NumCounters);
- __sanitizer_update_counter_bitset_and_clear_counters(0);
- }
- RecordBlockCoverage();
- RecordCallerCalleeCoverage();
+void Fuzzer::ExecuteCallback(const uint8_t *Data, size_t Size) {
+ assert(InFuzzingThread());
+ LazyAllocateCurrentUnitData();
+ UnitStartTime = system_clock::now();
+ // We copy the contents of Unit into a separate heap buffer
+ // so that we reliably find buffer overflows in it.
+ std::unique_ptr<uint8_t[]> DataCopy(new uint8_t[Size]);
+ memcpy(DataCopy.get(), Data, Size);
+ if (CurrentUnitData && CurrentUnitData != Data)
+ memcpy(CurrentUnitData, Data, Size);
+ AssignTaintLabels(DataCopy.get(), Size);
+ CurrentUnitSize = Size;
+ AllocTracer.Start();
+ int Res = CB(DataCopy.get(), Size);
+ (void)Res;
+ HasMoreMallocsThanFrees = AllocTracer.Stop();
+ CurrentUnitSize = 0;
+ assert(Res == 0);
}
-bool Fuzzer::CheckCoverageAfterRun() {
- size_t OldCoverage = LastRecordedBlockCoverage;
- size_t NewCoverage = RecordBlockCoverage();
- size_t OldCallerCalleeCoverage = LastRecordedCallerCalleeCoverage;
- size_t NewCallerCalleeCoverage = RecordCallerCalleeCoverage();
- size_t NumNewBits = 0;
- if (Options.UseCounters)
- NumNewBits = __sanitizer_update_counter_bitset_and_clear_counters(
- CounterBitmap.data());
- return NewCoverage > OldCoverage ||
- NewCallerCalleeCoverage > OldCallerCalleeCoverage || NumNewBits;
+std::string Fuzzer::Coverage::DebugString() const {
+ std::string Result =
+ std::string("Coverage{") + "BlockCoverage=" +
+ std::to_string(BlockCoverage) + " CallerCalleeCoverage=" +
+ std::to_string(CallerCalleeCoverage) + " CounterBitmapBits=" +
+ std::to_string(CounterBitmapBits) + " PcMapBits=" +
+ std::to_string(PcMapBits) + "}";
+ return Result;
}
void Fuzzer::WriteToOutputCorpus(const Unit &U) {
- if (Options.OutputCorpus.empty()) return;
+ 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());
- assert(!Options.OnlyASCII || IsASCII(U));
}
void Fuzzer::WriteUnitToFileWithPrefix(const Unit &U, const char *Prefix) {
@@ -314,7 +518,7 @@ void Fuzzer::WriteUnitToFileWithPrefix(const Unit &U, const char *Prefix) {
return;
std::string Path = Options.ArtifactPrefix + Prefix + Hash(U);
if (!Options.ExactArtifactPath.empty())
- Path = Options.ExactArtifactPath; // Overrides ArtifactPrefix.
+ 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());
@@ -323,7 +527,8 @@ void Fuzzer::WriteUnitToFileWithPrefix(const Unit &U, const char *Prefix) {
}
void Fuzzer::SaveCorpus() {
- if (Options.OutputCorpus.empty()) return;
+ if (Options.OutputCorpus.empty())
+ return;
for (const auto &U : Corpus)
WriteToFile(U, DirPlusFile(Options.OutputCorpus, Hash(U)));
if (Options.Verbosity)
@@ -337,18 +542,54 @@ void Fuzzer::PrintStatusForNewUnit(const Unit &U) {
PrintStats("NEW ", "");
if (Options.Verbosity) {
Printf(" L: %zd ", U.size());
- USF.PrintMutationSequence();
+ MD.PrintMutationSequence();
Printf("\n");
}
}
void Fuzzer::ReportNewCoverage(const Unit &U) {
Corpus.push_back(U);
+ UpdateCorpusDistribution();
UnitHashesAddedToCorpus.insert(Hash(U));
+ MD.RecordSuccessfulMutationSequence();
PrintStatusForNewUnit(U);
WriteToOutputCorpus(U);
- if (Options.ExitOnFirst)
- exit(0);
+ NumberOfNewUnitsAdded++;
+}
+
+// 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;
+ size_t OldSize = Res.size();
+ for (int Iter = 0; Iter < 10; Iter++) {
+ ShuffleCorpus(&Res);
+ ResetCoverage();
+
+ for (auto &U : Initial)
+ RunOne(U);
+
+ Corpus.clear();
+ for (auto &U : Res)
+ if (RunOne(U))
+ Corpus.push_back(U);
+
+ char Stat[7] = "MIN ";
+ Stat[3] = '0' + Iter;
+ PrintStats(Stat);
+
+ size_t NewSize = Corpus.size();
+ assert(NewSize <= OldSize);
+ Res.swap(Corpus);
+
+ if (NewSize + 5 >= OldSize)
+ break;
+ OldSize = NewSize;
+ }
+ return Res;
}
void Fuzzer::Merge(const std::vector<std::string> &Corpora) {
@@ -356,72 +597,105 @@ void Fuzzer::Merge(const std::vector<std::string> &Corpora) {
Printf("Merge requires two or more corpus dirs\n");
return;
}
- auto InitialCorpusDir = Corpora[0];
- ReadDir(InitialCorpusDir, nullptr);
- Printf("Merge: running the initial corpus '%s' of %d units\n",
- InitialCorpusDir.c_str(), Corpus.size());
- for (auto &U : Corpus)
- RunOne(U);
-
std::vector<std::string> ExtraCorpora(Corpora.begin() + 1, Corpora.end());
- size_t NumTried = 0;
- size_t NumMerged = 0;
- for (auto &C : ExtraCorpora) {
- Corpus.clear();
- ReadDir(C, nullptr);
- Printf("Merge: merging the extra corpus '%s' of %zd units\n", C.c_str(),
- Corpus.size());
- for (auto &U : Corpus) {
- NumTried++;
- if (RunOne(U)) {
- WriteToOutputCorpus(U);
- NumMerged++;
- }
- }
+ assert(Options.MaxLen > 0);
+ UnitVector Initial, Extra;
+ ReadDirToVectorOfUnits(Corpora[0].c_str(), &Initial, nullptr, Options.MaxLen);
+ for (auto &C : ExtraCorpora)
+ ReadDirToVectorOfUnits(C.c_str(), &Extra, nullptr, Options.MaxLen);
+
+ 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();
+ RunOne(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"
+ " 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.
}
- Printf("Merge: written %zd out of %zd units\n", NumMerged, NumTried);
}
void Fuzzer::MutateAndTestOne() {
- auto &U = CurrentUnit;
- USF.StartMutationSequence();
+ LazyAllocateCurrentUnitData();
+ MD.StartMutationSequence();
- U = ChooseUnitToMutate();
+ auto &U = ChooseUnitToMutate();
+ assert(CurrentUnitData);
+ size_t Size = U.size();
+ assert(Size <= Options.MaxLen && "Oversized Unit");
+ memcpy(CurrentUnitData, U.data(), Size);
for (int i = 0; i < Options.MutateDepth; i++) {
- size_t Size = U.size();
- U.resize(Options.MaxLen);
- size_t NewSize = USF.Mutate(U.data(), Size, U.size());
+ size_t NewSize = 0;
+ NewSize = MD.Mutate(CurrentUnitData, Size, Options.MaxLen);
assert(NewSize > 0 && "Mutator returned empty unit");
- assert(NewSize <= (size_t)Options.MaxLen &&
+ assert(NewSize <= Options.MaxLen &&
"Mutator return overisized unit");
- U.resize(NewSize);
+ Size = NewSize;
if (i == 0)
StartTraceRecording();
- RunOneAndUpdateCorpus(U);
+ RunOneAndUpdateCorpus(CurrentUnitData, Size);
StopTraceRecording();
+ TryDetectingAMemoryLeak(CurrentUnitData, Size,
+ /*DuringInitialCorpusExecution*/ false);
}
}
// Returns an index of random unit from the corpus to mutate.
// Hypothesis: units added to the corpus last are more likely to be interesting.
-// This function gives more wieght to the more recent units.
+// This function gives more weight to the more recent units.
size_t Fuzzer::ChooseUnitIdxToMutate() {
- size_t N = Corpus.size();
- size_t Total = (N + 1) * N / 2;
- size_t R = USF.GetRand()(Total);
- size_t IdxBeg = 0, IdxEnd = N;
- // Binary search.
- while (IdxEnd - IdxBeg >= 2) {
- size_t Idx = IdxBeg + (IdxEnd - IdxBeg) / 2;
- if (R > (Idx + 1) * Idx / 2)
- IdxBeg = Idx;
- else
- IdxEnd = Idx;
- }
- assert(IdxBeg < N);
- return IdxBeg;
+ size_t Idx =
+ static_cast<size_t>(CorpusDistribution(MD.GetRand().Get_mt19937()));
+ assert(Idx < Corpus.size());
+ return Idx;
+}
+
+void Fuzzer::ResetCoverage() {
+ CoverageController::Reset();
+ MaxCoverage.Reset();
+ CoverageController::Prepare(Options, &MaxCoverage);
}
// Experimental search heuristic: drilling.
@@ -434,16 +708,16 @@ size_t Fuzzer::ChooseUnitIdxToMutate() {
void Fuzzer::Drill() {
// The corpus is already read, shuffled, and minimized.
assert(!Corpus.empty());
- Options.PrintNEW = false; // Don't print NEW status lines when drilling.
+ Options.PrintNEW = false; // Don't print NEW status lines when drilling.
Unit U = ChooseUnitToMutate();
- CHECK_WEAK_API_FUNCTION(__sanitizer_reset_coverage);
- __sanitizer_reset_coverage();
+ ResetCoverage();
std::vector<Unit> SavedCorpus;
SavedCorpus.swap(Corpus);
Corpus.push_back(U);
+ UpdateCorpusDistribution();
assert(Corpus.size() == 1);
RunOne(U);
PrintStats("DRILL ");
@@ -451,19 +725,16 @@ void Fuzzer::Drill() {
SavedOutputCorpusPath.swap(Options.OutputCorpus);
Loop();
- __sanitizer_reset_coverage();
+ ResetCoverage();
PrintStats("REINIT");
SavedOutputCorpusPath.swap(Options.OutputCorpus);
- for (auto &U : SavedCorpus) {
- CurrentUnit = U;
+ for (auto &U : SavedCorpus)
RunOne(U);
- }
PrintStats("MERGE ");
Options.PrintNEW = true;
size_t NumMerged = 0;
for (auto &U : Corpus) {
- CurrentUnit = U;
if (RunOne(U)) {
PrintStatusForNewUnit(U);
NumMerged++;
@@ -478,35 +749,43 @@ void Fuzzer::Drill() {
void Fuzzer::Loop() {
system_clock::time_point LastCorpusReload = system_clock::now();
if (Options.DoCrossOver)
- USF.SetCorpus(&Corpus);
+ MD.SetCorpus(&Corpus);
while (true) {
- SyncCorpus();
auto Now = system_clock::now();
if (duration_cast<seconds>(Now - LastCorpusReload).count()) {
- RereadOutputCorpus();
+ RereadOutputCorpus(Options.MaxLen);
LastCorpusReload = Now;
}
if (TotalNumberOfRuns >= Options.MaxNumberOfRuns)
break;
if (Options.MaxTotalTimeSec > 0 &&
secondsSinceProcessStartUp() >
- static_cast<size_t>(Options.MaxTotalTimeSec))
+ static_cast<size_t>(Options.MaxTotalTimeSec))
break;
// Perform several mutations and runs.
MutateAndTestOne();
}
PrintStats("DONE ", "\n");
+ MD.PrintRecommendedDictionary();
}
-void Fuzzer::SyncCorpus() {
- if (Options.SyncCommand.empty() || Options.OutputCorpus.empty()) return;
- auto Now = system_clock::now();
- if (duration_cast<seconds>(Now - LastExternalSync).count() <
- Options.SyncTimeout)
- return;
- LastExternalSync = Now;
- ExecuteCommand(Options.SyncCommand + " " + Options.OutputCorpus);
+void Fuzzer::UpdateCorpusDistribution() {
+ size_t N = Corpus.size();
+ std::vector<double> Intervals(N + 1);
+ std::vector<double> Weights(N);
+ std::iota(Intervals.begin(), Intervals.end(), 0);
+ std::iota(Weights.begin(), Weights.end(), 1);
+ CorpusDistribution = std::piecewise_constant_distribution<double>(
+ Intervals.begin(), Intervals.end(), Weights.begin());
}
-} // namespace fuzzer
+} // 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"