2017-08-22 07:25:50 +08:00
|
|
|
//===- 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 "FuzzerIO.h"
|
|
|
|
#include "FuzzerInternal.h"
|
|
|
|
#include "FuzzerMutate.h"
|
|
|
|
#include "FuzzerRandom.h"
|
|
|
|
#include "FuzzerShmem.h"
|
|
|
|
#include "FuzzerTracePC.h"
|
|
|
|
#include <algorithm>
|
|
|
|
#include <cstring>
|
|
|
|
#include <memory>
|
2017-11-01 11:02:59 +08:00
|
|
|
#include <mutex>
|
2017-08-22 07:25:50 +08:00
|
|
|
#include <set>
|
|
|
|
|
|
|
|
#if defined(__has_include)
|
|
|
|
#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;
|
|
|
|
|
|
|
|
SharedMemoryRegion SMR;
|
|
|
|
|
2018-07-18 00:12:00 +08:00
|
|
|
bool RunningUserCallback = false;
|
|
|
|
|
2017-08-22 07:25:50 +08:00
|
|
|
// Only one Fuzzer per process.
|
|
|
|
static Fuzzer *F;
|
|
|
|
|
|
|
|
// 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;
|
2017-11-02 12:12:10 +08:00
|
|
|
|
|
|
|
std::recursive_mutex TraceMutex;
|
|
|
|
bool TraceDisabled = false;
|
2017-08-22 07:25:50 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
static MallocFreeTracer AllocTracer;
|
|
|
|
|
2017-11-02 12:12:10 +08:00
|
|
|
// Locks printing and avoids nested hooks triggered from mallocs/frees in
|
|
|
|
// sanitizer.
|
|
|
|
class TraceLock {
|
|
|
|
public:
|
|
|
|
TraceLock() : Lock(AllocTracer.TraceMutex) {
|
|
|
|
AllocTracer.TraceDisabled = !AllocTracer.TraceDisabled;
|
|
|
|
}
|
|
|
|
~TraceLock() { AllocTracer.TraceDisabled = !AllocTracer.TraceDisabled; }
|
|
|
|
|
|
|
|
bool IsDisabled() const {
|
|
|
|
// This is already inverted value.
|
|
|
|
return !AllocTracer.TraceDisabled;
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
std::lock_guard<std::recursive_mutex> Lock;
|
|
|
|
};
|
2017-11-01 11:02:59 +08:00
|
|
|
|
2017-08-22 07:25:50 +08:00
|
|
|
ATTRIBUTE_NO_SANITIZE_MEMORY
|
|
|
|
void MallocHook(const volatile void *ptr, size_t size) {
|
|
|
|
size_t N = AllocTracer.Mallocs++;
|
|
|
|
F->HandleMalloc(size);
|
|
|
|
if (int TraceLevel = AllocTracer.TraceLevel) {
|
2017-11-02 12:12:10 +08:00
|
|
|
TraceLock Lock;
|
|
|
|
if (Lock.IsDisabled())
|
|
|
|
return;
|
2017-08-22 07:25:50 +08:00
|
|
|
Printf("MALLOC[%zd] %p %zd\n", N, ptr, size);
|
|
|
|
if (TraceLevel >= 2 && EF)
|
2018-05-09 07:45:05 +08:00
|
|
|
PrintStackTrace();
|
2017-08-22 07:25:50 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ATTRIBUTE_NO_SANITIZE_MEMORY
|
|
|
|
void FreeHook(const volatile void *ptr) {
|
|
|
|
size_t N = AllocTracer.Frees++;
|
|
|
|
if (int TraceLevel = AllocTracer.TraceLevel) {
|
2017-11-02 12:12:10 +08:00
|
|
|
TraceLock Lock;
|
|
|
|
if (Lock.IsDisabled())
|
|
|
|
return;
|
2017-08-22 07:25:50 +08:00
|
|
|
Printf("FREE[%zd] %p\n", N, ptr);
|
|
|
|
if (TraceLevel >= 2 && EF)
|
2018-05-09 07:45:05 +08:00
|
|
|
PrintStackTrace();
|
2017-08-22 07:25:50 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Crash on a single malloc that exceeds the rss limit.
|
|
|
|
void Fuzzer::HandleMalloc(size_t Size) {
|
2017-12-02 06:12:04 +08:00
|
|
|
if (!Options.MallocLimitMb || (Size >> 20) < (size_t)Options.MallocLimitMb)
|
2017-08-22 07:25:50 +08:00
|
|
|
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");
|
2018-05-09 07:45:05 +08:00
|
|
|
PrintStackTrace();
|
2017-08-22 07:25:50 +08:00
|
|
|
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) {
|
|
|
|
if (EF->__sanitizer_set_death_callback)
|
|
|
|
EF->__sanitizer_set_death_callback(StaticDeathCallback);
|
|
|
|
assert(!F);
|
|
|
|
F = this;
|
|
|
|
TPC.ResetMaps();
|
|
|
|
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);
|
2018-07-04 06:33:09 +08:00
|
|
|
TPC.SetUseValueProfileMask(Options.UseValueProfile);
|
2017-08-22 07:25:50 +08:00
|
|
|
|
|
|
|
if (Options.Verbosity)
|
|
|
|
TPC.PrintModuleInfo();
|
|
|
|
if (!Options.OutputCorpus.empty() && Options.ReloadIntervalSec)
|
|
|
|
EpochOfLastReadOfOutputCorpus = GetEpoch(Options.OutputCorpus);
|
|
|
|
MaxInputLen = MaxMutationLen = Options.MaxLen;
|
|
|
|
TmpMaxMutationLen = Max(size_t(4), Corpus.MaxInputSize());
|
|
|
|
AllocateCurrentUnitData();
|
|
|
|
CurrentUnitSize = 0;
|
|
|
|
memset(BaseSha1, 0, sizeof(BaseSha1));
|
2018-05-17 07:26:37 +08:00
|
|
|
TPC.SetFocusFunction(Options.FocusFunction);
|
2018-06-06 09:23:29 +08:00
|
|
|
DFT.Init(Options.DataFlowTrace, Options.FocusFunction);
|
2017-08-22 07:25:50 +08:00
|
|
|
}
|
|
|
|
|
2017-10-24 07:24:33 +08:00
|
|
|
Fuzzer::~Fuzzer() {}
|
2017-08-22 07:25:50 +08:00
|
|
|
|
|
|
|
void Fuzzer::AllocateCurrentUnitData() {
|
2017-10-24 07:24:33 +08:00
|
|
|
if (CurrentUnitData || MaxInputLen == 0)
|
|
|
|
return;
|
2017-08-22 07:25:50 +08:00
|
|
|
CurrentUnitData = new uint8_t[MaxInputLen];
|
|
|
|
}
|
|
|
|
|
|
|
|
void Fuzzer::StaticDeathCallback() {
|
|
|
|
assert(F);
|
|
|
|
F->DeathCallback();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Fuzzer::DumpCurrentUnit(const char *Prefix) {
|
2017-10-24 07:24:33 +08:00
|
|
|
if (!CurrentUnitData)
|
|
|
|
return; // Happens when running individual inputs.
|
2018-07-10 07:51:08 +08:00
|
|
|
ScopedDisableMsanInterceptorChecks S;
|
2017-08-22 07:25:50 +08:00
|
|
|
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::StaticExitCallback() {
|
|
|
|
assert(F);
|
|
|
|
F->ExitCallback();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Fuzzer::StaticInterruptCallback() {
|
|
|
|
assert(F);
|
|
|
|
F->InterruptCallback();
|
|
|
|
}
|
|
|
|
|
2017-11-10 04:30:19 +08:00
|
|
|
void Fuzzer::StaticGracefulExitCallback() {
|
|
|
|
assert(F);
|
|
|
|
F->GracefulExitRequested = true;
|
|
|
|
Printf("INFO: signal received, trying to exit gracefully\n");
|
|
|
|
}
|
|
|
|
|
2017-08-22 07:25:50 +08:00
|
|
|
void Fuzzer::StaticFileSizeExceedCallback() {
|
|
|
|
Printf("==%lu== ERROR: libFuzzer: file size exceeded\n", GetPid());
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Fuzzer::CrashCallback() {
|
2018-05-02 10:55:28 +08:00
|
|
|
if (EF->__sanitizer_acquire_crash_state)
|
|
|
|
EF->__sanitizer_acquire_crash_state();
|
2017-08-22 07:25:50 +08:00
|
|
|
Printf("==%lu== ERROR: libFuzzer: deadly signal\n", GetPid());
|
2018-05-09 07:45:05 +08:00
|
|
|
PrintStackTrace();
|
2017-08-22 07:25:50 +08:00
|
|
|
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();
|
2017-10-24 07:24:33 +08:00
|
|
|
_Exit(Options.ErrorExitCode); // Stop right now.
|
2017-08-22 07:25:50 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void Fuzzer::ExitCallback() {
|
2018-07-18 00:12:00 +08:00
|
|
|
if (!RunningUserCallback)
|
2017-08-22 07:25:50 +08:00
|
|
|
return; // This exit did not come from the user callback
|
2018-05-02 05:01:53 +08:00
|
|
|
if (EF->__sanitizer_acquire_crash_state &&
|
|
|
|
!EF->__sanitizer_acquire_crash_state())
|
|
|
|
return;
|
2017-08-22 07:25:50 +08:00
|
|
|
Printf("==%lu== ERROR: libFuzzer: fuzz target exited\n", GetPid());
|
2018-05-09 07:45:05 +08:00
|
|
|
PrintStackTrace();
|
2017-08-22 07:25:50 +08:00
|
|
|
Printf("SUMMARY: libFuzzer: fuzz target exited\n");
|
|
|
|
DumpCurrentUnit("crash-");
|
|
|
|
PrintFinalStats();
|
|
|
|
_Exit(Options.ErrorExitCode);
|
|
|
|
}
|
|
|
|
|
2017-11-10 04:30:19 +08:00
|
|
|
void Fuzzer::MaybeExitGracefully() {
|
|
|
|
if (!GracefulExitRequested) return;
|
|
|
|
Printf("==%lu== INFO: libFuzzer: exiting as requested\n", GetPid());
|
|
|
|
PrintFinalStats();
|
|
|
|
_Exit(0);
|
|
|
|
}
|
|
|
|
|
2017-08-22 07:25:50 +08:00
|
|
|
void Fuzzer::InterruptCallback() {
|
|
|
|
Printf("==%lu== libFuzzer: run interrupted; exiting\n", GetPid());
|
|
|
|
PrintFinalStats();
|
2017-10-24 07:24:33 +08:00
|
|
|
_Exit(0); // Stop right now, don't perform any at-exit actions.
|
2017-08-22 07:25:50 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
NO_SANITIZE_MEMORY
|
|
|
|
void Fuzzer::AlarmCallback() {
|
|
|
|
assert(Options.UnitTimeoutSec > 0);
|
|
|
|
// In Windows Alarm callback is executed by a different thread.
|
2018-11-06 09:28:01 +08:00
|
|
|
// NetBSD's current behavior needs this change too.
|
|
|
|
#if !LIBFUZZER_WINDOWS && !LIBFUZZER_NETBSD
|
2017-10-24 07:24:33 +08:00
|
|
|
if (!InFuzzingThread())
|
|
|
|
return;
|
2017-08-22 07:25:50 +08:00
|
|
|
#endif
|
2018-07-18 00:12:00 +08:00
|
|
|
if (!RunningUserCallback)
|
2017-08-22 07:25:50 +08:00
|
|
|
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) {
|
2018-05-02 05:01:53 +08:00
|
|
|
if (EF->__sanitizer_acquire_crash_state &&
|
|
|
|
!EF->__sanitizer_acquire_crash_state())
|
|
|
|
return;
|
2017-08-22 07:25:50 +08:00
|
|
|
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);
|
2018-05-09 07:45:05 +08:00
|
|
|
PrintStackTrace();
|
2017-08-22 07:25:50 +08:00
|
|
|
Printf("SUMMARY: libFuzzer: timeout\n");
|
|
|
|
PrintFinalStats();
|
|
|
|
_Exit(Options.TimeoutExitCode); // Stop right now.
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Fuzzer::RssLimitCallback() {
|
2018-05-02 05:01:53 +08:00
|
|
|
if (EF->__sanitizer_acquire_crash_state &&
|
|
|
|
!EF->__sanitizer_acquire_crash_state())
|
|
|
|
return;
|
2017-08-22 07:25:50 +08:00
|
|
|
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");
|
2018-05-09 07:45:05 +08:00
|
|
|
PrintMemoryProfile();
|
2017-08-22 07:25:50 +08:00
|
|
|
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.Verbosity)
|
|
|
|
return;
|
|
|
|
Printf("#%zd\t%s", TotalNumberOfRuns, Where);
|
|
|
|
if (size_t N = TPC.GetTotalPCCoverage())
|
|
|
|
Printf(" cov: %zd", N);
|
|
|
|
if (size_t N = Corpus.NumFeatures())
|
2017-10-24 07:24:33 +08:00
|
|
|
Printf(" ft: %zd", N);
|
2017-08-22 07:25:50 +08:00
|
|
|
if (!Corpus.empty()) {
|
|
|
|
Printf(" corp: %zd", Corpus.NumActiveUnits());
|
|
|
|
if (size_t N = Corpus.SizeInBytes()) {
|
2017-10-24 07:24:33 +08:00
|
|
|
if (N < (1 << 14))
|
2017-08-22 07:25:50 +08:00
|
|
|
Printf("/%zdb", N);
|
|
|
|
else if (N < (1 << 24))
|
|
|
|
Printf("/%zdKb", N >> 10);
|
|
|
|
else
|
|
|
|
Printf("/%zdMb", N >> 20);
|
|
|
|
}
|
2018-05-17 07:26:37 +08:00
|
|
|
if (size_t FF = Corpus.NumInputsThatTouchFocusFunction())
|
|
|
|
Printf(" focus: %zd", FF);
|
2017-08-22 07:25:50 +08:00
|
|
|
}
|
2018-02-23 03:00:17 +08:00
|
|
|
if (TmpMaxMutationLen)
|
|
|
|
Printf(" lim: %zd", TmpMaxMutationLen);
|
2017-08-22 07:25:50 +08:00
|
|
|
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();
|
[libFuzzer] Implement stat::stability_rate based on the percentage of unstable edges.
Summary:
Created a -print_unstable_stats flag.
When -print_unstable_stats=1, we run it 2 more times on interesting inputs poisoning unstable edges in an array.
On program termination, we run PrintUnstableStats() which will print a line with a stability percentage like AFL does.
Patch by Kyungtak Woo (@kevinwkt).
Reviewers: metzman, Dor1s, kcc, morehouse
Reviewed By: metzman, Dor1s, morehouse
Subscribers: delcypher, llvm-commits, #sanitizers, kcc, morehouse, Dor1s
Differential Revision: https://reviews.llvm.org/D49212
llvm-svn: 337187
2018-07-17 00:01:31 +08:00
|
|
|
if (Options.PrintUnstableStats)
|
|
|
|
TPC.PrintUnstableStats();
|
2018-05-22 03:47:00 +08:00
|
|
|
if (Options.DumpCoverage)
|
|
|
|
TPC.DumpCoverage();
|
2017-08-22 07:25:50 +08:00
|
|
|
if (Options.PrintCorpusStats)
|
|
|
|
Corpus.PrintStats();
|
2017-10-24 07:24:33 +08:00
|
|
|
if (!Options.PrintFinalStats)
|
|
|
|
return;
|
2017-08-22 07:25:50 +08:00
|
|
|
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; "
|
|
|
|
"libFuzzer will not generate inputs larger than %zd bytes\n",
|
|
|
|
MaxInputLen);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Fuzzer::SetMaxMutationLen(size_t MaxMutationLen) {
|
|
|
|
assert(MaxMutationLen && MaxMutationLen <= MaxInputLen);
|
|
|
|
this->MaxMutationLen = MaxMutationLen;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Fuzzer::CheckExitOnSrcPosOrItem() {
|
|
|
|
if (!Options.ExitOnSrcPos.empty()) {
|
2017-08-28 07:20:09 +08:00
|
|
|
static auto *PCsSet = new Set<uintptr_t>;
|
2017-08-22 07:25:50 +08:00
|
|
|
auto HandlePC = [&](uintptr_t PC) {
|
2017-10-24 07:24:33 +08:00
|
|
|
if (!PCsSet->insert(PC).second)
|
|
|
|
return;
|
2017-08-22 07:25:50 +08:00
|
|
|
std::string Descr = DescribePC("%F %L", PC + 1);
|
|
|
|
if (Descr.find(Options.ExitOnSrcPos) != std::string::npos) {
|
|
|
|
Printf("INFO: found line matching '%s', exiting.\n",
|
|
|
|
Options.ExitOnSrcPos.c_str());
|
|
|
|
_Exit(0);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
TPC.ForEachObservedPC(HandlePC);
|
|
|
|
}
|
|
|
|
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) {
|
2017-10-24 07:24:33 +08:00
|
|
|
if (Options.OutputCorpus.empty() || !Options.ReloadIntervalSec)
|
|
|
|
return;
|
2017-08-28 07:20:09 +08:00
|
|
|
Vector<Unit> AdditionalCorpus;
|
2017-08-22 07:25:50 +08:00
|
|
|
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 (RunOne(U.data(), U.size())) {
|
|
|
|
CheckExitOnSrcPosOrItem();
|
|
|
|
Reloaded = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (Reloaded)
|
|
|
|
PrintStats("RELOAD");
|
|
|
|
}
|
|
|
|
|
|
|
|
void Fuzzer::PrintPulseAndReportSlowInput(const uint8_t *Data, size_t Size) {
|
|
|
|
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-");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
[libFuzzer] Implement stat::stability_rate based on the percentage of unstable edges.
Summary:
Created a -print_unstable_stats flag.
When -print_unstable_stats=1, we run it 2 more times on interesting inputs poisoning unstable edges in an array.
On program termination, we run PrintUnstableStats() which will print a line with a stability percentage like AFL does.
Patch by Kyungtak Woo (@kevinwkt).
Reviewers: metzman, Dor1s, kcc, morehouse
Reviewed By: metzman, Dor1s, morehouse
Subscribers: delcypher, llvm-commits, #sanitizers, kcc, morehouse, Dor1s
Differential Revision: https://reviews.llvm.org/D49212
llvm-svn: 337187
2018-07-17 00:01:31 +08:00
|
|
|
void Fuzzer::CheckForUnstableCounters(const uint8_t *Data, size_t Size) {
|
|
|
|
auto CBSetupAndRun = [&]() {
|
|
|
|
ScopedEnableMsanInterceptorChecks S;
|
|
|
|
UnitStartTime = system_clock::now();
|
|
|
|
TPC.ResetMaps();
|
2018-07-18 00:12:00 +08:00
|
|
|
RunningUserCallback = true;
|
[libFuzzer] Implement stat::stability_rate based on the percentage of unstable edges.
Summary:
Created a -print_unstable_stats flag.
When -print_unstable_stats=1, we run it 2 more times on interesting inputs poisoning unstable edges in an array.
On program termination, we run PrintUnstableStats() which will print a line with a stability percentage like AFL does.
Patch by Kyungtak Woo (@kevinwkt).
Reviewers: metzman, Dor1s, kcc, morehouse
Reviewed By: metzman, Dor1s, morehouse
Subscribers: delcypher, llvm-commits, #sanitizers, kcc, morehouse, Dor1s
Differential Revision: https://reviews.llvm.org/D49212
llvm-svn: 337187
2018-07-17 00:01:31 +08:00
|
|
|
CB(Data, Size);
|
2018-07-18 00:12:00 +08:00
|
|
|
RunningUserCallback = false;
|
[libFuzzer] Implement stat::stability_rate based on the percentage of unstable edges.
Summary:
Created a -print_unstable_stats flag.
When -print_unstable_stats=1, we run it 2 more times on interesting inputs poisoning unstable edges in an array.
On program termination, we run PrintUnstableStats() which will print a line with a stability percentage like AFL does.
Patch by Kyungtak Woo (@kevinwkt).
Reviewers: metzman, Dor1s, kcc, morehouse
Reviewed By: metzman, Dor1s, morehouse
Subscribers: delcypher, llvm-commits, #sanitizers, kcc, morehouse, Dor1s
Differential Revision: https://reviews.llvm.org/D49212
llvm-svn: 337187
2018-07-17 00:01:31 +08:00
|
|
|
UnitStopTime = system_clock::now();
|
|
|
|
};
|
|
|
|
|
|
|
|
// Copy original run counters into our unstable counters
|
|
|
|
TPC.InitializeUnstableCounters();
|
|
|
|
|
|
|
|
// First Rerun
|
|
|
|
CBSetupAndRun();
|
2018-08-08 22:32:46 +08:00
|
|
|
if (TPC.UpdateUnstableCounters(Options.HandleUnstable)) {
|
|
|
|
// Second Rerun
|
|
|
|
CBSetupAndRun();
|
|
|
|
TPC.UpdateAndApplyUnstableCounters(Options.HandleUnstable);
|
|
|
|
}
|
[libFuzzer] Implement stat::stability_rate based on the percentage of unstable edges.
Summary:
Created a -print_unstable_stats flag.
When -print_unstable_stats=1, we run it 2 more times on interesting inputs poisoning unstable edges in an array.
On program termination, we run PrintUnstableStats() which will print a line with a stability percentage like AFL does.
Patch by Kyungtak Woo (@kevinwkt).
Reviewers: metzman, Dor1s, kcc, morehouse
Reviewed By: metzman, Dor1s, morehouse
Subscribers: delcypher, llvm-commits, #sanitizers, kcc, morehouse, Dor1s
Differential Revision: https://reviews.llvm.org/D49212
llvm-svn: 337187
2018-07-17 00:01:31 +08:00
|
|
|
}
|
|
|
|
|
2017-08-22 07:25:50 +08:00
|
|
|
bool Fuzzer::RunOne(const uint8_t *Data, size_t Size, bool MayDeleteFile,
|
2017-12-02 03:18:38 +08:00
|
|
|
InputInfo *II, bool *FoundUniqFeatures) {
|
2017-10-24 07:24:33 +08:00
|
|
|
if (!Size)
|
|
|
|
return false;
|
2017-08-22 07:25:50 +08:00
|
|
|
|
|
|
|
ExecuteCallback(Data, Size);
|
|
|
|
|
|
|
|
UniqFeatureSetTmp.clear();
|
|
|
|
size_t FoundUniqFeaturesOfII = 0;
|
|
|
|
size_t NumUpdatesBefore = Corpus.NumFeatureUpdates();
|
[libFuzzer] Handle unstable edges by using minimum hit counts
Summary:
Created unstable_handle flag that takes 1 or 2, depending on the handling type.
Modified RunOne to accommodate the following heuristic:
Use the first CollectFeatures to count how many features there are.
If no new features, CollectFeatures like before.
If there is new feature, we run CB 2 more times,
Check which edges are unstable per input and we store the least amount of hit counts for each edge.
Apply these hit counts back to inline8bitcounters so that CollectFeatures can work as intended.
Modified UnstableCounters to 8int_t and created a bitset UnstableSet to tell which edges are unstable.
Patch by Kyungtak Woo (@kevinwkt).
Reviewers: Dor1s, metzman, morehouse
Reviewed By: Dor1s, morehouse
Subscribers: delcypher, #sanitizers, llvm-commits, kcc
Differential Revision: https://reviews.llvm.org/D49525
llvm-svn: 337696
2018-07-23 22:20:52 +08:00
|
|
|
bool NewFeaturesUnstable = false;
|
|
|
|
|
|
|
|
if (Options.HandleUnstable || Options.PrintUnstableStats) {
|
|
|
|
TPC.CollectFeatures([&](size_t Feature) {
|
|
|
|
if (Corpus.IsFeatureNew(Feature, Size, Options.Shrink))
|
|
|
|
NewFeaturesUnstable = true;
|
|
|
|
});
|
|
|
|
if (NewFeaturesUnstable)
|
|
|
|
CheckForUnstableCounters(Data, Size);
|
|
|
|
}
|
|
|
|
|
2017-08-22 07:25:50 +08:00
|
|
|
TPC.CollectFeatures([&](size_t Feature) {
|
|
|
|
if (Corpus.AddFeature(Feature, Size, Options.Shrink))
|
|
|
|
UniqFeatureSetTmp.push_back(Feature);
|
|
|
|
if (Options.ReduceInputs && II)
|
|
|
|
if (std::binary_search(II->UniqFeatureSet.begin(),
|
|
|
|
II->UniqFeatureSet.end(), Feature))
|
|
|
|
FoundUniqFeaturesOfII++;
|
|
|
|
});
|
[libFuzzer] Handle unstable edges by using minimum hit counts
Summary:
Created unstable_handle flag that takes 1 or 2, depending on the handling type.
Modified RunOne to accommodate the following heuristic:
Use the first CollectFeatures to count how many features there are.
If no new features, CollectFeatures like before.
If there is new feature, we run CB 2 more times,
Check which edges are unstable per input and we store the least amount of hit counts for each edge.
Apply these hit counts back to inline8bitcounters so that CollectFeatures can work as intended.
Modified UnstableCounters to 8int_t and created a bitset UnstableSet to tell which edges are unstable.
Patch by Kyungtak Woo (@kevinwkt).
Reviewers: Dor1s, metzman, morehouse
Reviewed By: Dor1s, morehouse
Subscribers: delcypher, #sanitizers, llvm-commits, kcc
Differential Revision: https://reviews.llvm.org/D49525
llvm-svn: 337696
2018-07-23 22:20:52 +08:00
|
|
|
|
2017-12-02 03:18:38 +08:00
|
|
|
if (FoundUniqFeatures)
|
|
|
|
*FoundUniqFeatures = FoundUniqFeaturesOfII;
|
2017-08-22 07:25:50 +08:00
|
|
|
PrintPulseAndReportSlowInput(Data, Size);
|
|
|
|
size_t NumNewFeatures = Corpus.NumFeatureUpdates() - NumUpdatesBefore;
|
[libFuzzer] Implement stat::stability_rate based on the percentage of unstable edges.
Summary:
Created a -print_unstable_stats flag.
When -print_unstable_stats=1, we run it 2 more times on interesting inputs poisoning unstable edges in an array.
On program termination, we run PrintUnstableStats() which will print a line with a stability percentage like AFL does.
Patch by Kyungtak Woo (@kevinwkt).
Reviewers: metzman, Dor1s, kcc, morehouse
Reviewed By: metzman, Dor1s, morehouse
Subscribers: delcypher, llvm-commits, #sanitizers, kcc, morehouse, Dor1s
Differential Revision: https://reviews.llvm.org/D49212
llvm-svn: 337187
2018-07-17 00:01:31 +08:00
|
|
|
|
2017-08-22 07:25:50 +08:00
|
|
|
if (NumNewFeatures) {
|
|
|
|
TPC.UpdateObservedPCs();
|
|
|
|
Corpus.AddToCorpus({Data, Data + Size}, NumNewFeatures, MayDeleteFile,
|
2018-07-19 09:23:32 +08:00
|
|
|
TPC.ObservedFocusFunction(), UniqFeatureSetTmp, DFT, II);
|
2017-08-22 07:25:50 +08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (II && FoundUniqFeaturesOfII &&
|
2018-06-07 09:40:20 +08:00
|
|
|
II->DataFlowTraceForFocusFunction.empty() &&
|
2017-08-22 07:25:50 +08:00
|
|
|
FoundUniqFeaturesOfII == II->UniqFeatureSet.size() &&
|
|
|
|
II->U.size() > Size) {
|
|
|
|
Corpus.Replace(II, {Data, Data + Size});
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t Fuzzer::GetCurrentUnitInFuzzingThead(const uint8_t **Data) const {
|
|
|
|
assert(InFuzzingThread());
|
|
|
|
*Data = CurrentUnitData;
|
|
|
|
return CurrentUnitSize;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Fuzzer::CrashOnOverwrittenData() {
|
|
|
|
Printf("==%d== ERROR: libFuzzer: fuzz target overwrites it's const input\n",
|
|
|
|
GetPid());
|
|
|
|
DumpCurrentUnit("crash-");
|
|
|
|
Printf("SUMMARY: libFuzzer: out-of-memory\n");
|
|
|
|
_Exit(Options.ErrorExitCode); // Stop right now.
|
|
|
|
}
|
|
|
|
|
|
|
|
// Compare two arrays, but not all bytes if the arrays are large.
|
|
|
|
static bool 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);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Fuzzer::ExecuteCallback(const uint8_t *Data, size_t Size) {
|
|
|
|
TPC.RecordInitialStack();
|
|
|
|
TotalNumberOfRuns++;
|
|
|
|
assert(InFuzzingThread());
|
|
|
|
if (SMR.IsClient())
|
|
|
|
SMR.WriteByteArray(Data, Size);
|
|
|
|
// 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);
|
2018-07-10 07:51:08 +08:00
|
|
|
if (EF->__msan_unpoison)
|
|
|
|
EF->__msan_unpoison(DataCopy, Size);
|
2017-08-22 07:25:50 +08:00
|
|
|
if (CurrentUnitData && CurrentUnitData != Data)
|
|
|
|
memcpy(CurrentUnitData, Data, Size);
|
|
|
|
CurrentUnitSize = Size;
|
2018-07-10 07:51:08 +08:00
|
|
|
{
|
|
|
|
ScopedEnableMsanInterceptorChecks S;
|
|
|
|
AllocTracer.Start(Options.TraceMalloc);
|
|
|
|
UnitStartTime = system_clock::now();
|
|
|
|
TPC.ResetMaps();
|
2018-07-18 00:12:00 +08:00
|
|
|
RunningUserCallback = true;
|
2018-07-10 07:51:08 +08:00
|
|
|
int Res = CB(DataCopy, Size);
|
2018-07-18 00:12:00 +08:00
|
|
|
RunningUserCallback = false;
|
2018-07-10 07:51:08 +08:00
|
|
|
UnitStopTime = system_clock::now();
|
|
|
|
(void)Res;
|
|
|
|
assert(Res == 0);
|
|
|
|
HasMoreMallocsThanFrees = AllocTracer.Stop();
|
|
|
|
}
|
2017-08-22 07:25:50 +08:00
|
|
|
if (!LooseMemeq(DataCopy, Data, Size))
|
|
|
|
CrashOnOverwrittenData();
|
|
|
|
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 %zd bytes to %s\n", U.size(), 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, const char *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();
|
2017-10-24 07:24:33 +08:00
|
|
|
PrintStatusForNewUnit(U, II->Reduced ? "REDUCE" : "NEW ");
|
2017-08-22 07:25:50 +08:00
|
|
|
WriteToOutputCorpus(U);
|
|
|
|
NumberOfNewUnitsAdded++;
|
2017-10-24 07:24:33 +08:00
|
|
|
CheckExitOnSrcPosOrItem(); // Check only after the unit is saved to corpus.
|
2017-08-22 07:25:50 +08:00
|
|
|
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) {
|
2017-10-24 07:24:33 +08:00
|
|
|
if (!HasMoreMallocsThanFrees)
|
|
|
|
return; // mallocs==frees, a leak is unlikely.
|
|
|
|
if (!Options.DetectLeaks)
|
|
|
|
return;
|
2017-09-12 10:01:54 +08:00
|
|
|
if (!DuringInitialCorpusExecution &&
|
2017-10-24 07:24:33 +08:00
|
|
|
TotalNumberOfRuns >= Options.MaxNumberOfRuns)
|
|
|
|
return;
|
2017-08-22 07:25:50 +08:00
|
|
|
if (!&(EF->__lsan_enable) || !&(EF->__lsan_disable) ||
|
|
|
|
!(EF->__lsan_do_recoverable_leak_check))
|
2017-10-24 07:24:33 +08:00
|
|
|
return; // No lsan.
|
2017-08-22 07:25:50 +08:00
|
|
|
// 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();
|
2017-10-24 07:24:33 +08:00
|
|
|
if (!HasMoreMallocsThanFrees)
|
|
|
|
return; // a leak is unlikely.
|
2017-08-22 07:25:50 +08:00
|
|
|
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();
|
2017-10-24 07:24:33 +08:00
|
|
|
_Exit(Options.ErrorExitCode); // not exit() to disable lsan further on.
|
2017-08-22 07:25:50 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
size_t CurrentMaxMutationLen =
|
|
|
|
Min(MaxMutationLen, Max(U.size(), TmpMaxMutationLen));
|
|
|
|
assert(CurrentMaxMutationLen > 0);
|
|
|
|
|
|
|
|
for (int i = 0; i < Options.MutateDepth; i++) {
|
|
|
|
if (TotalNumberOfRuns >= Options.MaxNumberOfRuns)
|
|
|
|
break;
|
2017-11-10 04:30:19 +08:00
|
|
|
MaybeExitGracefully();
|
2017-08-22 07:25:50 +08:00
|
|
|
size_t NewSize = 0;
|
2018-07-19 09:23:32 +08:00
|
|
|
if (II.HasFocusFunction && !II.DataFlowTraceForFocusFunction.empty() &&
|
|
|
|
Size <= CurrentMaxMutationLen)
|
|
|
|
NewSize = MD.MutateWithMask(CurrentUnitData, Size, Size,
|
|
|
|
II.DataFlowTraceForFocusFunction);
|
|
|
|
else
|
|
|
|
NewSize = MD.Mutate(CurrentUnitData, Size, CurrentMaxMutationLen);
|
2017-08-22 07:25:50 +08:00
|
|
|
assert(NewSize > 0 && "Mutator returned empty unit");
|
2017-10-24 06:04:30 +08:00
|
|
|
assert(NewSize <= CurrentMaxMutationLen && "Mutator return oversized unit");
|
2017-08-22 07:25:50 +08:00
|
|
|
Size = NewSize;
|
|
|
|
II.NumExecutedMutations++;
|
|
|
|
|
2017-12-02 03:18:38 +08:00
|
|
|
bool FoundUniqFeatures = false;
|
|
|
|
bool NewCov = RunOne(CurrentUnitData, Size, /*MayDeleteFile=*/true, &II,
|
|
|
|
&FoundUniqFeatures);
|
2017-08-22 07:25:50 +08:00
|
|
|
TryDetectingAMemoryLeak(CurrentUnitData, Size,
|
|
|
|
/*DuringInitialCorpusExecution*/ false);
|
2017-12-02 03:18:38 +08:00
|
|
|
if (NewCov) {
|
2017-11-10 04:44:08 +08:00
|
|
|
ReportNewCoverage(&II, {CurrentUnitData, CurrentUnitData + Size});
|
2017-12-02 03:18:38 +08:00
|
|
|
break; // We will mutate this input more in the next rounds.
|
|
|
|
}
|
|
|
|
if (Options.ReduceDepth && !FoundUniqFeatures)
|
2018-07-16 23:15:34 +08:00
|
|
|
break;
|
2017-08-22 07:25:50 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-24 06:04:30 +08:00
|
|
|
void Fuzzer::PurgeAllocator() {
|
2017-10-24 07:24:33 +08:00
|
|
|
if (Options.PurgeAllocatorIntervalSec < 0 || !EF->__sanitizer_purge_allocator)
|
2017-10-24 06:04:30 +08:00
|
|
|
return;
|
|
|
|
if (duration_cast<seconds>(system_clock::now() -
|
2017-10-24 07:24:33 +08:00
|
|
|
LastAllocatorPurgeAttemptTime)
|
|
|
|
.count() < Options.PurgeAllocatorIntervalSec)
|
2017-10-24 06:04:30 +08:00
|
|
|
return;
|
|
|
|
|
|
|
|
if (Options.RssLimitMb <= 0 ||
|
2017-10-24 07:24:33 +08:00
|
|
|
GetPeakRSSMb() > static_cast<size_t>(Options.RssLimitMb) / 2)
|
2017-10-24 06:04:30 +08:00
|
|
|
EF->__sanitizer_purge_allocator();
|
|
|
|
|
|
|
|
LastAllocatorPurgeAttemptTime = system_clock::now();
|
|
|
|
}
|
|
|
|
|
2017-08-29 10:05:01 +08:00
|
|
|
void Fuzzer::ReadAndExecuteSeedCorpora(const Vector<std::string> &CorpusDirs) {
|
|
|
|
const size_t kMaxSaneLen = 1 << 20;
|
|
|
|
const size_t kMinDefaultLen = 4096;
|
2017-08-30 04:51:24 +08:00
|
|
|
Vector<SizedFile> SizedFiles;
|
|
|
|
size_t MaxSize = 0;
|
|
|
|
size_t MinSize = -1;
|
|
|
|
size_t TotalSize = 0;
|
2017-09-13 05:58:07 +08:00
|
|
|
size_t LastNumFiles = 0;
|
2017-08-30 04:51:24 +08:00
|
|
|
for (auto &Dir : CorpusDirs) {
|
2017-09-13 05:58:07 +08:00
|
|
|
GetSizedFilesFromDir(Dir, &SizedFiles);
|
|
|
|
Printf("INFO: % 8zd files found in %s\n", SizedFiles.size() - LastNumFiles,
|
|
|
|
Dir.c_str());
|
|
|
|
LastNumFiles = SizedFiles.size();
|
|
|
|
}
|
|
|
|
for (auto &File : SizedFiles) {
|
|
|
|
MaxSize = Max(File.Size, MaxSize);
|
|
|
|
MinSize = Min(File.Size, MinSize);
|
|
|
|
TotalSize += File.Size;
|
2017-08-29 10:05:01 +08:00
|
|
|
}
|
2017-08-30 04:51:24 +08:00
|
|
|
if (Options.MaxLen == 0)
|
|
|
|
SetMaxInputLen(std::min(std::max(kMinDefaultLen, MaxSize), kMaxSaneLen));
|
|
|
|
assert(MaxInputLen > 0);
|
|
|
|
|
2017-10-13 09:12:23 +08:00
|
|
|
// Test the callback with empty input and never try it again.
|
|
|
|
uint8_t dummy = 0;
|
|
|
|
ExecuteCallback(&dummy, 0);
|
|
|
|
|
2017-08-30 04:51:24 +08:00
|
|
|
if (SizedFiles.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",
|
|
|
|
SizedFiles.size(), MinSize, MaxSize, TotalSize, GetPeakRSSMb());
|
|
|
|
if (Options.ShuffleAtStartUp)
|
|
|
|
std::shuffle(SizedFiles.begin(), SizedFiles.end(), MD.GetRand());
|
|
|
|
|
2017-09-13 05:58:07 +08:00
|
|
|
if (Options.PreferSmall) {
|
|
|
|
std::stable_sort(SizedFiles.begin(), SizedFiles.end());
|
|
|
|
assert(SizedFiles.front().Size <= SizedFiles.back().Size);
|
|
|
|
}
|
2017-08-30 04:51:24 +08:00
|
|
|
|
|
|
|
// Load and execute inputs one by one.
|
|
|
|
for (auto &SF : SizedFiles) {
|
2017-09-01 03:17:15 +08:00
|
|
|
auto U = FileToVector(SF.File, MaxInputLen, /*ExitOnError=*/false);
|
2017-08-30 04:51:24 +08:00
|
|
|
assert(U.size() <= MaxInputLen);
|
|
|
|
RunOne(U.data(), U.size());
|
|
|
|
CheckExitOnSrcPosOrItem();
|
|
|
|
TryDetectingAMemoryLeak(U.data(), U.size(),
|
|
|
|
/*DuringInitialCorpusExecution*/ true);
|
|
|
|
}
|
2017-08-29 10:05:01 +08:00
|
|
|
}
|
|
|
|
|
2017-08-30 04:51:24 +08:00
|
|
|
PrintStats("INITED");
|
2018-05-17 07:26:37 +08:00
|
|
|
if (!Options.FocusFunction.empty())
|
|
|
|
Printf("INFO: %zd/%zd inputs touch the focus function\n",
|
|
|
|
Corpus.NumInputsThatTouchFocusFunction(), Corpus.size());
|
2018-06-07 09:40:20 +08:00
|
|
|
if (!Options.DataFlowTrace.empty())
|
|
|
|
Printf("INFO: %zd/%zd inputs have the Data Flow Trace\n",
|
|
|
|
Corpus.NumInputsWithDataFlowTrace(), Corpus.size());
|
2018-05-17 07:26:37 +08:00
|
|
|
|
2018-05-24 03:42:30 +08:00
|
|
|
if (Corpus.empty() && Options.MaxNumberOfRuns) {
|
2017-08-30 04:51:24 +08:00
|
|
|
Printf("ERROR: no interesting inputs were found. "
|
|
|
|
"Is the code instrumented for coverage? Exiting.\n");
|
|
|
|
exit(1);
|
2017-08-29 10:05:01 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Fuzzer::Loop(const Vector<std::string> &CorpusDirs) {
|
|
|
|
ReadAndExecuteSeedCorpora(CorpusDirs);
|
2018-06-07 09:40:20 +08:00
|
|
|
DFT.Clear(); // No need for DFT any more.
|
2017-08-22 07:25:50 +08:00
|
|
|
TPC.SetPrintNewPCs(Options.PrintNewCovPcs);
|
2017-08-26 04:09:25 +08:00
|
|
|
TPC.SetPrintNewFuncs(Options.PrintNewCovFuncs);
|
2017-08-22 07:25:50 +08:00
|
|
|
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;
|
2017-10-24 07:24:33 +08:00
|
|
|
if (TimedOut())
|
|
|
|
break;
|
2017-08-22 07:25:50 +08:00
|
|
|
|
|
|
|
// Update TmpMaxMutationLen
|
2018-02-14 04:52:15 +08:00
|
|
|
if (Options.LenControl) {
|
2017-08-22 07:25:50 +08:00
|
|
|
if (TmpMaxMutationLen < MaxMutationLen &&
|
2017-12-13 07:11:28 +08:00
|
|
|
TotalNumberOfRuns - LastCorpusUpdateRun >
|
2018-02-14 04:52:15 +08:00
|
|
|
Options.LenControl * Log(TmpMaxMutationLen)) {
|
2017-08-22 07:25:50 +08:00
|
|
|
TmpMaxMutationLen =
|
2017-12-13 07:11:28 +08:00
|
|
|
Min(MaxMutationLen, TmpMaxMutationLen + Log(TmpMaxMutationLen));
|
|
|
|
LastCorpusUpdateRun = TotalNumberOfRuns;
|
2017-08-22 07:25:50 +08:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
TmpMaxMutationLen = MaxMutationLen;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Perform several mutations and runs.
|
|
|
|
MutateAndTestOne();
|
2017-10-24 06:04:30 +08:00
|
|
|
|
|
|
|
PurgeAllocator();
|
2017-08-22 07:25:50 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
PrintStats("DONE ", "\n");
|
|
|
|
MD.PrintRecommendedDictionary();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Fuzzer::MinimizeCrashLoop(const Unit &U) {
|
2017-10-24 07:24:33 +08:00
|
|
|
if (U.size() <= 1)
|
|
|
|
return;
|
2017-08-22 07:25:50 +08:00
|
|
|
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);
|
|
|
|
ExecuteCallback(CurrentUnitData, NewSize);
|
|
|
|
PrintPulseAndReportSlowInput(CurrentUnitData, NewSize);
|
|
|
|
TryDetectingAMemoryLeak(CurrentUnitData, NewSize,
|
|
|
|
/*DuringInitialCorpusExecution*/ false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Fuzzer::AnnounceOutput(const uint8_t *Data, size_t Size) {
|
|
|
|
if (SMR.IsServer()) {
|
|
|
|
SMR.WriteByteArray(Data, Size);
|
|
|
|
} else if (SMR.IsClient()) {
|
|
|
|
SMR.PostClient();
|
|
|
|
SMR.WaitServer();
|
|
|
|
size_t OtherSize = SMR.ReadByteArraySize();
|
|
|
|
uint8_t *OtherData = SMR.GetByteArray();
|
|
|
|
if (Size != OtherSize || memcmp(Data, OtherData, Size) != 0) {
|
|
|
|
size_t i = 0;
|
|
|
|
for (i = 0; i < Min(Size, OtherSize); i++)
|
|
|
|
if (Data[i] != OtherData[i])
|
|
|
|
break;
|
|
|
|
Printf("==%lu== ERROR: libFuzzer: equivalence-mismatch. Sizes: %zd %zd; "
|
2017-10-24 07:24:33 +08:00
|
|
|
"offset %zd\n",
|
|
|
|
GetPid(), Size, OtherSize, i);
|
2017-08-22 07:25:50 +08:00
|
|
|
DumpCurrentUnit("mismatch-");
|
|
|
|
Printf("SUMMARY: libFuzzer: equivalence-mismatch\n");
|
|
|
|
PrintFinalStats();
|
|
|
|
_Exit(Options.ErrorExitCode);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace fuzzer
|
|
|
|
|
|
|
|
extern "C" {
|
|
|
|
|
2018-01-18 04:39:14 +08:00
|
|
|
__attribute__((visibility("default"))) size_t
|
|
|
|
LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize) {
|
2017-08-22 07:25:50 +08:00
|
|
|
assert(fuzzer::F);
|
|
|
|
return fuzzer::F->GetMD().DefaultMutate(Data, Size, MaxSize);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Experimental
|
2018-01-18 04:39:14 +08:00
|
|
|
__attribute__((visibility("default"))) void
|
|
|
|
LLVMFuzzerAnnounceOutput(const uint8_t *Data, size_t Size) {
|
2017-08-22 07:25:50 +08:00
|
|
|
assert(fuzzer::F);
|
|
|
|
fuzzer::F->AnnounceOutput(Data, Size);
|
|
|
|
}
|
2017-10-24 07:24:33 +08:00
|
|
|
} // extern "C"
|