2017-08-21 23:25:50 +00:00
|
|
|
//===- FuzzerDriver.cpp - FuzzerDriver function and flags -----------------===//
|
|
|
|
//
|
2019-01-19 08:50:56 +00: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-21 23:25:50 +00:00
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// FuzzerDriver and flag parsing.
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2017-12-04 19:25:59 +00:00
|
|
|
#include "FuzzerCommand.h"
|
2017-08-21 23:25:50 +00:00
|
|
|
#include "FuzzerCorpus.h"
|
2019-02-12 22:48:55 +00:00
|
|
|
#include "FuzzerFork.h"
|
2017-08-21 23:25:50 +00:00
|
|
|
#include "FuzzerIO.h"
|
|
|
|
#include "FuzzerInterface.h"
|
|
|
|
#include "FuzzerInternal.h"
|
2019-02-12 22:48:55 +00:00
|
|
|
#include "FuzzerMerge.h"
|
2017-08-21 23:25:50 +00:00
|
|
|
#include "FuzzerMutate.h"
|
2020-07-14 21:02:32 +00:00
|
|
|
#include "FuzzerPlatform.h"
|
2017-08-21 23:25:50 +00:00
|
|
|
#include "FuzzerRandom.h"
|
|
|
|
#include "FuzzerTracePC.h"
|
|
|
|
#include <algorithm>
|
|
|
|
#include <atomic>
|
|
|
|
#include <chrono>
|
|
|
|
#include <cstdlib>
|
|
|
|
#include <cstring>
|
|
|
|
#include <mutex>
|
|
|
|
#include <string>
|
|
|
|
#include <thread>
|
2021-07-02 09:44:54 -07:00
|
|
|
#include <fstream>
|
2017-08-21 23:25:50 +00:00
|
|
|
|
|
|
|
// This function should be present in the libFuzzer so that the client
|
|
|
|
// binary can test for its existence.
|
2019-01-17 16:36:05 +00:00
|
|
|
#if LIBFUZZER_MSVC
|
|
|
|
extern "C" void __libfuzzer_is_present() {}
|
2020-07-17 18:29:47 -07:00
|
|
|
#if defined(_M_IX86) || defined(__i386__)
|
|
|
|
#pragma comment(linker, "/include:___libfuzzer_is_present")
|
|
|
|
#else
|
2019-01-17 16:36:05 +00:00
|
|
|
#pragma comment(linker, "/include:__libfuzzer_is_present")
|
2020-07-17 18:29:47 -07:00
|
|
|
#endif
|
2019-01-17 16:36:05 +00:00
|
|
|
#else
|
2017-08-21 23:25:50 +00:00
|
|
|
extern "C" __attribute__((used)) void __libfuzzer_is_present() {}
|
2019-01-17 16:36:05 +00:00
|
|
|
#endif // LIBFUZZER_MSVC
|
2017-08-21 23:25:50 +00:00
|
|
|
|
|
|
|
namespace fuzzer {
|
|
|
|
|
|
|
|
// Program arguments.
|
|
|
|
struct FlagDescription {
|
|
|
|
const char *Name;
|
|
|
|
const char *Description;
|
|
|
|
int Default;
|
|
|
|
int *IntFlag;
|
|
|
|
const char **StrFlag;
|
|
|
|
unsigned int *UIntFlag;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct {
|
|
|
|
#define FUZZER_DEPRECATED_FLAG(Name)
|
|
|
|
#define FUZZER_FLAG_INT(Name, Default, Description) int Name;
|
|
|
|
#define FUZZER_FLAG_UNSIGNED(Name, Default, Description) unsigned int Name;
|
|
|
|
#define FUZZER_FLAG_STRING(Name, Description) const char *Name;
|
|
|
|
#include "FuzzerFlags.def"
|
|
|
|
#undef FUZZER_DEPRECATED_FLAG
|
|
|
|
#undef FUZZER_FLAG_INT
|
|
|
|
#undef FUZZER_FLAG_UNSIGNED
|
|
|
|
#undef FUZZER_FLAG_STRING
|
|
|
|
} Flags;
|
|
|
|
|
|
|
|
static const FlagDescription FlagDescriptions [] {
|
|
|
|
#define FUZZER_DEPRECATED_FLAG(Name) \
|
|
|
|
{#Name, "Deprecated; don't use", 0, nullptr, nullptr, nullptr},
|
|
|
|
#define FUZZER_FLAG_INT(Name, Default, Description) \
|
|
|
|
{#Name, Description, Default, &Flags.Name, nullptr, nullptr},
|
|
|
|
#define FUZZER_FLAG_UNSIGNED(Name, Default, Description) \
|
|
|
|
{#Name, Description, static_cast<int>(Default), \
|
|
|
|
nullptr, nullptr, &Flags.Name},
|
|
|
|
#define FUZZER_FLAG_STRING(Name, Description) \
|
|
|
|
{#Name, Description, 0, nullptr, &Flags.Name, nullptr},
|
|
|
|
#include "FuzzerFlags.def"
|
|
|
|
#undef FUZZER_DEPRECATED_FLAG
|
|
|
|
#undef FUZZER_FLAG_INT
|
|
|
|
#undef FUZZER_FLAG_UNSIGNED
|
|
|
|
#undef FUZZER_FLAG_STRING
|
|
|
|
};
|
|
|
|
|
|
|
|
static const size_t kNumFlags =
|
|
|
|
sizeof(FlagDescriptions) / sizeof(FlagDescriptions[0]);
|
|
|
|
|
2021-08-03 10:04:16 -07:00
|
|
|
static std::vector<std::string> *Inputs;
|
2017-08-21 23:25:50 +00:00
|
|
|
static std::string *ProgName;
|
|
|
|
|
|
|
|
static void PrintHelp() {
|
|
|
|
Printf("Usage:\n");
|
|
|
|
auto Prog = ProgName->c_str();
|
|
|
|
Printf("\nTo run fuzzing pass 0 or more directories.\n");
|
|
|
|
Printf("%s [-flag1=val1 [-flag2=val2 ...] ] [dir1 [dir2 ...] ]\n", Prog);
|
|
|
|
|
|
|
|
Printf("\nTo run individual tests without fuzzing pass 1 or more files:\n");
|
|
|
|
Printf("%s [-flag1=val1 [-flag2=val2 ...] ] file1 [file2 ...]\n", Prog);
|
|
|
|
|
|
|
|
Printf("\nFlags: (strictly in form -flag=value)\n");
|
|
|
|
size_t MaxFlagLen = 0;
|
|
|
|
for (size_t F = 0; F < kNumFlags; F++)
|
|
|
|
MaxFlagLen = std::max(strlen(FlagDescriptions[F].Name), MaxFlagLen);
|
|
|
|
|
|
|
|
for (size_t F = 0; F < kNumFlags; F++) {
|
|
|
|
const auto &D = FlagDescriptions[F];
|
|
|
|
if (strstr(D.Description, "internal flag") == D.Description) continue;
|
|
|
|
Printf(" %s", D.Name);
|
|
|
|
for (size_t i = 0, n = MaxFlagLen - strlen(D.Name); i < n; i++)
|
|
|
|
Printf(" ");
|
|
|
|
Printf("\t");
|
|
|
|
Printf("%d\t%s\n", D.Default, D.Description);
|
|
|
|
}
|
|
|
|
Printf("\nFlags starting with '--' will be ignored and "
|
2018-07-16 15:15:34 +00:00
|
|
|
"will be passed verbatim to subprocesses.\n");
|
2017-08-21 23:25:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static const char *FlagValue(const char *Param, const char *Name) {
|
|
|
|
size_t Len = strlen(Name);
|
|
|
|
if (Param[0] == '-' && strstr(Param + 1, Name) == Param + 1 &&
|
|
|
|
Param[Len + 1] == '=')
|
2018-07-16 15:15:34 +00:00
|
|
|
return &Param[Len + 2];
|
2017-08-21 23:25:50 +00:00
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Avoid calling stol as it triggers a bug in clang/glibc build.
|
|
|
|
static long MyStol(const char *Str) {
|
|
|
|
long Res = 0;
|
|
|
|
long Sign = 1;
|
|
|
|
if (*Str == '-') {
|
|
|
|
Str++;
|
|
|
|
Sign = -1;
|
|
|
|
}
|
|
|
|
for (size_t i = 0; Str[i]; i++) {
|
|
|
|
char Ch = Str[i];
|
|
|
|
if (Ch < '0' || Ch > '9')
|
|
|
|
return Res;
|
|
|
|
Res = Res * 10 + (Ch - '0');
|
|
|
|
}
|
|
|
|
return Res * Sign;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool ParseOneFlag(const char *Param) {
|
|
|
|
if (Param[0] != '-') return false;
|
|
|
|
if (Param[1] == '-') {
|
|
|
|
static bool PrintedWarning = false;
|
|
|
|
if (!PrintedWarning) {
|
|
|
|
PrintedWarning = true;
|
|
|
|
Printf("INFO: libFuzzer ignores flags that start with '--'\n");
|
|
|
|
}
|
|
|
|
for (size_t F = 0; F < kNumFlags; F++)
|
|
|
|
if (FlagValue(Param + 1, FlagDescriptions[F].Name))
|
|
|
|
Printf("WARNING: did you mean '%s' (single dash)?\n", Param + 1);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
for (size_t F = 0; F < kNumFlags; F++) {
|
|
|
|
const char *Name = FlagDescriptions[F].Name;
|
|
|
|
const char *Str = FlagValue(Param, Name);
|
|
|
|
if (Str) {
|
|
|
|
if (FlagDescriptions[F].IntFlag) {
|
[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-11 16:00:53 -08:00
|
|
|
auto Val = MyStol(Str);
|
|
|
|
*FlagDescriptions[F].IntFlag = static_cast<int>(Val);
|
2017-08-21 23:25:50 +00:00
|
|
|
if (Flags.verbosity >= 2)
|
|
|
|
Printf("Flag: %s %d\n", Name, Val);
|
|
|
|
return true;
|
|
|
|
} else if (FlagDescriptions[F].UIntFlag) {
|
[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-11 16:00:53 -08:00
|
|
|
auto Val = std::stoul(Str);
|
|
|
|
*FlagDescriptions[F].UIntFlag = static_cast<unsigned int>(Val);
|
2017-08-21 23:25:50 +00:00
|
|
|
if (Flags.verbosity >= 2)
|
|
|
|
Printf("Flag: %s %u\n", Name, Val);
|
|
|
|
return true;
|
|
|
|
} else if (FlagDescriptions[F].StrFlag) {
|
|
|
|
*FlagDescriptions[F].StrFlag = Str;
|
|
|
|
if (Flags.verbosity >= 2)
|
|
|
|
Printf("Flag: %s %s\n", Name, Str);
|
|
|
|
return true;
|
|
|
|
} else { // Deprecated flag.
|
|
|
|
Printf("Flag: %s: deprecated, don't use\n", Name);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Printf("\n\nWARNING: unrecognized flag '%s'; "
|
|
|
|
"use -help=1 to list all flags\n\n", Param);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// We don't use any library to minimize dependencies.
|
2021-08-03 10:04:16 -07:00
|
|
|
static void ParseFlags(const std::vector<std::string> &Args,
|
2019-06-14 19:34:11 +00:00
|
|
|
const ExternalFunctions *EF) {
|
2017-08-21 23:25:50 +00:00
|
|
|
for (size_t F = 0; F < kNumFlags; F++) {
|
|
|
|
if (FlagDescriptions[F].IntFlag)
|
|
|
|
*FlagDescriptions[F].IntFlag = FlagDescriptions[F].Default;
|
|
|
|
if (FlagDescriptions[F].UIntFlag)
|
|
|
|
*FlagDescriptions[F].UIntFlag =
|
|
|
|
static_cast<unsigned int>(FlagDescriptions[F].Default);
|
|
|
|
if (FlagDescriptions[F].StrFlag)
|
|
|
|
*FlagDescriptions[F].StrFlag = nullptr;
|
|
|
|
}
|
2019-06-14 19:34:11 +00:00
|
|
|
|
|
|
|
// Disable len_control by default, if LLVMFuzzerCustomMutator is used.
|
2020-01-22 12:56:16 -08:00
|
|
|
if (EF->LLVMFuzzerCustomMutator) {
|
2019-06-14 19:34:11 +00:00
|
|
|
Flags.len_control = 0;
|
2020-01-22 12:56:16 -08:00
|
|
|
Printf("INFO: found LLVMFuzzerCustomMutator (%p). "
|
|
|
|
"Disabling -len_control by default.\n", EF->LLVMFuzzerCustomMutator);
|
|
|
|
}
|
2019-06-14 19:34:11 +00:00
|
|
|
|
2021-08-03 10:04:16 -07:00
|
|
|
Inputs = new std::vector<std::string>;
|
2017-08-21 23:25:50 +00:00
|
|
|
for (size_t A = 1; A < Args.size(); A++) {
|
|
|
|
if (ParseOneFlag(Args[A].c_str())) {
|
|
|
|
if (Flags.ignore_remaining_args)
|
|
|
|
break;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
Inputs->push_back(Args[A]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static std::mutex Mu;
|
|
|
|
|
|
|
|
static void PulseThread() {
|
|
|
|
while (true) {
|
|
|
|
SleepSeconds(600);
|
|
|
|
std::lock_guard<std::mutex> Lock(Mu);
|
|
|
|
Printf("pulse...\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-04 19:25:59 +00:00
|
|
|
static void WorkerThread(const Command &BaseCmd, std::atomic<unsigned> *Counter,
|
2017-08-21 23:25:50 +00:00
|
|
|
unsigned NumJobs, std::atomic<bool> *HasErrors) {
|
|
|
|
while (true) {
|
|
|
|
unsigned C = (*Counter)++;
|
|
|
|
if (C >= NumJobs) break;
|
|
|
|
std::string Log = "fuzz-" + std::to_string(C) + ".log";
|
2017-12-04 19:25:59 +00:00
|
|
|
Command Cmd(BaseCmd);
|
|
|
|
Cmd.setOutputFile(Log);
|
|
|
|
Cmd.combineOutAndErr();
|
|
|
|
if (Flags.verbosity) {
|
|
|
|
std::string CommandLine = Cmd.toString();
|
2017-12-06 22:12:24 +00:00
|
|
|
Printf("%s\n", CommandLine.c_str());
|
2017-12-04 19:25:59 +00:00
|
|
|
}
|
|
|
|
int ExitCode = ExecuteCommand(Cmd);
|
2017-08-21 23:25:50 +00:00
|
|
|
if (ExitCode != 0)
|
|
|
|
*HasErrors = true;
|
|
|
|
std::lock_guard<std::mutex> Lock(Mu);
|
|
|
|
Printf("================== Job %u exited with exit code %d ============\n",
|
|
|
|
C, ExitCode);
|
|
|
|
fuzzer::CopyFileToErr(Log);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-03 07:58:45 -07:00
|
|
|
static void ValidateDirectoryExists(const std::string &Path,
|
|
|
|
bool CreateDirectory) {
|
|
|
|
if (Path.empty()) {
|
|
|
|
Printf("ERROR: Provided directory path is an empty string\n");
|
2020-08-26 09:06:12 -07:00
|
|
|
exit(1);
|
|
|
|
}
|
2020-09-03 07:58:45 -07:00
|
|
|
|
|
|
|
if (IsDirectory(Path))
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (CreateDirectory) {
|
|
|
|
if (!MkDirRecursive(Path)) {
|
|
|
|
Printf("ERROR: Failed to create directory \"%s\"\n", Path.c_str());
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
Printf("ERROR: The required directory \"%s\" does not exist\n", Path.c_str());
|
|
|
|
exit(1);
|
2020-08-26 09:06:12 -07:00
|
|
|
}
|
|
|
|
|
2021-08-03 10:04:16 -07:00
|
|
|
std::string CloneArgsWithoutX(const std::vector<std::string> &Args,
|
2017-08-21 23:25:50 +00:00
|
|
|
const char *X1, const char *X2) {
|
|
|
|
std::string Cmd;
|
|
|
|
for (auto &S : Args) {
|
|
|
|
if (FlagValue(S.c_str(), X1) || FlagValue(S.c_str(), X2))
|
|
|
|
continue;
|
|
|
|
Cmd += S + " ";
|
|
|
|
}
|
|
|
|
return Cmd;
|
|
|
|
}
|
|
|
|
|
2021-08-03 10:04:16 -07:00
|
|
|
static int RunInMultipleProcesses(const std::vector<std::string> &Args,
|
2017-08-21 23:25:50 +00:00
|
|
|
unsigned NumWorkers, unsigned NumJobs) {
|
|
|
|
std::atomic<unsigned> Counter(0);
|
|
|
|
std::atomic<bool> HasErrors(false);
|
2017-12-04 19:25:59 +00:00
|
|
|
Command Cmd(Args);
|
|
|
|
Cmd.removeFlag("jobs");
|
|
|
|
Cmd.removeFlag("workers");
|
2021-08-03 10:04:16 -07:00
|
|
|
std::vector<std::thread> V;
|
2017-08-21 23:25:50 +00:00
|
|
|
std::thread Pulse(PulseThread);
|
|
|
|
Pulse.detach();
|
2023-07-26 05:49:35 +01:00
|
|
|
V.resize(NumWorkers);
|
|
|
|
for (unsigned i = 0; i < NumWorkers; i++) {
|
|
|
|
V[i] = std::thread(WorkerThread, std::ref(Cmd), &Counter, NumJobs,
|
|
|
|
&HasErrors);
|
|
|
|
SetThreadName(V[i], "FuzzerWorker");
|
|
|
|
}
|
2017-08-21 23:25:50 +00:00
|
|
|
for (auto &T : V)
|
|
|
|
T.join();
|
|
|
|
return HasErrors ? 1 : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void RssThread(Fuzzer *F, size_t RssLimitMb) {
|
|
|
|
while (true) {
|
|
|
|
SleepSeconds(1);
|
|
|
|
size_t Peak = GetPeakRSSMb();
|
|
|
|
if (Peak > RssLimitMb)
|
|
|
|
F->RssLimitCallback();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void StartRssThread(Fuzzer *F, size_t RssLimitMb) {
|
2019-12-12 08:48:54 -08:00
|
|
|
if (!RssLimitMb)
|
|
|
|
return;
|
2017-08-21 23:25:50 +00:00
|
|
|
std::thread T(RssThread, F, RssLimitMb);
|
|
|
|
T.detach();
|
|
|
|
}
|
|
|
|
|
|
|
|
int RunOneTest(Fuzzer *F, const char *InputFilePath, size_t MaxLen) {
|
|
|
|
Unit U = FileToVector(InputFilePath);
|
|
|
|
if (MaxLen && MaxLen < U.size())
|
|
|
|
U.resize(MaxLen);
|
|
|
|
F->ExecuteCallback(U.data(), U.size());
|
2020-10-23 11:07:30 -07:00
|
|
|
if (Flags.print_full_coverage) {
|
|
|
|
// Leak detection is not needed when collecting full coverage data.
|
|
|
|
F->TPCUpdateObservedPCs();
|
|
|
|
} else {
|
|
|
|
F->TryDetectingAMemoryLeak(U.data(), U.size(), true);
|
|
|
|
}
|
2017-08-21 23:25:50 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool AllInputsAreFiles() {
|
|
|
|
if (Inputs->empty()) return false;
|
|
|
|
for (auto &Path : *Inputs)
|
|
|
|
if (!IsFile(Path))
|
|
|
|
return false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-02-10 14:31:47 -08:00
|
|
|
static std::string GetDedupTokenFromCmdOutput(const std::string &S) {
|
2017-08-21 23:25:50 +00:00
|
|
|
auto Beg = S.find("DEDUP_TOKEN:");
|
|
|
|
if (Beg == std::string::npos)
|
|
|
|
return "";
|
|
|
|
auto End = S.find('\n', Beg);
|
|
|
|
if (End == std::string::npos)
|
|
|
|
return "";
|
|
|
|
return S.substr(Beg, End - Beg);
|
|
|
|
}
|
|
|
|
|
2021-08-03 10:04:16 -07:00
|
|
|
int CleanseCrashInput(const std::vector<std::string> &Args,
|
|
|
|
const FuzzingOptions &Options) {
|
2017-08-21 23:25:50 +00:00
|
|
|
if (Inputs->size() != 1 || !Flags.exact_artifact_path) {
|
|
|
|
Printf("ERROR: -cleanse_crash should be given one input file and"
|
2018-07-16 15:15:34 +00:00
|
|
|
" -exact_artifact_path\n");
|
2017-08-21 23:25:50 +00:00
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
std::string InputFilePath = Inputs->at(0);
|
|
|
|
std::string OutputFilePath = Flags.exact_artifact_path;
|
2017-12-04 19:25:59 +00:00
|
|
|
Command Cmd(Args);
|
|
|
|
Cmd.removeFlag("cleanse_crash");
|
2017-08-21 23:25:50 +00:00
|
|
|
|
2017-12-04 19:25:59 +00:00
|
|
|
assert(Cmd.hasArgument(InputFilePath));
|
|
|
|
Cmd.removeArgument(InputFilePath);
|
2017-08-21 23:25:50 +00:00
|
|
|
|
2020-02-10 18:22:09 -08:00
|
|
|
auto TmpFilePath = TempPath("CleanseCrashInput", ".repro");
|
2017-12-04 19:25:59 +00:00
|
|
|
Cmd.addArgument(TmpFilePath);
|
2020-02-10 14:31:47 -08:00
|
|
|
Cmd.setOutputFile(getDevNull());
|
2017-12-04 19:25:59 +00:00
|
|
|
Cmd.combineOutAndErr();
|
2017-08-21 23:25:50 +00:00
|
|
|
|
|
|
|
std::string CurrentFilePath = InputFilePath;
|
|
|
|
auto U = FileToVector(CurrentFilePath);
|
|
|
|
size_t Size = U.size();
|
|
|
|
|
2021-08-03 10:04:16 -07:00
|
|
|
const std::vector<uint8_t> ReplacementBytes = {' ', 0xff};
|
2017-08-21 23:25:50 +00:00
|
|
|
for (int NumAttempts = 0; NumAttempts < 5; NumAttempts++) {
|
|
|
|
bool Changed = false;
|
|
|
|
for (size_t Idx = 0; Idx < Size; Idx++) {
|
|
|
|
Printf("CLEANSE[%d]: Trying to replace byte %zd of %zd\n", NumAttempts,
|
|
|
|
Idx, Size);
|
|
|
|
uint8_t OriginalByte = U[Idx];
|
|
|
|
if (ReplacementBytes.end() != std::find(ReplacementBytes.begin(),
|
|
|
|
ReplacementBytes.end(),
|
|
|
|
OriginalByte))
|
|
|
|
continue;
|
|
|
|
for (auto NewByte : ReplacementBytes) {
|
|
|
|
U[Idx] = NewByte;
|
|
|
|
WriteToFile(U, TmpFilePath);
|
|
|
|
auto ExitCode = ExecuteCommand(Cmd);
|
|
|
|
RemoveFile(TmpFilePath);
|
|
|
|
if (!ExitCode) {
|
|
|
|
U[Idx] = OriginalByte;
|
|
|
|
} else {
|
|
|
|
Changed = true;
|
|
|
|
Printf("CLEANSE: Replaced byte %zd with 0x%x\n", Idx, NewByte);
|
|
|
|
WriteToFile(U, OutputFilePath);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!Changed) break;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-08-03 10:04:16 -07:00
|
|
|
int MinimizeCrashInput(const std::vector<std::string> &Args,
|
2017-08-21 23:25:50 +00:00
|
|
|
const FuzzingOptions &Options) {
|
|
|
|
if (Inputs->size() != 1) {
|
|
|
|
Printf("ERROR: -minimize_crash should be given one input file\n");
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
std::string InputFilePath = Inputs->at(0);
|
2017-12-04 19:25:59 +00:00
|
|
|
Command BaseCmd(Args);
|
|
|
|
BaseCmd.removeFlag("minimize_crash");
|
|
|
|
BaseCmd.removeFlag("exact_artifact_path");
|
|
|
|
assert(BaseCmd.hasArgument(InputFilePath));
|
|
|
|
BaseCmd.removeArgument(InputFilePath);
|
2017-08-21 23:25:50 +00:00
|
|
|
if (Flags.runs <= 0 && Flags.max_total_time == 0) {
|
|
|
|
Printf("INFO: you need to specify -runs=N or "
|
|
|
|
"-max_total_time=N with -minimize_crash=1\n"
|
|
|
|
"INFO: defaulting to -max_total_time=600\n");
|
2017-12-04 19:25:59 +00:00
|
|
|
BaseCmd.addFlag("max_total_time", "600");
|
2017-08-21 23:25:50 +00:00
|
|
|
}
|
|
|
|
|
2017-12-04 19:25:59 +00:00
|
|
|
BaseCmd.combineOutAndErr();
|
2017-08-21 23:25:50 +00:00
|
|
|
|
|
|
|
std::string CurrentFilePath = InputFilePath;
|
|
|
|
while (true) {
|
|
|
|
Unit U = FileToVector(CurrentFilePath);
|
|
|
|
Printf("CRASH_MIN: minimizing crash input: '%s' (%zd bytes)\n",
|
|
|
|
CurrentFilePath.c_str(), U.size());
|
|
|
|
|
2017-12-04 19:25:59 +00:00
|
|
|
Command Cmd(BaseCmd);
|
|
|
|
Cmd.addArgument(CurrentFilePath);
|
2017-08-21 23:25:50 +00:00
|
|
|
|
2020-02-10 14:31:47 -08:00
|
|
|
Printf("CRASH_MIN: executing: %s\n", Cmd.toString().c_str());
|
|
|
|
std::string CmdOutput;
|
2020-02-12 15:43:44 -08:00
|
|
|
bool Success = ExecuteCommand(Cmd, &CmdOutput);
|
2020-02-10 14:31:47 -08:00
|
|
|
if (Success) {
|
2017-08-21 23:25:50 +00:00
|
|
|
Printf("ERROR: the input %s did not crash\n", CurrentFilePath.c_str());
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
Printf("CRASH_MIN: '%s' (%zd bytes) caused a crash. Will try to minimize "
|
|
|
|
"it further\n",
|
|
|
|
CurrentFilePath.c_str(), U.size());
|
2020-02-10 14:31:47 -08:00
|
|
|
auto DedupToken1 = GetDedupTokenFromCmdOutput(CmdOutput);
|
2017-08-21 23:25:50 +00:00
|
|
|
if (!DedupToken1.empty())
|
|
|
|
Printf("CRASH_MIN: DedupToken1: %s\n", DedupToken1.c_str());
|
|
|
|
|
|
|
|
std::string ArtifactPath =
|
|
|
|
Flags.exact_artifact_path
|
|
|
|
? Flags.exact_artifact_path
|
|
|
|
: Options.ArtifactPrefix + "minimized-from-" + Hash(U);
|
2017-12-04 19:25:59 +00:00
|
|
|
Cmd.addFlag("minimize_crash_internal_step", "1");
|
|
|
|
Cmd.addFlag("exact_artifact_path", ArtifactPath);
|
2020-02-10 14:31:47 -08:00
|
|
|
Printf("CRASH_MIN: executing: %s\n", Cmd.toString().c_str());
|
|
|
|
CmdOutput.clear();
|
2020-02-12 15:43:44 -08:00
|
|
|
Success = ExecuteCommand(Cmd, &CmdOutput);
|
2020-02-10 14:31:47 -08:00
|
|
|
Printf("%s", CmdOutput.c_str());
|
|
|
|
if (Success) {
|
2017-08-21 23:25:50 +00:00
|
|
|
if (Flags.exact_artifact_path) {
|
|
|
|
CurrentFilePath = Flags.exact_artifact_path;
|
|
|
|
WriteToFile(U, CurrentFilePath);
|
|
|
|
}
|
2023-03-07 21:55:33 -08:00
|
|
|
Printf("CRASH_MIN: failed to minimize beyond %s (%zu bytes), exiting\n",
|
2017-08-21 23:25:50 +00:00
|
|
|
CurrentFilePath.c_str(), U.size());
|
|
|
|
break;
|
|
|
|
}
|
2020-02-10 14:31:47 -08:00
|
|
|
auto DedupToken2 = GetDedupTokenFromCmdOutput(CmdOutput);
|
2017-08-21 23:25:50 +00:00
|
|
|
if (!DedupToken2.empty())
|
|
|
|
Printf("CRASH_MIN: DedupToken2: %s\n", DedupToken2.c_str());
|
|
|
|
|
|
|
|
if (DedupToken1 != DedupToken2) {
|
|
|
|
if (Flags.exact_artifact_path) {
|
|
|
|
CurrentFilePath = Flags.exact_artifact_path;
|
|
|
|
WriteToFile(U, CurrentFilePath);
|
|
|
|
}
|
|
|
|
Printf("CRASH_MIN: mismatch in dedup tokens"
|
|
|
|
" (looks like a different bug). Won't minimize further\n");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
CurrentFilePath = ArtifactPath;
|
|
|
|
Printf("*********************************\n");
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int MinimizeCrashInputInternalStep(Fuzzer *F, InputCorpus *Corpus) {
|
|
|
|
assert(Inputs->size() == 1);
|
|
|
|
std::string InputFilePath = Inputs->at(0);
|
|
|
|
Unit U = FileToVector(InputFilePath);
|
|
|
|
Printf("INFO: Starting MinimizeCrashInputInternalStep: %zd\n", U.size());
|
|
|
|
if (U.size() < 2) {
|
|
|
|
Printf("INFO: The input is small enough, exiting\n");
|
|
|
|
exit(0);
|
|
|
|
}
|
|
|
|
F->SetMaxInputLen(U.size());
|
|
|
|
F->SetMaxMutationLen(U.size() - 1);
|
|
|
|
F->MinimizeCrashLoop(U);
|
|
|
|
Printf("INFO: Done MinimizeCrashInputInternalStep, no crashes found\n");
|
|
|
|
exit(0);
|
|
|
|
}
|
|
|
|
|
2021-08-03 10:04:16 -07:00
|
|
|
void Merge(Fuzzer *F, FuzzingOptions &Options,
|
|
|
|
const std::vector<std::string> &Args,
|
|
|
|
const std::vector<std::string> &Corpora, const char *CFPathOrNull) {
|
2019-02-08 22:59:03 +00:00
|
|
|
if (Corpora.size() < 2) {
|
|
|
|
Printf("INFO: Merge requires two or more corpus dirs\n");
|
|
|
|
exit(0);
|
|
|
|
}
|
|
|
|
|
2021-08-03 10:04:16 -07:00
|
|
|
std::vector<SizedFile> OldCorpus, NewCorpus;
|
2019-02-08 22:59:03 +00:00
|
|
|
GetSizedFilesFromDir(Corpora[0], &OldCorpus);
|
|
|
|
for (size_t i = 1; i < Corpora.size(); i++)
|
|
|
|
GetSizedFilesFromDir(Corpora[i], &NewCorpus);
|
|
|
|
std::sort(OldCorpus.begin(), OldCorpus.end());
|
|
|
|
std::sort(NewCorpus.begin(), NewCorpus.end());
|
|
|
|
|
2020-02-10 18:22:09 -08:00
|
|
|
std::string CFPath = CFPathOrNull ? CFPathOrNull : TempPath("Merge", ".txt");
|
2021-08-03 10:04:16 -07:00
|
|
|
std::vector<std::string> NewFiles;
|
|
|
|
std::set<uint32_t> NewFeatures, NewCov;
|
2019-02-09 00:16:21 +00:00
|
|
|
CrashResistantMerge(Args, OldCorpus, NewCorpus, &NewFiles, {}, &NewFeatures,
|
2021-09-07 09:25:44 -07:00
|
|
|
{}, &NewCov, CFPath, true, Flags.set_cover_merge);
|
2019-02-09 00:16:21 +00:00
|
|
|
for (auto &Path : NewFiles)
|
2019-02-08 22:59:03 +00:00
|
|
|
F->WriteToOutputCorpus(FileToVector(Path, Options.MaxLen));
|
|
|
|
// We are done, delete the control file if it was a temporary one.
|
|
|
|
if (!Flags.merge_control_file)
|
|
|
|
RemoveFile(CFPath);
|
|
|
|
|
|
|
|
exit(0);
|
|
|
|
}
|
|
|
|
|
2021-08-03 10:04:16 -07:00
|
|
|
int AnalyzeDictionary(Fuzzer *F, const std::vector<Unit> &Dict,
|
|
|
|
UnitVector &Corpus) {
|
2023-03-07 21:55:33 -08:00
|
|
|
Printf("Started dictionary minimization (up to %zu tests)\n",
|
2017-08-21 23:25:50 +00:00
|
|
|
Dict.size() * Corpus.size() * 2);
|
|
|
|
|
|
|
|
// Scores and usage count for each dictionary unit.
|
2021-08-03 10:04:16 -07:00
|
|
|
std::vector<int> Scores(Dict.size());
|
|
|
|
std::vector<int> Usages(Dict.size());
|
2017-08-21 23:25:50 +00:00
|
|
|
|
2021-08-03 10:04:16 -07:00
|
|
|
std::vector<size_t> InitialFeatures;
|
|
|
|
std::vector<size_t> ModifiedFeatures;
|
2017-08-21 23:25:50 +00:00
|
|
|
for (auto &C : Corpus) {
|
|
|
|
// Get coverage for the testcase without modifications.
|
|
|
|
F->ExecuteCallback(C.data(), C.size());
|
|
|
|
InitialFeatures.clear();
|
2017-09-15 22:10:36 +00:00
|
|
|
TPC.CollectFeatures([&](size_t Feature) {
|
2017-08-21 23:25:50 +00:00
|
|
|
InitialFeatures.push_back(Feature);
|
|
|
|
});
|
|
|
|
|
|
|
|
for (size_t i = 0; i < Dict.size(); ++i) {
|
2021-08-03 10:04:16 -07:00
|
|
|
std::vector<uint8_t> Data = C;
|
2017-08-21 23:25:50 +00:00
|
|
|
auto StartPos = std::search(Data.begin(), Data.end(),
|
|
|
|
Dict[i].begin(), Dict[i].end());
|
|
|
|
// Skip dictionary unit, if the testcase does not contain it.
|
|
|
|
if (StartPos == Data.end())
|
|
|
|
continue;
|
|
|
|
|
|
|
|
++Usages[i];
|
|
|
|
while (StartPos != Data.end()) {
|
|
|
|
// Replace all occurrences of dictionary unit in the testcase.
|
|
|
|
auto EndPos = StartPos + Dict[i].size();
|
|
|
|
for (auto It = StartPos; It != EndPos; ++It)
|
|
|
|
*It ^= 0xFF;
|
|
|
|
|
|
|
|
StartPos = std::search(EndPos, Data.end(),
|
|
|
|
Dict[i].begin(), Dict[i].end());
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get coverage for testcase with masked occurrences of dictionary unit.
|
|
|
|
F->ExecuteCallback(Data.data(), Data.size());
|
|
|
|
ModifiedFeatures.clear();
|
2017-09-15 22:10:36 +00:00
|
|
|
TPC.CollectFeatures([&](size_t Feature) {
|
2017-08-21 23:25:50 +00:00
|
|
|
ModifiedFeatures.push_back(Feature);
|
|
|
|
});
|
|
|
|
|
|
|
|
if (InitialFeatures == ModifiedFeatures)
|
|
|
|
--Scores[i];
|
|
|
|
else
|
|
|
|
Scores[i] += 2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Printf("###### Useless dictionary elements. ######\n");
|
|
|
|
for (size_t i = 0; i < Dict.size(); ++i) {
|
|
|
|
// Dictionary units with positive score are treated as useful ones.
|
|
|
|
if (Scores[i] > 0)
|
2018-07-16 15:15:34 +00:00
|
|
|
continue;
|
2017-08-21 23:25:50 +00:00
|
|
|
|
|
|
|
Printf("\"");
|
|
|
|
PrintASCII(Dict[i].data(), Dict[i].size(), "\"");
|
|
|
|
Printf(" # Score: %d, Used: %d\n", Scores[i], Usages[i]);
|
|
|
|
}
|
|
|
|
Printf("###### End of useless dictionary elements. ######\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-08-03 10:04:16 -07:00
|
|
|
std::vector<std::string> ParseSeedInuts(const char *seed_inputs) {
|
2019-05-10 00:59:32 +00:00
|
|
|
// Parse -seed_inputs=file1,file2,... or -seed_inputs=@seed_inputs_file
|
2021-08-03 10:04:16 -07:00
|
|
|
std::vector<std::string> Files;
|
2019-05-10 00:59:32 +00:00
|
|
|
if (!seed_inputs) return Files;
|
|
|
|
std::string SeedInputs;
|
|
|
|
if (Flags.seed_inputs[0] == '@')
|
|
|
|
SeedInputs = FileToString(Flags.seed_inputs + 1); // File contains list.
|
|
|
|
else
|
|
|
|
SeedInputs = Flags.seed_inputs; // seed_inputs contains the list.
|
|
|
|
if (SeedInputs.empty()) {
|
|
|
|
Printf("seed_inputs is empty or @file does not exist.\n");
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
// Parse SeedInputs.
|
|
|
|
size_t comma_pos = 0;
|
|
|
|
while ((comma_pos = SeedInputs.find_last_of(',')) != std::string::npos) {
|
|
|
|
Files.push_back(SeedInputs.substr(comma_pos + 1));
|
|
|
|
SeedInputs = SeedInputs.substr(0, comma_pos);
|
|
|
|
}
|
|
|
|
Files.push_back(SeedInputs);
|
|
|
|
return Files;
|
|
|
|
}
|
|
|
|
|
2021-08-03 10:04:16 -07:00
|
|
|
static std::vector<SizedFile>
|
|
|
|
ReadCorpora(const std::vector<std::string> &CorpusDirs,
|
|
|
|
const std::vector<std::string> &ExtraSeedFiles) {
|
|
|
|
std::vector<SizedFile> SizedFiles;
|
2019-05-10 01:34:26 +00:00
|
|
|
size_t LastNumFiles = 0;
|
|
|
|
for (auto &Dir : CorpusDirs) {
|
|
|
|
GetSizedFilesFromDir(Dir, &SizedFiles);
|
|
|
|
Printf("INFO: % 8zd files found in %s\n", SizedFiles.size() - LastNumFiles,
|
|
|
|
Dir.c_str());
|
|
|
|
LastNumFiles = SizedFiles.size();
|
|
|
|
}
|
|
|
|
for (auto &File : ExtraSeedFiles)
|
|
|
|
if (auto Size = FileSize(File))
|
|
|
|
SizedFiles.push_back({File, Size});
|
|
|
|
return SizedFiles;
|
|
|
|
}
|
|
|
|
|
2017-08-21 23:25:50 +00:00
|
|
|
int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) {
|
|
|
|
using namespace fuzzer;
|
|
|
|
assert(argc && argv && "Argument pointers cannot be nullptr");
|
|
|
|
std::string Argv0((*argv)[0]);
|
|
|
|
EF = new ExternalFunctions();
|
|
|
|
if (EF->LLVMFuzzerInitialize)
|
|
|
|
EF->LLVMFuzzerInitialize(argc, argv);
|
2018-07-09 23:51:08 +00:00
|
|
|
if (EF->__msan_scoped_disable_interceptor_checks)
|
|
|
|
EF->__msan_scoped_disable_interceptor_checks();
|
2021-08-03 10:04:16 -07:00
|
|
|
const std::vector<std::string> Args(*argv, *argv + *argc);
|
2017-08-21 23:25:50 +00:00
|
|
|
assert(!Args.empty());
|
|
|
|
ProgName = new std::string(Args[0]);
|
|
|
|
if (Argv0 != *ProgName) {
|
|
|
|
Printf("ERROR: argv[0] has been modified in LLVMFuzzerInitialize\n");
|
|
|
|
exit(1);
|
|
|
|
}
|
2019-06-14 19:34:11 +00:00
|
|
|
ParseFlags(Args, EF);
|
2017-08-21 23:25:50 +00:00
|
|
|
if (Flags.help) {
|
|
|
|
PrintHelp();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Flags.close_fd_mask & 2)
|
|
|
|
DupAndCloseStderr();
|
|
|
|
if (Flags.close_fd_mask & 1)
|
|
|
|
CloseStdout();
|
|
|
|
|
|
|
|
if (Flags.jobs > 0 && Flags.workers == 0) {
|
|
|
|
Flags.workers = std::min(NumberOfCpuCores() / 2, Flags.jobs);
|
|
|
|
if (Flags.workers > 1)
|
|
|
|
Printf("Running %u workers\n", Flags.workers);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Flags.workers > 0 && Flags.jobs > 0)
|
|
|
|
return RunInMultipleProcesses(Args, Flags.workers, Flags.jobs);
|
|
|
|
|
|
|
|
FuzzingOptions Options;
|
|
|
|
Options.Verbosity = Flags.verbosity;
|
|
|
|
Options.MaxLen = Flags.max_len;
|
2018-02-13 20:52:15 +00:00
|
|
|
Options.LenControl = Flags.len_control;
|
2020-07-31 00:07:20 +00:00
|
|
|
Options.KeepSeed = Flags.keep_seed;
|
2017-08-21 23:25:50 +00:00
|
|
|
Options.UnitTimeoutSec = Flags.timeout;
|
|
|
|
Options.ErrorExitCode = Flags.error_exitcode;
|
|
|
|
Options.TimeoutExitCode = Flags.timeout_exitcode;
|
2019-02-12 02:18:53 +00:00
|
|
|
Options.IgnoreTimeouts = Flags.ignore_timeouts;
|
|
|
|
Options.IgnoreOOMs = Flags.ignore_ooms;
|
2019-02-15 21:51:15 +00:00
|
|
|
Options.IgnoreCrashes = Flags.ignore_crashes;
|
2017-08-21 23:25:50 +00:00
|
|
|
Options.MaxTotalTimeSec = Flags.max_total_time;
|
|
|
|
Options.DoCrossOver = Flags.cross_over;
|
2020-09-01 16:22:59 +00:00
|
|
|
Options.CrossOverUniformDist = Flags.cross_over_uniform_dist;
|
2017-08-21 23:25:50 +00:00
|
|
|
Options.MutateDepth = Flags.mutate_depth;
|
2017-12-01 19:18:38 +00:00
|
|
|
Options.ReduceDepth = Flags.reduce_depth;
|
2017-08-21 23:25:50 +00:00
|
|
|
Options.UseCounters = Flags.use_counters;
|
|
|
|
Options.UseMemmem = Flags.use_memmem;
|
|
|
|
Options.UseCmp = Flags.use_cmp;
|
|
|
|
Options.UseValueProfile = Flags.use_value_profile;
|
|
|
|
Options.Shrink = Flags.shrink;
|
|
|
|
Options.ReduceInputs = Flags.reduce_inputs;
|
|
|
|
Options.ShuffleAtStartUp = Flags.shuffle;
|
|
|
|
Options.PreferSmall = Flags.prefer_small;
|
|
|
|
Options.ReloadIntervalSec = Flags.reload;
|
|
|
|
Options.OnlyASCII = Flags.only_ascii;
|
|
|
|
Options.DetectLeaks = Flags.detect_leaks;
|
2017-10-23 22:04:30 +00:00
|
|
|
Options.PurgeAllocatorIntervalSec = Flags.purge_allocator_interval;
|
2017-08-21 23:25:50 +00:00
|
|
|
Options.TraceMalloc = Flags.trace_malloc;
|
|
|
|
Options.RssLimitMb = Flags.rss_limit_mb;
|
2017-12-01 22:12:04 +00:00
|
|
|
Options.MallocLimitMb = Flags.malloc_limit_mb;
|
|
|
|
if (!Options.MallocLimitMb)
|
|
|
|
Options.MallocLimitMb = Options.RssLimitMb;
|
2017-08-21 23:25:50 +00:00
|
|
|
if (Flags.runs >= 0)
|
|
|
|
Options.MaxNumberOfRuns = Flags.runs;
|
2020-08-26 09:06:12 -07:00
|
|
|
if (!Inputs->empty() && !Flags.minimize_crash_internal_step) {
|
|
|
|
// Ensure output corpus assumed to be the first arbitrary argument input
|
|
|
|
// is not a path to an existing file.
|
|
|
|
std::string OutputCorpusDir = (*Inputs)[0];
|
|
|
|
if (!IsFile(OutputCorpusDir)) {
|
|
|
|
Options.OutputCorpus = OutputCorpusDir;
|
2020-09-03 07:58:45 -07:00
|
|
|
ValidateDirectoryExists(Options.OutputCorpus, Flags.create_missing_dirs);
|
2020-08-26 09:06:12 -07:00
|
|
|
}
|
|
|
|
}
|
2017-08-21 23:25:50 +00:00
|
|
|
Options.ReportSlowUnits = Flags.report_slow_units;
|
2020-08-26 09:06:12 -07:00
|
|
|
if (Flags.artifact_prefix) {
|
2017-08-21 23:25:50 +00:00
|
|
|
Options.ArtifactPrefix = Flags.artifact_prefix;
|
2020-08-26 09:06:12 -07:00
|
|
|
|
|
|
|
// Since the prefix could be a full path to a file name prefix, assume
|
|
|
|
// that if the path ends with the platform's separator that a directory
|
|
|
|
// is desired
|
|
|
|
std::string ArtifactPathDir = Options.ArtifactPrefix;
|
|
|
|
if (!IsSeparator(ArtifactPathDir[ArtifactPathDir.length() - 1])) {
|
|
|
|
ArtifactPathDir = DirName(ArtifactPathDir);
|
|
|
|
}
|
2020-09-03 07:58:45 -07:00
|
|
|
ValidateDirectoryExists(ArtifactPathDir, Flags.create_missing_dirs);
|
2020-08-26 09:06:12 -07:00
|
|
|
}
|
|
|
|
if (Flags.exact_artifact_path) {
|
2017-08-21 23:25:50 +00:00
|
|
|
Options.ExactArtifactPath = Flags.exact_artifact_path;
|
2020-09-03 07:58:45 -07:00
|
|
|
ValidateDirectoryExists(DirName(Options.ExactArtifactPath),
|
|
|
|
Flags.create_missing_dirs);
|
2020-08-26 09:06:12 -07:00
|
|
|
}
|
2021-08-03 10:04:16 -07:00
|
|
|
std::vector<Unit> Dictionary;
|
2017-08-21 23:25:50 +00:00
|
|
|
if (Flags.dict)
|
|
|
|
if (!ParseDictionaryFile(FileToString(Flags.dict), &Dictionary))
|
|
|
|
return 1;
|
|
|
|
if (Flags.verbosity > 0 && !Dictionary.empty())
|
|
|
|
Printf("Dictionary: %zd entries\n", Dictionary.size());
|
2019-05-10 01:34:26 +00:00
|
|
|
bool RunIndividualFiles = AllInputsAreFiles();
|
2017-08-21 23:25:50 +00:00
|
|
|
Options.SaveArtifacts =
|
2019-05-10 01:34:26 +00:00
|
|
|
!RunIndividualFiles || Flags.minimize_crash_internal_step;
|
2017-08-21 23:25:50 +00:00
|
|
|
Options.PrintNewCovPcs = Flags.print_pcs;
|
2017-08-25 20:09:25 +00:00
|
|
|
Options.PrintNewCovFuncs = Flags.print_funcs;
|
2017-08-21 23:25:50 +00:00
|
|
|
Options.PrintFinalStats = Flags.print_final_stats;
|
|
|
|
Options.PrintCorpusStats = Flags.print_corpus_stats;
|
|
|
|
Options.PrintCoverage = Flags.print_coverage;
|
2020-10-23 11:07:30 -07:00
|
|
|
Options.PrintFullCoverage = Flags.print_full_coverage;
|
2017-08-21 23:25:50 +00:00
|
|
|
if (Flags.exit_on_src_pos)
|
|
|
|
Options.ExitOnSrcPos = Flags.exit_on_src_pos;
|
|
|
|
if (Flags.exit_on_item)
|
|
|
|
Options.ExitOnItem = Flags.exit_on_item;
|
2018-05-16 23:26:37 +00:00
|
|
|
if (Flags.focus_function)
|
|
|
|
Options.FocusFunction = Flags.focus_function;
|
2018-06-06 01:23:29 +00:00
|
|
|
if (Flags.data_flow_trace)
|
|
|
|
Options.DataFlowTrace = Flags.data_flow_trace;
|
2020-08-26 09:06:12 -07:00
|
|
|
if (Flags.features_dir) {
|
2019-04-13 00:20:31 +00:00
|
|
|
Options.FeaturesDir = Flags.features_dir;
|
2020-09-03 07:58:45 -07:00
|
|
|
ValidateDirectoryExists(Options.FeaturesDir, Flags.create_missing_dirs);
|
2020-08-26 09:06:12 -07:00
|
|
|
}
|
2020-07-08 19:30:53 +00:00
|
|
|
if (Flags.mutation_graph_file)
|
|
|
|
Options.MutationGraphFile = Flags.mutation_graph_file;
|
2019-05-23 00:22:46 +00:00
|
|
|
if (Flags.collect_data_flow)
|
|
|
|
Options.CollectDataFlow = Flags.collect_data_flow;
|
2019-06-14 22:56:50 +00:00
|
|
|
if (Flags.stop_file)
|
|
|
|
Options.StopFile = Flags.stop_file;
|
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-19 10:28:18 -07:00
|
|
|
Options.Entropic = Flags.entropic;
|
|
|
|
Options.EntropicFeatureFrequencyThreshold =
|
|
|
|
(size_t)Flags.entropic_feature_frequency_threshold;
|
|
|
|
Options.EntropicNumberOfRarestFeatures =
|
|
|
|
(size_t)Flags.entropic_number_of_rarest_features;
|
2020-08-17 16:59:59 +00:00
|
|
|
Options.EntropicScalePerExecTime = Flags.entropic_scale_per_exec_time;
|
2020-09-15 10:33:23 -07:00
|
|
|
if (!Options.FocusFunction.empty())
|
|
|
|
Options.Entropic = false; // FocusFunction overrides entropic scheduling.
|
|
|
|
if (Options.Entropic)
|
2023-03-07 21:55:33 -08:00
|
|
|
Printf("INFO: Running with entropic power schedule (0x%zX, %zu).\n",
|
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-19 10:28:18 -07:00
|
|
|
Options.EntropicFeatureFrequencyThreshold,
|
|
|
|
Options.EntropicNumberOfRarestFeatures);
|
|
|
|
struct EntropicOptions Entropic;
|
|
|
|
Entropic.Enabled = Options.Entropic;
|
|
|
|
Entropic.FeatureFrequencyThreshold =
|
|
|
|
Options.EntropicFeatureFrequencyThreshold;
|
|
|
|
Entropic.NumberOfRarestFeatures = Options.EntropicNumberOfRarestFeatures;
|
2020-08-17 16:59:59 +00:00
|
|
|
Entropic.ScalePerExecTime = Options.EntropicScalePerExecTime;
|
2017-08-21 23:25:50 +00:00
|
|
|
|
|
|
|
unsigned Seed = Flags.seed;
|
|
|
|
// Initialize Seed.
|
|
|
|
if (Seed == 0)
|
[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-11 16:00:53 -08:00
|
|
|
Seed = static_cast<unsigned>(
|
|
|
|
std::chrono::system_clock::now().time_since_epoch().count() + GetPid());
|
2017-08-21 23:25:50 +00:00
|
|
|
if (Flags.verbosity)
|
|
|
|
Printf("INFO: Seed: %u\n", Seed);
|
|
|
|
|
2023-03-07 22:04:31 -08:00
|
|
|
if (Flags.collect_data_flow && Flags.data_flow_trace && !Flags.fork &&
|
2021-09-07 09:25:44 -07:00
|
|
|
!(Flags.merge || Flags.set_cover_merge)) {
|
2019-05-10 01:34:26 +00:00
|
|
|
if (RunIndividualFiles)
|
|
|
|
return CollectDataFlow(Flags.collect_data_flow, Flags.data_flow_trace,
|
|
|
|
ReadCorpora({}, *Inputs));
|
|
|
|
else
|
|
|
|
return CollectDataFlow(Flags.collect_data_flow, Flags.data_flow_trace,
|
|
|
|
ReadCorpora(*Inputs, {}));
|
|
|
|
}
|
2019-05-10 00:59:32 +00:00
|
|
|
|
2021-07-02 09:44:54 -07:00
|
|
|
Random Rand(Seed);
|
|
|
|
auto *MD = new MutationDispatcher(Rand, Options);
|
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-19 10:28:18 -07:00
|
|
|
auto *Corpus = new InputCorpus(Options.OutputCorpus, Entropic);
|
2017-08-21 23:25:50 +00:00
|
|
|
auto *F = new Fuzzer(Callback, *Corpus, *MD, Options);
|
|
|
|
|
|
|
|
for (auto &U: Dictionary)
|
|
|
|
if (U.size() <= Word::GetMaxSize())
|
|
|
|
MD->AddWordToManualDictionary(Word(U.data(), U.size()));
|
|
|
|
|
2019-12-12 08:48:54 -08:00
|
|
|
// Threads are only supported by Chrome. Don't use them with emscripten
|
|
|
|
// for now.
|
|
|
|
#if !LIBFUZZER_EMSCRIPTEN
|
2017-08-21 23:25:50 +00:00
|
|
|
StartRssThread(F, Flags.rss_limit_mb);
|
2019-12-12 08:48:54 -08:00
|
|
|
#endif // LIBFUZZER_EMSCRIPTEN
|
2017-08-21 23:25:50 +00:00
|
|
|
|
|
|
|
Options.HandleAbrt = Flags.handle_abrt;
|
2020-08-11 22:16:08 +02:00
|
|
|
Options.HandleAlrm = !Flags.minimize_crash;
|
2017-08-21 23:25:50 +00:00
|
|
|
Options.HandleBus = Flags.handle_bus;
|
|
|
|
Options.HandleFpe = Flags.handle_fpe;
|
|
|
|
Options.HandleIll = Flags.handle_ill;
|
|
|
|
Options.HandleInt = Flags.handle_int;
|
|
|
|
Options.HandleSegv = Flags.handle_segv;
|
|
|
|
Options.HandleTerm = Flags.handle_term;
|
|
|
|
Options.HandleXfsz = Flags.handle_xfsz;
|
2017-11-09 20:30:19 +00:00
|
|
|
Options.HandleUsr1 = Flags.handle_usr1;
|
|
|
|
Options.HandleUsr2 = Flags.handle_usr2;
|
2020-11-12 12:37:35 -08:00
|
|
|
Options.HandleWinExcept = Flags.handle_winexcept;
|
|
|
|
|
2017-08-21 23:25:50 +00:00
|
|
|
SetSignalHandler(Options);
|
|
|
|
|
|
|
|
std::atexit(Fuzzer::StaticExitCallback);
|
|
|
|
|
|
|
|
if (Flags.minimize_crash)
|
|
|
|
return MinimizeCrashInput(Args, Options);
|
|
|
|
|
|
|
|
if (Flags.minimize_crash_internal_step)
|
|
|
|
return MinimizeCrashInputInternalStep(F, Corpus);
|
|
|
|
|
|
|
|
if (Flags.cleanse_crash)
|
|
|
|
return CleanseCrashInput(Args, Options);
|
|
|
|
|
2019-05-10 01:34:26 +00:00
|
|
|
if (RunIndividualFiles) {
|
2017-08-21 23:25:50 +00:00
|
|
|
Options.SaveArtifacts = false;
|
|
|
|
int Runs = std::max(1, Flags.runs);
|
|
|
|
Printf("%s: Running %zd inputs %d time(s) each.\n", ProgName->c_str(),
|
|
|
|
Inputs->size(), Runs);
|
|
|
|
for (auto &Path : *Inputs) {
|
|
|
|
auto StartTime = system_clock::now();
|
|
|
|
Printf("Running: %s\n", Path.c_str());
|
|
|
|
for (int Iter = 0; Iter < Runs; Iter++)
|
|
|
|
RunOneTest(F, Path.c_str(), Options.MaxLen);
|
|
|
|
auto StopTime = system_clock::now();
|
|
|
|
auto MS = duration_cast<milliseconds>(StopTime - StartTime).count();
|
2023-03-07 21:55:33 -08:00
|
|
|
Printf("Executed %s in %ld ms\n", Path.c_str(), (long)MS);
|
2017-08-21 23:25:50 +00:00
|
|
|
}
|
|
|
|
Printf("***\n"
|
|
|
|
"*** NOTE: fuzzing was not performed, you have only\n"
|
|
|
|
"*** executed the target code on a fixed set of inputs.\n"
|
|
|
|
"***\n");
|
|
|
|
F->PrintFinalStats();
|
|
|
|
exit(0);
|
|
|
|
}
|
|
|
|
|
2021-09-08 06:18:19 -07:00
|
|
|
Options.ForkCorpusGroups = Flags.fork_corpus_groups;
|
2019-02-08 21:27:23 +00:00
|
|
|
if (Flags.fork)
|
2019-02-14 00:25:43 +00:00
|
|
|
FuzzWithFork(F->GetMD().GetRand(), Options, Args, *Inputs, Flags.fork);
|
2019-02-08 21:27:23 +00:00
|
|
|
|
2021-09-07 09:25:44 -07:00
|
|
|
if (Flags.merge || Flags.set_cover_merge)
|
2019-02-08 22:59:03 +00:00
|
|
|
Merge(F, Options, Args, *Inputs, Flags.merge_control_file);
|
2017-11-09 01:05:29 +00:00
|
|
|
|
|
|
|
if (Flags.merge_inner) {
|
2017-08-29 02:05:01 +00:00
|
|
|
const size_t kDefaultMaxMergeLen = 1 << 20;
|
2017-08-21 23:25:50 +00:00
|
|
|
if (Options.MaxLen == 0)
|
2017-08-29 02:05:01 +00:00
|
|
|
F->SetMaxInputLen(kDefaultMaxMergeLen);
|
2017-11-09 01:05:29 +00:00
|
|
|
assert(Flags.merge_control_file);
|
2021-09-07 09:25:44 -07:00
|
|
|
F->CrashResistantMergeInternalStep(Flags.merge_control_file,
|
|
|
|
!strncmp(Flags.merge_inner, "2", 1));
|
2017-08-21 23:25:50 +00:00
|
|
|
exit(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Flags.analyze_dict) {
|
2017-08-29 02:05:01 +00:00
|
|
|
size_t MaxLen = INT_MAX; // Large max length.
|
|
|
|
UnitVector InitialCorpus;
|
|
|
|
for (auto &Inp : *Inputs) {
|
|
|
|
Printf("Loading corpus dir: %s\n", Inp.c_str());
|
|
|
|
ReadDirToVectorOfUnits(Inp.c_str(), &InitialCorpus, nullptr,
|
|
|
|
MaxLen, /*ExitOnError=*/false);
|
|
|
|
}
|
|
|
|
|
2017-08-21 23:25:50 +00:00
|
|
|
if (Dictionary.empty() || Inputs->empty()) {
|
|
|
|
Printf("ERROR: can't analyze dict without dict and corpus provided\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
if (AnalyzeDictionary(F, Dictionary, InitialCorpus)) {
|
|
|
|
Printf("Dictionary analysis failed\n");
|
|
|
|
exit(1);
|
|
|
|
}
|
2018-03-13 14:35:10 +00:00
|
|
|
Printf("Dictionary analysis succeeded\n");
|
2017-08-21 23:25:50 +00:00
|
|
|
exit(0);
|
|
|
|
}
|
|
|
|
|
2019-05-10 01:34:26 +00:00
|
|
|
auto CorporaFiles = ReadCorpora(*Inputs, ParseSeedInuts(Flags.seed_inputs));
|
|
|
|
F->Loop(CorporaFiles);
|
2017-08-21 23:25:50 +00:00
|
|
|
|
|
|
|
if (Flags.verbosity)
|
|
|
|
Printf("Done %zd runs in %zd second(s)\n", F->getTotalNumberOfRuns(),
|
|
|
|
F->secondsSinceProcessStartUp());
|
|
|
|
F->PrintFinalStats();
|
|
|
|
|
|
|
|
exit(0); // Don't let F destroy itself.
|
|
|
|
}
|
|
|
|
|
2020-07-27 18:15:35 +00:00
|
|
|
extern "C" ATTRIBUTE_INTERFACE int
|
|
|
|
LLVMFuzzerRunDriver(int *argc, char ***argv,
|
|
|
|
int (*UserCb)(const uint8_t *Data, size_t Size)) {
|
|
|
|
return FuzzerDriver(argc, argv, UserCb);
|
|
|
|
}
|
|
|
|
|
2017-08-21 23:25:50 +00:00
|
|
|
// Storage for global ExternalFunctions object.
|
|
|
|
ExternalFunctions *EF = nullptr;
|
|
|
|
|
|
|
|
} // namespace fuzzer
|