2017-08-22 07:25:50 +08:00
|
|
|
//===- FuzzerLoop.cpp - Fuzzer's main loop --------------------------------===//
|
|
|
|
//
|
2019-01-19 16:50:56 +08:00
|
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
2017-08-22 07:25:50 +08:00
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Fuzzer's main loop.
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
#include "FuzzerCorpus.h"
|
|
|
|
#include "FuzzerIO.h"
|
|
|
|
#include "FuzzerInternal.h"
|
|
|
|
#include "FuzzerMutate.h"
|
2020-07-15 06:25:51 +08:00
|
|
|
#include "FuzzerPlatform.h"
|
2017-08-22 07:25:50 +08:00
|
|
|
#include "FuzzerRandom.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;
|
|
|
|
|
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();
|
2019-02-09 08:16:21 +08:00
|
|
|
_Exit(Options.OOMExitCode); // Stop right now.
|
2017-08-22 07:25:50 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
2019-02-16 09:23:41 +08:00
|
|
|
TmpMaxMutationLen = 0; // Will be set once we load the corpus.
|
2017-08-22 07:25:50 +08:00
|
|
|
AllocateCurrentUnitData();
|
|
|
|
CurrentUnitSize = 0;
|
|
|
|
memset(BaseSha1, 0, sizeof(BaseSha1));
|
|
|
|
}
|
|
|
|
|
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;
|
2021-05-27 06:14:37 +08:00
|
|
|
MD.PrintMutationSequence();
|
2017-08-22 07:25:50 +08:00
|
|
|
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() {
|
2019-01-31 09:24:01 +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: 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() {
|
2019-02-09 05:27:23 +08:00
|
|
|
if (!F->GracefulExitRequested) return;
|
2017-11-10 04:30:19 +08:00
|
|
|
Printf("==%lu== INFO: libFuzzer: exiting as requested\n", GetPid());
|
2020-02-11 10:22:09 +08:00
|
|
|
RmDirRecursive(TempPath("FuzzWithFork", ".dir"));
|
2019-02-09 05:27:23 +08:00
|
|
|
F->PrintFinalStats();
|
2017-11-10 04:30:19 +08:00
|
|
|
_Exit(0);
|
|
|
|
}
|
|
|
|
|
2017-08-22 07:25:50 +08:00
|
|
|
void Fuzzer::InterruptCallback() {
|
|
|
|
Printf("==%lu== libFuzzer: run interrupted; exiting\n", GetPid());
|
|
|
|
PrintFinalStats();
|
2019-04-26 08:17:41 +08:00
|
|
|
ScopedDisableMsanInterceptorChecks S; // RmDirRecursive may call opendir().
|
2020-02-11 10:22:09 +08:00
|
|
|
RmDirRecursive(TempPath("FuzzWithFork", ".dir"));
|
2019-02-09 08:16:21 +08:00
|
|
|
// Stop right now, don't perform any at-exit actions.
|
|
|
|
_Exit(Options.InterruptExitCode);
|
2017-08-22 07:25:50 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
NO_SANITIZE_MEMORY
|
|
|
|
void Fuzzer::AlarmCallback() {
|
|
|
|
assert(Options.UnitTimeoutSec > 0);
|
2019-10-10 05:01:50 +08:00
|
|
|
// In Windows and Fuchsia, Alarm callback is executed by a different thread.
|
2018-11-06 09:28:01 +08:00
|
|
|
// NetBSD's current behavior needs this change too.
|
2019-10-10 05:01:50 +08:00
|
|
|
#if !LIBFUZZER_WINDOWS && !LIBFUZZER_NETBSD && !LIBFUZZER_FUCHSIA
|
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();
|
2019-02-09 08:16:21 +08:00
|
|
|
_Exit(Options.OOMExitCode); // Stop right now.
|
2017-08-22 07:25:50 +08:00
|
|
|
}
|
|
|
|
|
[libFuzzer] Merge: print feature coverage number as well.
Summary:
feature coverage is a useful signal that is available during the merge
process, but was not printed previously.
Output example:
```
$ ./fuzzer -use_value_profile=1 -merge=1 new_corpus/ seed_corpus/
INFO: Seed: 1676551929
INFO: Loaded 1 modules (2380 inline 8-bit counters): 2380 [0x90d180, 0x90dacc),
INFO: Loaded 1 PC tables (2380 PCs): 2380 [0x684018,0x68d4d8),
MERGE-OUTER: 180 files, 78 in the initial corpus
MERGE-OUTER: attempt 1
INFO: Seed: 1676574577
INFO: Loaded 1 modules (2380 inline 8-bit counters): 2380 [0x90d180, 0x90dacc),
INFO: Loaded 1 PC tables (2380 PCs): 2380 [0x684018,0x68d4d8),
INFO: -max_len is not provided; libFuzzer will not generate inputs larger than 1048576 bytes
MERGE-INNER: using the control file '/tmp/libFuzzerTemp.111754.txt'
MERGE-INNER: 180 total files; 0 processed earlier; will process 180 files now
#1 pulse cov: 134 ft: 330 exec/s: 0 rss: 37Mb
#2 pulse cov: 142 ft: 462 exec/s: 0 rss: 38Mb
#4 pulse cov: 152 ft: 651 exec/s: 0 rss: 38Mb
#8 pulse cov: 152 ft: 943 exec/s: 0 rss: 38Mb
#16 pulse cov: 520 ft: 2783 exec/s: 0 rss: 39Mb
#32 pulse cov: 552 ft: 3280 exec/s: 0 rss: 41Mb
#64 pulse cov: 576 ft: 3641 exec/s: 0 rss: 50Mb
#78 LOADED cov: 602 ft: 3936 exec/s: 0 rss: 88Mb
#128 pulse cov: 611 ft: 3996 exec/s: 0 rss: 93Mb
#180 DONE cov: 611 ft: 4016 exec/s: 0 rss: 155Mb
MERGE-OUTER: succesfull in 1 attempt(s)
MERGE-OUTER: the control file has 39741 bytes
MERGE-OUTER: consumed 0Mb (37Mb rss) to parse the control file
MERGE-OUTER: 9 new files with 80 new features added; 9 new coverage edges
```
Reviewers: hctim, morehouse
Reviewed By: morehouse
Subscribers: delcypher, #sanitizers, llvm-commits, kcc
Tags: #llvm, #sanitizers
Differential Revision: https://reviews.llvm.org/D66030
llvm-svn: 368617
2019-08-13 04:21:27 +08:00
|
|
|
void Fuzzer::PrintStats(const char *Where, const char *End, size_t Units,
|
|
|
|
size_t Features) {
|
2017-08-22 07:25:50 +08:00
|
|
|
size_t ExecPerSec = execPerSec();
|
|
|
|
if (!Options.Verbosity)
|
|
|
|
return;
|
|
|
|
Printf("#%zd\t%s", TotalNumberOfRuns, Where);
|
|
|
|
if (size_t N = TPC.GetTotalPCCoverage())
|
|
|
|
Printf(" cov: %zd", N);
|
[libFuzzer] Merge: print feature coverage number as well.
Summary:
feature coverage is a useful signal that is available during the merge
process, but was not printed previously.
Output example:
```
$ ./fuzzer -use_value_profile=1 -merge=1 new_corpus/ seed_corpus/
INFO: Seed: 1676551929
INFO: Loaded 1 modules (2380 inline 8-bit counters): 2380 [0x90d180, 0x90dacc),
INFO: Loaded 1 PC tables (2380 PCs): 2380 [0x684018,0x68d4d8),
MERGE-OUTER: 180 files, 78 in the initial corpus
MERGE-OUTER: attempt 1
INFO: Seed: 1676574577
INFO: Loaded 1 modules (2380 inline 8-bit counters): 2380 [0x90d180, 0x90dacc),
INFO: Loaded 1 PC tables (2380 PCs): 2380 [0x684018,0x68d4d8),
INFO: -max_len is not provided; libFuzzer will not generate inputs larger than 1048576 bytes
MERGE-INNER: using the control file '/tmp/libFuzzerTemp.111754.txt'
MERGE-INNER: 180 total files; 0 processed earlier; will process 180 files now
#1 pulse cov: 134 ft: 330 exec/s: 0 rss: 37Mb
#2 pulse cov: 142 ft: 462 exec/s: 0 rss: 38Mb
#4 pulse cov: 152 ft: 651 exec/s: 0 rss: 38Mb
#8 pulse cov: 152 ft: 943 exec/s: 0 rss: 38Mb
#16 pulse cov: 520 ft: 2783 exec/s: 0 rss: 39Mb
#32 pulse cov: 552 ft: 3280 exec/s: 0 rss: 41Mb
#64 pulse cov: 576 ft: 3641 exec/s: 0 rss: 50Mb
#78 LOADED cov: 602 ft: 3936 exec/s: 0 rss: 88Mb
#128 pulse cov: 611 ft: 3996 exec/s: 0 rss: 93Mb
#180 DONE cov: 611 ft: 4016 exec/s: 0 rss: 155Mb
MERGE-OUTER: succesfull in 1 attempt(s)
MERGE-OUTER: the control file has 39741 bytes
MERGE-OUTER: consumed 0Mb (37Mb rss) to parse the control file
MERGE-OUTER: 9 new files with 80 new features added; 9 new coverage edges
```
Reviewers: hctim, morehouse
Reviewed By: morehouse
Subscribers: delcypher, #sanitizers, llvm-commits, kcc
Tags: #llvm, #sanitizers
Differential Revision: https://reviews.llvm.org/D66030
llvm-svn: 368617
2019-08-13 04:21:27 +08:00
|
|
|
if (size_t N = Features ? Features : 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() {
|
2020-10-24 02:07:30 +08:00
|
|
|
if (Options.PrintFullCoverage)
|
|
|
|
TPC.PrintCoverage(/*PrintAllCounters=*/true);
|
2017-08-22 07:25:50 +08:00
|
|
|
if (Options.PrintCoverage)
|
2020-10-24 02:07:30 +08:00
|
|
|
TPC.PrintCoverage(/*PrintAllCounters=*/false);
|
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>;
|
2019-02-15 07:12:33 +08:00
|
|
|
auto HandlePC = [&](const TracePC::PCTableEntry *TE) {
|
|
|
|
if (!PCsSet->insert(TE->PC).second)
|
2017-10-24 07:24:33 +08:00
|
|
|
return;
|
2019-02-15 07:12:33 +08:00
|
|
|
std::string Descr = DescribePC("%F %L", TE->PC + 1);
|
2017-08-22 07:25:50 +08:00
|
|
|
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;
|
2021-04-17 00:21:49 +08:00
|
|
|
Vector<std::string> AdditionalCorpusPaths;
|
|
|
|
ReadDirToVectorOfUnits(
|
|
|
|
Options.OutputCorpus.c_str(), &AdditionalCorpus,
|
|
|
|
&EpochOfLastReadOfOutputCorpus, MaxSize,
|
|
|
|
/*ExitOnError*/ false,
|
|
|
|
(Options.Verbosity >= 2 ? &AdditionalCorpusPaths : nullptr));
|
2017-08-22 07:25:50 +08:00
|
|
|
if (Options.Verbosity >= 2)
|
|
|
|
Printf("Reload: read %zd new units.\n", AdditionalCorpus.size());
|
|
|
|
bool Reloaded = false;
|
2021-04-17 00:21:49 +08:00
|
|
|
for (size_t i = 0; i != AdditionalCorpus.size(); ++i) {
|
|
|
|
auto &U = AdditionalCorpus[i];
|
2017-08-22 07:25:50 +08:00
|
|
|
if (U.size() > MaxSize)
|
|
|
|
U.resize(MaxSize);
|
|
|
|
if (!Corpus.HasUnit(U)) {
|
|
|
|
if (RunOne(U.data(), U.size())) {
|
|
|
|
CheckExitOnSrcPosOrItem();
|
|
|
|
Reloaded = true;
|
2021-04-17 00:21:49 +08:00
|
|
|
if (Options.Verbosity >= 2)
|
|
|
|
Printf("Reloaded %s\n", AdditionalCorpusPaths[i].c_str());
|
2017-08-22 07:25:50 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
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 ");
|
[crt][fuzzer] Fix up various numeric conversions
Attempting to build a standalone libFuzzer in Fuchsia's default toolchain for the purpose of cross-compiling the unit tests revealed a number of not-quite-proper type conversions. Fuchsia's toolchain include `-std=c++17` and `-Werror`, among others, leading to many errors like `-Wshorten-64-to-32`, `-Wimplicit-float-conversion`, etc.
Most of these have been addressed by simply making the conversion explicit with a `static_cast`. These typically fell into one of two categories: 1) conversions between types where high precision isn't critical, e.g. the "energy" calculations for `InputInfo`, and 2) conversions where the values will never reach the bits being truncated, e.g. `DftTimeInSeconds` is not going to exceed 136 years.
The major exception to this is the number of features: there are several places that treat features as `size_t`, and others as `uint32_t`. This change makes the decision to cap the features at 32 bits. The maximum value of a feature as produced by `TracePC::CollectFeatures` is roughly:
(NumPCsInPCTables + ValueBitMap::kMapSizeInBits + ExtraCountersBegin() - ExtraCountersEnd() + log2(SIZE_MAX)) * 8
It's conceivable for extremely large targets and/or extra counters that this limit could be reached. This shouldn't break fuzzing, but it will cause certain features to collide and lower the fuzzers overall precision. To address this, this change adds a warning to TracePC::PrintModuleInfo about excessive feature size if it is detected, and recommends refactoring the fuzzer into several smaller ones.
Reviewed By: morehouse
Differential Revision: https://reviews.llvm.org/D97992
2021-03-12 08:00:53 +08:00
|
|
|
auto Threshhold =
|
|
|
|
static_cast<long>(static_cast<double>(TimeOfLongestUnitInSeconds) * 1.1);
|
|
|
|
if (TimeOfUnit > Threshhold && TimeOfUnit >= Options.ReportSlowUnits) {
|
2017-08-22 07:25:50 +08:00
|
|
|
TimeOfLongestUnitInSeconds = TimeOfUnit;
|
|
|
|
Printf("Slowest unit: %zd s:\n", TimeOfLongestUnitInSeconds);
|
|
|
|
WriteUnitToFileWithPrefix({Data, Data + Size}, "slow-unit-");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-13 08:20:31 +08:00
|
|
|
static void WriteFeatureSetToFile(const std::string &FeaturesDir,
|
2019-04-13 09:57:33 +08:00
|
|
|
const std::string &FileName,
|
2019-04-13 08:20:31 +08:00
|
|
|
const Vector<uint32_t> &FeatureSet) {
|
|
|
|
if (FeaturesDir.empty() || FeatureSet.empty()) return;
|
|
|
|
WriteToFile(reinterpret_cast<const uint8_t *>(FeatureSet.data()),
|
|
|
|
FeatureSet.size() * sizeof(FeatureSet[0]),
|
2019-04-13 09:57:33 +08:00
|
|
|
DirPlusFile(FeaturesDir, FileName));
|
2019-04-13 08:20:31 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void RenameFeatureSetFile(const std::string &FeaturesDir,
|
|
|
|
const std::string &OldFile,
|
|
|
|
const std::string &NewFile) {
|
|
|
|
if (FeaturesDir.empty()) return;
|
|
|
|
RenameFile(DirPlusFile(FeaturesDir, OldFile),
|
|
|
|
DirPlusFile(FeaturesDir, NewFile));
|
|
|
|
}
|
|
|
|
|
2020-07-09 03:30:53 +08:00
|
|
|
static void WriteEdgeToMutationGraphFile(const std::string &MutationGraphFile,
|
|
|
|
const InputInfo *II,
|
|
|
|
const InputInfo *BaseII,
|
|
|
|
const std::string &MS) {
|
|
|
|
if (MutationGraphFile.empty())
|
|
|
|
return;
|
|
|
|
|
|
|
|
std::string Sha1 = Sha1ToString(II->Sha1);
|
|
|
|
|
|
|
|
std::string OutputString;
|
|
|
|
|
|
|
|
// Add a new vertex.
|
|
|
|
OutputString.append("\"");
|
|
|
|
OutputString.append(Sha1);
|
|
|
|
OutputString.append("\"\n");
|
|
|
|
|
|
|
|
// Add a new edge if there is base input.
|
|
|
|
if (BaseII) {
|
|
|
|
std::string BaseSha1 = Sha1ToString(BaseII->Sha1);
|
|
|
|
OutputString.append("\"");
|
|
|
|
OutputString.append(BaseSha1);
|
|
|
|
OutputString.append("\" -> \"");
|
|
|
|
OutputString.append(Sha1);
|
|
|
|
OutputString.append("\" [label=\"");
|
|
|
|
OutputString.append(MS);
|
|
|
|
OutputString.append("\"];\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
AppendToFile(OutputString, MutationGraphFile);
|
|
|
|
}
|
|
|
|
|
2017-08-22 07:25:50 +08:00
|
|
|
bool Fuzzer::RunOne(const uint8_t *Data, size_t Size, bool MayDeleteFile,
|
2020-07-31 08:07:20 +08:00
|
|
|
InputInfo *II, bool ForceAddToCorpus,
|
|
|
|
bool *FoundUniqFeatures) {
|
2017-10-24 07:24:33 +08:00
|
|
|
if (!Size)
|
|
|
|
return false;
|
[crt][fuzzer] Fix up various numeric conversions
Attempting to build a standalone libFuzzer in Fuchsia's default toolchain for the purpose of cross-compiling the unit tests revealed a number of not-quite-proper type conversions. Fuchsia's toolchain include `-std=c++17` and `-Werror`, among others, leading to many errors like `-Wshorten-64-to-32`, `-Wimplicit-float-conversion`, etc.
Most of these have been addressed by simply making the conversion explicit with a `static_cast`. These typically fell into one of two categories: 1) conversions between types where high precision isn't critical, e.g. the "energy" calculations for `InputInfo`, and 2) conversions where the values will never reach the bits being truncated, e.g. `DftTimeInSeconds` is not going to exceed 136 years.
The major exception to this is the number of features: there are several places that treat features as `size_t`, and others as `uint32_t`. This change makes the decision to cap the features at 32 bits. The maximum value of a feature as produced by `TracePC::CollectFeatures` is roughly:
(NumPCsInPCTables + ValueBitMap::kMapSizeInBits + ExtraCountersBegin() - ExtraCountersEnd() + log2(SIZE_MAX)) * 8
It's conceivable for extremely large targets and/or extra counters that this limit could be reached. This shouldn't break fuzzing, but it will cause certain features to collide and lower the fuzzers overall precision. To address this, this change adds a warning to TracePC::PrintModuleInfo about excessive feature size if it is detected, and recommends refactoring the fuzzer into several smaller ones.
Reviewed By: morehouse
Differential Revision: https://reviews.llvm.org/D97992
2021-03-12 08:00:53 +08:00
|
|
|
// Largest input length should be INT_MAX.
|
|
|
|
assert(Size < std::numeric_limits<uint32_t>::max());
|
2017-08-22 07:25:50 +08:00
|
|
|
|
|
|
|
ExecuteCallback(Data, Size);
|
2020-08-18 00:59:59 +08:00
|
|
|
auto TimeOfUnit = duration_cast<microseconds>(UnitStopTime - UnitStartTime);
|
2017-08-22 07:25:50 +08:00
|
|
|
|
|
|
|
UniqFeatureSetTmp.clear();
|
|
|
|
size_t FoundUniqFeaturesOfII = 0;
|
|
|
|
size_t NumUpdatesBefore = Corpus.NumFeatureUpdates();
|
[crt][fuzzer] Fix up various numeric conversions
Attempting to build a standalone libFuzzer in Fuchsia's default toolchain for the purpose of cross-compiling the unit tests revealed a number of not-quite-proper type conversions. Fuchsia's toolchain include `-std=c++17` and `-Werror`, among others, leading to many errors like `-Wshorten-64-to-32`, `-Wimplicit-float-conversion`, etc.
Most of these have been addressed by simply making the conversion explicit with a `static_cast`. These typically fell into one of two categories: 1) conversions between types where high precision isn't critical, e.g. the "energy" calculations for `InputInfo`, and 2) conversions where the values will never reach the bits being truncated, e.g. `DftTimeInSeconds` is not going to exceed 136 years.
The major exception to this is the number of features: there are several places that treat features as `size_t`, and others as `uint32_t`. This change makes the decision to cap the features at 32 bits. The maximum value of a feature as produced by `TracePC::CollectFeatures` is roughly:
(NumPCsInPCTables + ValueBitMap::kMapSizeInBits + ExtraCountersBegin() - ExtraCountersEnd() + log2(SIZE_MAX)) * 8
It's conceivable for extremely large targets and/or extra counters that this limit could be reached. This shouldn't break fuzzing, but it will cause certain features to collide and lower the fuzzers overall precision. To address this, this change adds a warning to TracePC::PrintModuleInfo about excessive feature size if it is detected, and recommends refactoring the fuzzer into several smaller ones.
Reviewed By: morehouse
Differential Revision: https://reviews.llvm.org/D97992
2021-03-12 08:00:53 +08:00
|
|
|
TPC.CollectFeatures([&](uint32_t Feature) {
|
|
|
|
if (Corpus.AddFeature(Feature, static_cast<uint32_t>(Size), Options.Shrink))
|
2017-08-22 07:25:50 +08:00
|
|
|
UniqFeatureSetTmp.push_back(Feature);
|
Entropic: Boosting LibFuzzer Performance
Summary:
This is collaboration between Marcel Boehme @ Monash, Australia and Valentin Manès plus Sang Kil Cha @ KAIST, South Korea.
We have made a few modifications to boost LibFuzzer performance by changing how weights are assigned to the seeds in the corpus. Essentially, seeds that reveal more "information" about globally rare features are assigned a higher weight. Our results on the Fuzzer Test Suite seem quite promising. In terms of bug finding, our Entropic patch usually finds the same errors much faster and in more runs. In terms of coverage, our version Entropic achieves the same coverage in less than half the time for the majority of subjects. For the lack of space, we shared more detailed performance results directly with @kcc. We'll publish the preprint with all the technical details as soon as it is accepted. Happy to share if you drop us an email.
There should be plenty of opportunities to optimise further. For instance, while Entropic achieves the same coverage in less than half the time, Entropic has a much lower #execs per second. We ran the perf-tool and found a few performance bottlenecks.
Thanks for open-sourcing LibFuzzer (and the entire LLVM Compiler Infrastructure)! This has been such a tremendous help to my research.
Patch By: Marcel Boehme
Reviewers: kcc, metzman, morehouse, Dor1s, vitalybuka
Reviewed By: kcc
Subscribers: dgg5503, Valentin, llvm-commits, kcc
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D73776
2020-05-20 01:28:18 +08:00
|
|
|
if (Options.Entropic)
|
|
|
|
Corpus.UpdateFeatureFrequency(II, Feature);
|
2020-07-31 08:07:20 +08:00
|
|
|
if (Options.ReduceInputs && II && !II->NeverReduce)
|
2017-08-22 07:25:50 +08:00
|
|
|
if (std::binary_search(II->UniqFeatureSet.begin(),
|
|
|
|
II->UniqFeatureSet.end(), Feature))
|
|
|
|
FoundUniqFeaturesOfII++;
|
|
|
|
});
|
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;
|
2020-07-31 08:07:20 +08:00
|
|
|
if (NumNewFeatures || ForceAddToCorpus) {
|
2017-08-22 07:25:50 +08:00
|
|
|
TPC.UpdateObservedPCs();
|
2020-07-31 08:07:20 +08:00
|
|
|
auto NewII =
|
|
|
|
Corpus.AddToCorpus({Data, Data + Size}, NumNewFeatures, MayDeleteFile,
|
|
|
|
TPC.ObservedFocusFunction(), ForceAddToCorpus,
|
2020-08-18 00:59:59 +08:00
|
|
|
TimeOfUnit, UniqFeatureSetTmp, DFT, II);
|
2019-04-13 09:57:33 +08:00
|
|
|
WriteFeatureSetToFile(Options.FeaturesDir, Sha1ToString(NewII->Sha1),
|
2019-04-13 08:20:31 +08:00
|
|
|
NewII->UniqFeatureSet);
|
2020-07-09 03:30:53 +08:00
|
|
|
WriteEdgeToMutationGraphFile(Options.MutationGraphFile, NewII, II,
|
2021-05-27 06:14:37 +08:00
|
|
|
MD.MutationSequence());
|
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) {
|
2019-04-13 08:20:31 +08:00
|
|
|
auto OldFeaturesFile = Sha1ToString(II->Sha1);
|
2017-08-22 07:25:50 +08:00
|
|
|
Corpus.Replace(II, {Data, Data + Size});
|
2019-04-13 08:20:31 +08:00
|
|
|
RenameFeatureSetFile(Options.FeaturesDir, OldFeaturesFile,
|
|
|
|
Sha1ToString(II->Sha1));
|
2017-08-22 07:25:50 +08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-10-24 02:07:30 +08:00
|
|
|
void Fuzzer::TPCUpdateObservedPCs() { TPC.UpdateObservedPCs(); }
|
|
|
|
|
2017-08-22 07:25:50 +08:00
|
|
|
size_t Fuzzer::GetCurrentUnitInFuzzingThead(const uint8_t **Data) const {
|
|
|
|
assert(InFuzzingThread());
|
|
|
|
*Data = CurrentUnitData;
|
|
|
|
return CurrentUnitSize;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Fuzzer::CrashOnOverwrittenData() {
|
2019-09-26 08:54:30 +08:00
|
|
|
Printf("==%d== ERROR: libFuzzer: fuzz target overwrites its const input\n",
|
2017-08-22 07:25:50 +08:00
|
|
|
GetPid());
|
2019-09-28 06:04:36 +08:00
|
|
|
PrintStackTrace();
|
|
|
|
Printf("SUMMARY: libFuzzer: overwrites-const-input\n");
|
2017-08-22 07:25:50 +08:00
|
|
|
DumpCurrentUnit("crash-");
|
2019-09-28 06:04:36 +08:00
|
|
|
PrintFinalStats();
|
2017-08-22 07:25:50 +08:00
|
|
|
_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);
|
|
|
|
}
|
|
|
|
|
2021-03-05 04:55:41 +08:00
|
|
|
// This method is not inlined because it would cause a test to fail where it
|
|
|
|
// is part of the stack unwinding. See D97975 for details.
|
2021-03-13 06:36:57 +08:00
|
|
|
ATTRIBUTE_NOINLINE void Fuzzer::ExecuteCallback(const uint8_t *Data,
|
|
|
|
size_t Size) {
|
2017-08-22 07:25:50 +08:00
|
|
|
TPC.RecordInitialStack();
|
|
|
|
TotalNumberOfRuns++;
|
|
|
|
assert(InFuzzingThread());
|
|
|
|
// We copy the contents of Unit into a separate heap buffer
|
|
|
|
// so that we reliably find buffer overflows in it.
|
|
|
|
uint8_t *DataCopy = new uint8_t[Size];
|
|
|
|
memcpy(DataCopy, Data, Size);
|
2018-07-10 07:51:08 +08:00
|
|
|
if (EF->__msan_unpoison)
|
|
|
|
EF->__msan_unpoison(DataCopy, Size);
|
2019-05-10 06:48:46 +08:00
|
|
|
if (EF->__msan_unpoison_param)
|
|
|
|
EF->__msan_unpoison_param(2);
|
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;
|
|
|
|
}
|
|
|
|
|
2019-02-12 08:12:33 +08:00
|
|
|
std::string Fuzzer::WriteToOutputCorpus(const Unit &U) {
|
2017-08-22 07:25:50 +08:00
|
|
|
if (Options.OnlyASCII)
|
|
|
|
assert(IsASCII(U));
|
|
|
|
if (Options.OutputCorpus.empty())
|
2019-02-12 08:12:33 +08:00
|
|
|
return "";
|
2017-08-22 07:25:50 +08:00
|
|
|
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());
|
2019-02-12 08:12:33 +08:00
|
|
|
return Path;
|
2017-08-22 07:25:50 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
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());
|
2021-05-27 06:14:37 +08:00
|
|
|
MD.PrintMutationSequence(Options.Verbosity >= 2);
|
2017-08-22 07:25:50 +08:00
|
|
|
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());
|
2020-09-02 00:22:59 +08:00
|
|
|
if (Options.DoCrossOver) {
|
|
|
|
auto &CrossOverII = Corpus.ChooseUnitToCrossOverWith(
|
|
|
|
MD.GetRand(), Options.CrossOverUniformDist);
|
|
|
|
MD.SetCrossOverWith(&CrossOverII.U);
|
|
|
|
}
|
2017-08-22 07:25:50 +08:00
|
|
|
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);
|
2019-04-26 08:17:41 +08:00
|
|
|
|
2019-04-12 00:24:53 +08:00
|
|
|
// If MutateWithMask either failed or wasn't called, call default Mutate.
|
|
|
|
if (!NewSize)
|
2018-07-19 09:23:32 +08:00
|
|
|
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++;
|
Entropic: Boosting LibFuzzer Performance
Summary:
This is collaboration between Marcel Boehme @ Monash, Australia and Valentin Manès plus Sang Kil Cha @ KAIST, South Korea.
We have made a few modifications to boost LibFuzzer performance by changing how weights are assigned to the seeds in the corpus. Essentially, seeds that reveal more "information" about globally rare features are assigned a higher weight. Our results on the Fuzzer Test Suite seem quite promising. In terms of bug finding, our Entropic patch usually finds the same errors much faster and in more runs. In terms of coverage, our version Entropic achieves the same coverage in less than half the time for the majority of subjects. For the lack of space, we shared more detailed performance results directly with @kcc. We'll publish the preprint with all the technical details as soon as it is accepted. Happy to share if you drop us an email.
There should be plenty of opportunities to optimise further. For instance, while Entropic achieves the same coverage in less than half the time, Entropic has a much lower #execs per second. We ran the perf-tool and found a few performance bottlenecks.
Thanks for open-sourcing LibFuzzer (and the entire LLVM Compiler Infrastructure)! This has been such a tremendous help to my research.
Patch By: Marcel Boehme
Reviewers: kcc, metzman, morehouse, Dor1s, vitalybuka
Reviewed By: kcc
Subscribers: dgg5503, Valentin, llvm-commits, kcc
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D73776
2020-05-20 01:28:18 +08:00
|
|
|
Corpus.IncrementNumExecutedMutations();
|
2017-08-22 07:25:50 +08:00
|
|
|
|
2017-12-02 03:18:38 +08:00
|
|
|
bool FoundUniqFeatures = false;
|
|
|
|
bool NewCov = RunOne(CurrentUnitData, Size, /*MayDeleteFile=*/true, &II,
|
2020-07-31 08:07:20 +08:00
|
|
|
/*ForceAddToCorpus*/ false, &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)
|
2019-05-10 06:48:46 +08:00
|
|
|
break;
|
2017-08-22 07:25:50 +08:00
|
|
|
}
|
Entropic: Boosting LibFuzzer Performance
Summary:
This is collaboration between Marcel Boehme @ Monash, Australia and Valentin Manès plus Sang Kil Cha @ KAIST, South Korea.
We have made a few modifications to boost LibFuzzer performance by changing how weights are assigned to the seeds in the corpus. Essentially, seeds that reveal more "information" about globally rare features are assigned a higher weight. Our results on the Fuzzer Test Suite seem quite promising. In terms of bug finding, our Entropic patch usually finds the same errors much faster and in more runs. In terms of coverage, our version Entropic achieves the same coverage in less than half the time for the majority of subjects. For the lack of space, we shared more detailed performance results directly with @kcc. We'll publish the preprint with all the technical details as soon as it is accepted. Happy to share if you drop us an email.
There should be plenty of opportunities to optimise further. For instance, while Entropic achieves the same coverage in less than half the time, Entropic has a much lower #execs per second. We ran the perf-tool and found a few performance bottlenecks.
Thanks for open-sourcing LibFuzzer (and the entire LLVM Compiler Infrastructure)! This has been such a tremendous help to my research.
Patch By: Marcel Boehme
Reviewers: kcc, metzman, morehouse, Dor1s, vitalybuka
Reviewed By: kcc
Subscribers: dgg5503, Valentin, llvm-commits, kcc
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D73776
2020-05-20 01:28:18 +08:00
|
|
|
|
|
|
|
II.NeedsEnergyUpdate = true;
|
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();
|
|
|
|
}
|
|
|
|
|
2019-05-10 09:34:26 +08:00
|
|
|
void Fuzzer::ReadAndExecuteSeedCorpora(Vector<SizedFile> &CorporaFiles) {
|
2017-08-29 10:05:01 +08:00
|
|
|
const size_t kMaxSaneLen = 1 << 20;
|
|
|
|
const size_t kMinDefaultLen = 4096;
|
2017-08-30 04:51:24 +08:00
|
|
|
size_t MaxSize = 0;
|
|
|
|
size_t MinSize = -1;
|
|
|
|
size_t TotalSize = 0;
|
2019-05-10 09:34:26 +08:00
|
|
|
for (auto &File : CorporaFiles) {
|
2017-09-13 05:58:07 +08:00
|
|
|
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);
|
|
|
|
|
2019-05-10 09:34:26 +08:00
|
|
|
if (CorporaFiles.empty()) {
|
2017-08-30 04:51:24 +08:00
|
|
|
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",
|
2019-05-10 09:34:26 +08:00
|
|
|
CorporaFiles.size(), MinSize, MaxSize, TotalSize, GetPeakRSSMb());
|
2017-08-30 04:51:24 +08:00
|
|
|
if (Options.ShuffleAtStartUp)
|
2019-05-10 09:34:26 +08:00
|
|
|
std::shuffle(CorporaFiles.begin(), CorporaFiles.end(), MD.GetRand());
|
2017-08-30 04:51:24 +08:00
|
|
|
|
2017-09-13 05:58:07 +08:00
|
|
|
if (Options.PreferSmall) {
|
2019-05-10 09:34:26 +08:00
|
|
|
std::stable_sort(CorporaFiles.begin(), CorporaFiles.end());
|
|
|
|
assert(CorporaFiles.front().Size <= CorporaFiles.back().Size);
|
2017-09-13 05:58:07 +08:00
|
|
|
}
|
2017-08-30 04:51:24 +08:00
|
|
|
|
|
|
|
// Load and execute inputs one by one.
|
2019-05-10 09:34:26 +08:00
|
|
|
for (auto &SF : CorporaFiles) {
|
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);
|
2020-07-31 08:07:20 +08:00
|
|
|
RunOne(U.data(), U.size(), /*MayDeleteFile*/ false, /*II*/ nullptr,
|
|
|
|
/*ForceAddToCorpus*/ Options.KeepSeed,
|
|
|
|
/*FoundUniqFeatures*/ nullptr);
|
2017-08-30 04:51:24 +08:00
|
|
|
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");
|
2020-02-19 01:57:16 +08:00
|
|
|
if (!Options.FocusFunction.empty()) {
|
2018-05-17 07:26:37 +08:00
|
|
|
Printf("INFO: %zd/%zd inputs touch the focus function\n",
|
|
|
|
Corpus.NumInputsThatTouchFocusFunction(), Corpus.size());
|
2020-02-19 01:57:16 +08:00
|
|
|
if (!Options.DataFlowTrace.empty())
|
|
|
|
Printf("INFO: %zd/%zd inputs have the Data Flow Trace\n",
|
|
|
|
Corpus.NumInputsWithDataFlowTrace(),
|
|
|
|
Corpus.NumInputsThatTouchFocusFunction());
|
|
|
|
}
|
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
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-10 09:34:26 +08:00
|
|
|
void Fuzzer::Loop(Vector<SizedFile> &CorporaFiles) {
|
2019-05-24 08:43:52 +08:00
|
|
|
auto FocusFunctionOrAuto = Options.FocusFunction;
|
|
|
|
DFT.Init(Options.DataFlowTrace, &FocusFunctionOrAuto, CorporaFiles,
|
|
|
|
MD.GetRand());
|
|
|
|
TPC.SetFocusFunction(FocusFunctionOrAuto);
|
2019-05-10 09:34:26 +08:00
|
|
|
ReadAndExecuteSeedCorpora(CorporaFiles);
|
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();
|
2019-02-16 09:23:41 +08:00
|
|
|
|
|
|
|
TmpMaxMutationLen =
|
|
|
|
Min(MaxMutationLen, Max(size_t(4), Corpus.MaxInputSize()));
|
|
|
|
|
2017-08-22 07:25:50 +08:00
|
|
|
while (true) {
|
|
|
|
auto Now = system_clock::now();
|
2019-06-15 06:56:50 +08:00
|
|
|
if (!Options.StopFile.empty() &&
|
|
|
|
!FileToVector(Options.StopFile, 1, false).empty())
|
|
|
|
break;
|
2017-08-22 07:25:50 +08:00
|
|
|
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");
|
2021-05-27 06:14:37 +08:00
|
|
|
MD.PrintRecommendedDictionary();
|
2017-08-22 07:25:50 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace fuzzer
|
|
|
|
|
|
|
|
extern "C" {
|
|
|
|
|
2019-01-18 00:36:05 +08:00
|
|
|
ATTRIBUTE_INTERFACE size_t
|
2018-01-18 04:39:14 +08:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2017-10-24 07:24:33 +08:00
|
|
|
} // extern "C"
|