Bug 1450047 - part 2 - updated in-tree copy of libFuzzer; r=decoder

MozReview-Commit-ID: I1LZ8N82kr7

--HG--
extra : rebase_source : fefb34f48a7eed9428bc055224bb5868dcc37430
This commit is contained in:
Alex Gaynor 2018-03-29 14:18:36 -04:00
Родитель 94ed3ca79d
Коммит 4259227b9c
40 изменённых файлов: 1883 добавлений и 978 удалений

Просмотреть файл

@ -0,0 +1,49 @@
//===- FuzzerExtraCounters.cpp - Extra coverage counters ------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
// Coverage counters from Clang's SourceBasedCodeCoverage.
//===----------------------------------------------------------------------===//
// Support for SourceBasedCodeCoverage is experimental:
// * Works only for the main binary, not DSOs yet.
// * Works only on Linux.
// * Does not implement print_pcs/print_coverage yet.
// * Is not fully evaluated for performance and sensitivity.
// We expect large performance drop due to 64-bit counters,
// and *maybe* better sensitivity due to more fine-grained counters.
// Preliminary comparison on a single benchmark (RE2) shows
// a bit worse sensitivity though.
#include "FuzzerDefs.h"
#if LIBFUZZER_LINUX
__attribute__((weak)) extern uint64_t __start___llvm_prf_cnts;
__attribute__((weak)) extern uint64_t __stop___llvm_prf_cnts;
namespace fuzzer {
uint64_t *ClangCountersBegin() { return &__start___llvm_prf_cnts; }
uint64_t *ClangCountersEnd() { return &__stop___llvm_prf_cnts; }
} // namespace fuzzer
#else
// TODO: Implement on Mac (if the data shows it's worth it).
//__attribute__((visibility("hidden")))
//extern uint64_t CountersStart __asm("section$start$__DATA$__llvm_prf_cnts");
//__attribute__((visibility("hidden")))
//extern uint64_t CountersEnd __asm("section$end$__DATA$__llvm_prf_cnts");
namespace fuzzer {
uint64_t *ClangCountersBegin() { return nullptr; }
uint64_t *ClangCountersEnd() { return nullptr; }
} // namespace fuzzer
#endif
namespace fuzzer {
ATTRIBUTE_NO_SANITIZE_ALL
void ClearClangCounters() { // hand-written memset, don't asan-ify.
for (auto P = ClangCountersBegin(); P < ClangCountersEnd(); P++)
*P = 0;
}
}

Просмотреть файл

@ -0,0 +1,180 @@
//===- FuzzerCommand.h - Interface representing a process -------*- C++ -* ===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
// FuzzerCommand represents a command to run in a subprocess. It allows callers
// to manage command line arguments and output and error streams.
//===----------------------------------------------------------------------===//
#ifndef LLVM_FUZZER_COMMAND_H
#define LLVM_FUZZER_COMMAND_H
#include "FuzzerDefs.h"
#include "FuzzerIO.h"
#include <algorithm>
#include <sstream>
#include <string>
#include <vector>
namespace fuzzer {
class Command final {
public:
// This command line flag is used to indicate that the remaining command line
// is immutable, meaning this flag effectively marks the end of the mutable
// argument list.
static inline const char *ignoreRemainingArgs() {
static const char *kIgnoreRemaining = "-ignore_remaining_args=1";
return kIgnoreRemaining;
}
Command() : CombinedOutAndErr(false) {}
explicit Command(const Vector<std::string> &ArgsToAdd)
: Args(ArgsToAdd), CombinedOutAndErr(false) {}
explicit Command(const Command &Other)
: Args(Other.Args), CombinedOutAndErr(Other.CombinedOutAndErr),
OutputFile(Other.OutputFile) {}
Command &operator=(const Command &Other) {
Args = Other.Args;
CombinedOutAndErr = Other.CombinedOutAndErr;
OutputFile = Other.OutputFile;
return *this;
}
~Command() {}
// Returns true if the given Arg is present in Args. Only checks up to
// "-ignore_remaining_args=1".
bool hasArgument(const std::string &Arg) const {
auto i = endMutableArgs();
return std::find(Args.begin(), i, Arg) != i;
}
// Gets all of the current command line arguments, **including** those after
// "-ignore-remaining-args=1".
const Vector<std::string> &getArguments() const { return Args; }
// Adds the given argument before "-ignore_remaining_args=1", or at the end
// if that flag isn't present.
void addArgument(const std::string &Arg) {
Args.insert(endMutableArgs(), Arg);
}
// Adds all given arguments before "-ignore_remaining_args=1", or at the end
// if that flag isn't present.
void addArguments(const Vector<std::string> &ArgsToAdd) {
Args.insert(endMutableArgs(), ArgsToAdd.begin(), ArgsToAdd.end());
}
// Removes the given argument from the command argument list. Ignores any
// occurrences after "-ignore_remaining_args=1", if present.
void removeArgument(const std::string &Arg) {
auto i = endMutableArgs();
Args.erase(std::remove(Args.begin(), i, Arg), i);
}
// Like hasArgument, but checks for "-[Flag]=...".
bool hasFlag(const std::string &Flag) {
std::string Arg("-" + Flag + "=");
auto IsMatch = [&](const std::string &Other) {
return Arg.compare(0, std::string::npos, Other, 0, Arg.length()) == 0;
};
return std::any_of(Args.begin(), endMutableArgs(), IsMatch);
}
// Returns the value of the first instance of a given flag, or an empty string
// if the flag isn't present. Ignores any occurrences after
// "-ignore_remaining_args=1", if present.
std::string getFlagValue(const std::string &Flag) {
std::string Arg("-" + Flag + "=");
auto IsMatch = [&](const std::string &Other) {
return Arg.compare(0, std::string::npos, Other, 0, Arg.length()) == 0;
};
auto i = endMutableArgs();
auto j = std::find_if(Args.begin(), i, IsMatch);
std::string result;
if (j != i) {
result = j->substr(Arg.length());
}
return result;
}
// Like AddArgument, but adds "-[Flag]=[Value]".
void addFlag(const std::string &Flag, const std::string &Value) {
addArgument("-" + Flag + "=" + Value);
}
// Like RemoveArgument, but removes "-[Flag]=...".
void removeFlag(const std::string &Flag) {
std::string Arg("-" + Flag + "=");
auto IsMatch = [&](const std::string &Other) {
return Arg.compare(0, std::string::npos, Other, 0, Arg.length()) == 0;
};
auto i = endMutableArgs();
Args.erase(std::remove_if(Args.begin(), i, IsMatch), i);
}
// Returns whether the command's stdout is being written to an output file.
bool hasOutputFile() const { return !OutputFile.empty(); }
// Returns the currently set output file.
const std::string &getOutputFile() const { return OutputFile; }
// Configures the command to redirect its output to the name file.
void setOutputFile(const std::string &FileName) { OutputFile = FileName; }
// Returns whether the command's stderr is redirected to stdout.
bool isOutAndErrCombined() const { return CombinedOutAndErr; }
// Sets whether to redirect the command's stderr to its stdout.
void combineOutAndErr(bool combine = true) { CombinedOutAndErr = combine; }
// Returns a string representation of the command. On many systems this will
// be the equivalent command line.
std::string toString() const {
std::stringstream SS;
for (auto arg : getArguments())
SS << arg << " ";
if (hasOutputFile())
SS << ">" << getOutputFile() << " ";
if (isOutAndErrCombined())
SS << "2>&1 ";
std::string result = SS.str();
if (!result.empty())
result = result.substr(0, result.length() - 1);
return result;
}
private:
Command(Command &&Other) = delete;
Command &operator=(Command &&Other) = delete;
Vector<std::string>::iterator endMutableArgs() {
return std::find(Args.begin(), Args.end(), ignoreRemainingArgs());
}
Vector<std::string>::const_iterator endMutableArgs() const {
return std::find(Args.begin(), Args.end(), ignoreRemainingArgs());
}
// The command arguments. Args[0] is the command name.
Vector<std::string> Args;
// True indicates stderr is redirected to stdout.
bool CombinedOutAndErr;
// If not empty, stdout is redirected to the named file.
std::string OutputFile;
};
} // namespace fuzzer
#endif // LLVM_FUZZER_COMMAND_H

Просмотреть файл

@ -34,6 +34,9 @@ struct InputInfo {
size_t NumExecutedMutations = 0;
size_t NumSuccessfullMutations = 0;
bool MayDeleteFile = false;
bool Reduced = false;
Vector<uint32_t> UniqFeatureSet;
float FeatureFrequencyScore = 1.0;
};
class InputCorpus {
@ -42,6 +45,7 @@ class InputCorpus {
InputCorpus(const std::string &OutputCorpus) : OutputCorpus(OutputCorpus) {
memset(InputSizesPerFeature, 0, sizeof(InputSizesPerFeature));
memset(SmallestElementPerFeature, 0, sizeof(SmallestElementPerFeature));
memset(FeatureFrequency, 0, sizeof(FeatureFrequency));
}
~InputCorpus() {
for (auto II : Inputs)
@ -68,24 +72,73 @@ class InputCorpus {
}
bool empty() const { return Inputs.empty(); }
const Unit &operator[] (size_t Idx) const { return Inputs[Idx]->U; }
void AddToCorpus(const Unit &U, size_t NumFeatures,
bool MayDeleteFile = false) {
void AddToCorpus(const Unit &U, size_t NumFeatures, bool MayDeleteFile,
const Vector<uint32_t> &FeatureSet) {
assert(!U.empty());
uint8_t Hash[kSHA1NumBytes];
if (FeatureDebug)
Printf("ADD_TO_CORPUS %zd NF %zd\n", Inputs.size(), NumFeatures);
ComputeSHA1(U.data(), U.size(), Hash);
Hashes.insert(Sha1ToString(Hash));
Inputs.push_back(new InputInfo());
InputInfo &II = *Inputs.back();
II.U = U;
II.NumFeatures = NumFeatures;
II.MayDeleteFile = MayDeleteFile;
memcpy(II.Sha1, Hash, kSHA1NumBytes);
II.UniqFeatureSet = FeatureSet;
std::sort(II.UniqFeatureSet.begin(), II.UniqFeatureSet.end());
ComputeSHA1(U.data(), U.size(), II.Sha1);
Hashes.insert(Sha1ToString(II.Sha1));
UpdateCorpusDistribution();
PrintCorpus();
// ValidateFeatureSet();
}
// Debug-only
void PrintUnit(const Unit &U) {
if (!FeatureDebug) return;
for (uint8_t C : U) {
if (C != 'F' && C != 'U' && C != 'Z')
C = '.';
Printf("%c", C);
}
}
// Debug-only
void PrintFeatureSet(const Vector<uint32_t> &FeatureSet) {
if (!FeatureDebug) return;
Printf("{");
for (uint32_t Feature: FeatureSet)
Printf("%u,", Feature);
Printf("}");
}
// Debug-only
void PrintCorpus() {
if (!FeatureDebug) return;
Printf("======= CORPUS:\n");
int i = 0;
for (auto II : Inputs) {
if (std::find(II->U.begin(), II->U.end(), 'F') != II->U.end()) {
Printf("[%2d] ", i);
Printf("%s sz=%zd ", Sha1ToString(II->Sha1).c_str(), II->U.size());
PrintUnit(II->U);
Printf(" ");
PrintFeatureSet(II->UniqFeatureSet);
Printf("\n");
}
i++;
}
}
void Replace(InputInfo *II, const Unit &U) {
assert(II->U.size() > U.size());
Hashes.erase(Sha1ToString(II->Sha1));
DeleteFile(*II);
ComputeSHA1(U.data(), U.size(), II->Sha1);
Hashes.insert(Sha1ToString(II->Sha1));
II->U = U;
II->Reduced = true;
UpdateCorpusDistribution();
}
bool HasUnit(const Unit &U) { return Hashes.count(Hash(U)); }
bool HasUnit(const std::string &H) { return Hashes.count(H); }
InputInfo &ChooseUnitToMutate(Random &Rand) {
@ -95,8 +148,6 @@ class InputCorpus {
};
// Returns an index of random unit from the corpus to mutate.
// Hypothesis: units added to the corpus last are more likely to be
// interesting. This function gives more weight to the more recent units.
size_t ChooseUnitIdxToMutate(Random &Rand) {
size_t Idx = static_cast<size_t>(CorpusDistribution(Rand));
assert(Idx < Inputs.size());
@ -124,16 +175,20 @@ class InputCorpus {
Printf("\n");
}
void DeleteInput(size_t Idx) {
InputInfo &II = *Inputs[Idx];
void DeleteFile(const InputInfo &II) {
if (!OutputCorpus.empty() && II.MayDeleteFile)
RemoveFile(DirPlusFile(OutputCorpus, Sha1ToString(II.Sha1)));
}
void DeleteInput(size_t Idx) {
InputInfo &II = *Inputs[Idx];
DeleteFile(II);
Unit().swap(II.U);
if (FeatureDebug)
Printf("EVICTED %zd\n", Idx);
}
void AddFeature(size_t Idx, uint32_t NewSize, bool Shrink) {
bool AddFeature(size_t Idx, uint32_t NewSize, bool Shrink) {
assert(NewSize);
Idx = Idx % kFeatureSetSize;
uint32_t OldSize = GetFeature(Idx);
@ -153,19 +208,28 @@ class InputCorpus {
Printf("ADD FEATURE %zd sz %d\n", Idx, NewSize);
SmallestElementPerFeature[Idx] = Inputs.size();
InputSizesPerFeature[Idx] = NewSize;
CountingFeatures = true;
return true;
}
return false;
}
void UpdateFeatureFrequency(size_t Idx) {
FeatureFrequency[Idx % kFeatureSetSize]++;
}
float GetFeatureFrequency(size_t Idx) const {
return FeatureFrequency[Idx % kFeatureSetSize];
}
void UpdateFeatureFrequencyScore(InputInfo *II) {
const float kMin = 0.01, kMax = 100.;
II->FeatureFrequencyScore = kMin;
for (auto Idx : II->UniqFeatureSet)
II->FeatureFrequencyScore += 1. / (GetFeatureFrequency(Idx) + 1.);
II->FeatureFrequencyScore = Min(II->FeatureFrequencyScore, kMax);
}
size_t NumFeatures() const { return NumAddedFeatures; }
size_t NumFeatureUpdates() const { return NumUpdatedFeatures; }
void ResetFeatureSet() {
assert(Inputs.empty());
memset(InputSizesPerFeature, 0, sizeof(InputSizesPerFeature));
memset(SmallestElementPerFeature, 0, sizeof(SmallestElementPerFeature));
}
private:
static const bool FeatureDebug = false;
@ -173,7 +237,6 @@ private:
size_t GetFeature(size_t Idx) const { return InputSizesPerFeature[Idx]; }
void ValidateFeatureSet() {
if (!CountingFeatures) return;
if (FeatureDebug)
PrintFeatureSet();
for (size_t Idx = 0; Idx < kFeatureSetSize; Idx++)
@ -189,32 +252,47 @@ private:
// Updates the probability distribution for the units in the corpus.
// Must be called whenever the corpus or unit weights are changed.
//
// Hypothesis: units added to the corpus last are more interesting.
//
// Hypothesis: inputs with infrequent features are more interesting.
void UpdateCorpusDistribution() {
size_t N = Inputs.size();
assert(N);
Intervals.resize(N + 1);
Weights.resize(N);
std::iota(Intervals.begin(), Intervals.end(), 0);
if (CountingFeatures)
for (size_t i = 0; i < N; i++)
Weights[i] = Inputs[i]->NumFeatures
? (i + 1) * Inputs[i]->FeatureFrequencyScore
: 0.;
if (FeatureDebug) {
for (size_t i = 0; i < N; i++)
Weights[i] = Inputs[i]->NumFeatures * (i + 1);
else
std::iota(Weights.begin(), Weights.end(), 1);
Printf("%zd ", Inputs[i]->NumFeatures);
Printf("NUM\n");
for (size_t i = 0; i < N; i++)
Printf("%f ", Inputs[i]->FeatureFrequencyScore);
Printf("SCORE\n");
for (size_t i = 0; i < N; i++)
Printf("%f ", Weights[i]);
Printf("Weights\n");
}
CorpusDistribution = std::piecewise_constant_distribution<double>(
Intervals.begin(), Intervals.end(), Weights.begin());
}
std::piecewise_constant_distribution<double> CorpusDistribution;
std::vector<double> Intervals;
std::vector<double> Weights;
Vector<double> Intervals;
Vector<double> Weights;
std::unordered_set<std::string> Hashes;
std::vector<InputInfo*> Inputs;
Vector<InputInfo*> Inputs;
bool CountingFeatures = false;
size_t NumAddedFeatures = 0;
size_t NumUpdatedFeatures = 0;
uint32_t InputSizesPerFeature[kFeatureSetSize];
uint32_t SmallestElementPerFeature[kFeatureSetSize];
float FeatureFrequency[kFeatureSetSize];
std::string OutputCorpus;
};

Просмотреть файл

@ -18,20 +18,52 @@
#include <cstring>
#include <string>
#include <vector>
#include <set>
#include <memory>
// Platform detection.
#ifdef __linux__
#define LIBFUZZER_APPLE 0
#define LIBFUZZER_FUCHSIA 0
#define LIBFUZZER_LINUX 1
#define LIBFUZZER_NETBSD 0
#define LIBFUZZER_FREEBSD 0
#define LIBFUZZER_WINDOWS 0
#elif __APPLE__
#define LIBFUZZER_APPLE 1
#define LIBFUZZER_FUCHSIA 0
#define LIBFUZZER_LINUX 0
#define LIBFUZZER_NETBSD 0
#define LIBFUZZER_FREEBSD 0
#define LIBFUZZER_WINDOWS 0
#elif __NetBSD__
#define LIBFUZZER_APPLE 0
#define LIBFUZZER_FUCHSIA 0
#define LIBFUZZER_LINUX 0
#define LIBFUZZER_NETBSD 1
#define LIBFUZZER_FREEBSD 0
#define LIBFUZZER_WINDOWS 0
#elif __FreeBSD__
#define LIBFUZZER_APPLE 0
#define LIBFUZZER_FUCHSIA 0
#define LIBFUZZER_LINUX 0
#define LIBFUZZER_NETBSD 0
#define LIBFUZZER_FREEBSD 1
#define LIBFUZZER_WINDOWS 0
#elif _WIN32
#define LIBFUZZER_APPLE 0
#define LIBFUZZER_FUCHSIA 0
#define LIBFUZZER_LINUX 0
#define LIBFUZZER_NETBSD 0
#define LIBFUZZER_FREEBSD 0
#define LIBFUZZER_WINDOWS 1
#elif __Fuchsia__
#define LIBFUZZER_APPLE 0
#define LIBFUZZER_FUCHSIA 1
#define LIBFUZZER_LINUX 0
#define LIBFUZZER_NETBSD 0
#define LIBFUZZER_FREEBSD 0
#define LIBFUZZER_WINDOWS 0
#else
#error "Support for your platform has not been implemented"
#endif
@ -40,7 +72,7 @@
# define __has_attribute(x) 0
#endif
#define LIBFUZZER_POSIX LIBFUZZER_APPLE || LIBFUZZER_LINUX
#define LIBFUZZER_POSIX (LIBFUZZER_APPLE || LIBFUZZER_LINUX || LIBFUZZER_NETBSD || LIBFUZZER_FREEBSD)
#ifdef __x86_64
# if __has_attribute(target)
@ -102,8 +134,23 @@ struct ExternalFunctions;
// Global interface to functions that may or may not be available.
extern ExternalFunctions *EF;
typedef std::vector<uint8_t> Unit;
typedef std::vector<Unit> UnitVector;
// We are using a custom allocator to give a different symbol name to STL
// containers in order to avoid ODR violations.
template<typename T>
class fuzzer_allocator: public std::allocator<T> {
public:
template<class Other>
struct rebind { typedef fuzzer_allocator<Other> other; };
};
template<typename T>
using Vector = std::vector<T, fuzzer_allocator<T>>;
template<typename T>
using Set = std::set<T, std::less<T>, fuzzer_allocator<T>>;
typedef Vector<uint8_t> Unit;
typedef Vector<Unit> UnitVector;
typedef int (*UserCallback)(const uint8_t *Data, size_t Size);
int FuzzerDriver(int *argc, char ***argv, UserCallback Callback);
@ -123,6 +170,10 @@ uint8_t *ExtraCountersBegin();
uint8_t *ExtraCountersEnd();
void ClearExtraCounters();
uint64_t *ClangCountersBegin();
uint64_t *ClangCountersEnd();
void ClearClangCounters();
} // namespace fuzzer
#endif // LLVM_FUZZER_DEFS_H

Просмотреть файл

@ -115,12 +115,12 @@ private:
};
// Parses one dictionary entry.
// If successfull, write the enty to Unit and returns true,
// If successful, write the enty to Unit and returns true,
// otherwise returns false.
bool ParseOneDictionaryEntry(const std::string &Str, Unit *U);
// Parses the dictionary file, fills Units, returns true iff all lines
// were parsed succesfully.
bool ParseDictionaryFile(const std::string &Text, std::vector<Unit> *Units);
// were parsed successfully.
bool ParseDictionaryFile(const std::string &Text, Vector<Unit> *Units);
} // namespace fuzzer

Просмотреть файл

@ -9,10 +9,11 @@
// FuzzerDriver and flag parsing.
//===----------------------------------------------------------------------===//
#include "FuzzerCommand.h"
#include "FuzzerCorpus.h"
#include "FuzzerIO.h"
#include "FuzzerInterface.h"
#include "FuzzerInternal.h"
#include "FuzzerIO.h"
#include "FuzzerMutate.h"
#include "FuzzerRandom.h"
#include "FuzzerShmem.h"
@ -20,6 +21,7 @@
#include <algorithm>
#include <atomic>
#include <chrono>
#include <cstdlib>
#include <cstring>
#include <mutex>
#include <string>
@ -73,7 +75,7 @@ static const FlagDescription FlagDescriptions [] {
static const size_t kNumFlags =
sizeof(FlagDescriptions) / sizeof(FlagDescriptions[0]);
static std::vector<std::string> *Inputs;
static Vector<std::string> *Inputs;
static std::string *ProgName;
static void PrintHelp() {
@ -149,7 +151,7 @@ static bool ParseOneFlag(const char *Param) {
int Val = MyStol(Str);
*FlagDescriptions[F].IntFlag = Val;
if (Flags.verbosity >= 2)
Printf("Flag: %s %d\n", Name, Val);;
Printf("Flag: %s %d\n", Name, Val);
return true;
} else if (FlagDescriptions[F].UIntFlag) {
unsigned int Val = std::stoul(Str);
@ -174,7 +176,7 @@ static bool ParseOneFlag(const char *Param) {
}
// We don't use any library to minimize dependencies.
static void ParseFlags(const std::vector<std::string> &Args) {
static void ParseFlags(const Vector<std::string> &Args) {
for (size_t F = 0; F < kNumFlags; F++) {
if (FlagDescriptions[F].IntFlag)
*FlagDescriptions[F].IntFlag = FlagDescriptions[F].Default;
@ -184,9 +186,13 @@ static void ParseFlags(const std::vector<std::string> &Args) {
if (FlagDescriptions[F].StrFlag)
*FlagDescriptions[F].StrFlag = nullptr;
}
Inputs = new std::vector<std::string>;
Inputs = new Vector<std::string>;
for (size_t A = 1; A < Args.size(); A++) {
if (ParseOneFlag(Args[A].c_str())) continue;
if (ParseOneFlag(Args[A].c_str())) {
if (Flags.ignore_remaining_args)
break;
continue;
}
Inputs->push_back(Args[A]);
}
}
@ -201,16 +207,20 @@ static void PulseThread() {
}
}
static void WorkerThread(const std::string &Cmd, std::atomic<unsigned> *Counter,
static void WorkerThread(const Command &BaseCmd, std::atomic<unsigned> *Counter,
unsigned NumJobs, std::atomic<bool> *HasErrors) {
while (true) {
unsigned C = (*Counter)++;
if (C >= NumJobs) break;
std::string Log = "fuzz-" + std::to_string(C) + ".log";
std::string ToRun = Cmd + " > " + Log + " 2>&1\n";
if (Flags.verbosity)
Printf("%s", ToRun.c_str());
int ExitCode = ExecuteCommand(ToRun);
Command Cmd(BaseCmd);
Cmd.setOutputFile(Log);
Cmd.combineOutAndErr();
if (Flags.verbosity) {
std::string CommandLine = Cmd.toString();
Printf("%s\n", CommandLine.c_str());
}
int ExitCode = ExecuteCommand(Cmd);
if (ExitCode != 0)
*HasErrors = true;
std::lock_guard<std::mutex> Lock(Mu);
@ -220,7 +230,7 @@ static void WorkerThread(const std::string &Cmd, std::atomic<unsigned> *Counter,
}
}
std::string CloneArgsWithoutX(const std::vector<std::string> &Args,
std::string CloneArgsWithoutX(const Vector<std::string> &Args,
const char *X1, const char *X2) {
std::string Cmd;
for (auto &S : Args) {
@ -231,16 +241,18 @@ std::string CloneArgsWithoutX(const std::vector<std::string> &Args,
return Cmd;
}
static int RunInMultipleProcesses(const std::vector<std::string> &Args,
static int RunInMultipleProcesses(const Vector<std::string> &Args,
unsigned NumWorkers, unsigned NumJobs) {
std::atomic<unsigned> Counter(0);
std::atomic<bool> HasErrors(false);
std::string Cmd = CloneArgsWithoutX(Args, "jobs", "workers");
std::vector<std::thread> V;
Command Cmd(Args);
Cmd.removeFlag("jobs");
Cmd.removeFlag("workers");
Vector<std::thread> V;
std::thread Pulse(PulseThread);
Pulse.detach();
for (unsigned i = 0; i < NumWorkers; i++)
V.push_back(std::thread(WorkerThread, Cmd, &Counter, NumJobs, &HasErrors));
V.push_back(std::thread(WorkerThread, std::ref(Cmd), &Counter, NumJobs, &HasErrors));
for (auto &T : V)
T.join();
return HasErrors ? 1 : 0;
@ -265,7 +277,7 @@ int RunOneTest(Fuzzer *F, const char *InputFilePath, size_t MaxLen) {
Unit U = FileToVector(InputFilePath);
if (MaxLen && MaxLen < U.size())
U.resize(MaxLen);
F->RunOne(U.data(), U.size());
F->ExecuteCallback(U.data(), U.size());
F->TryDetectingAMemoryLeak(U.data(), U.size(), true);
return 0;
}
@ -289,7 +301,7 @@ static std::string GetDedupTokenFromFile(const std::string &Path) {
return S.substr(Beg, End - Beg);
}
int CleanseCrashInput(const std::vector<std::string> &Args,
int CleanseCrashInput(const Vector<std::string> &Args,
const FuzzingOptions &Options) {
if (Inputs->size() != 1 || !Flags.exact_artifact_path) {
Printf("ERROR: -cleanse_crash should be given one input file and"
@ -298,26 +310,25 @@ int CleanseCrashInput(const std::vector<std::string> &Args,
}
std::string InputFilePath = Inputs->at(0);
std::string OutputFilePath = Flags.exact_artifact_path;
std::string BaseCmd =
CloneArgsWithoutX(Args, "cleanse_crash", "cleanse_crash");
Command Cmd(Args);
Cmd.removeFlag("cleanse_crash");
auto InputPos = BaseCmd.find(" " + InputFilePath + " ");
assert(InputPos != std::string::npos);
BaseCmd.erase(InputPos, InputFilePath.size() + 1);
assert(Cmd.hasArgument(InputFilePath));
Cmd.removeArgument(InputFilePath);
auto LogFilePath = DirPlusFile(
TmpDir(), "libFuzzerTemp." + std::to_string(GetPid()) + ".txt");
auto TmpFilePath = DirPlusFile(
TmpDir(), "libFuzzerTemp." + std::to_string(GetPid()) + ".repro");
auto LogFileRedirect = " > " + LogFilePath + " 2>&1 ";
auto Cmd = BaseCmd + " " + TmpFilePath + LogFileRedirect;
Cmd.addArgument(TmpFilePath);
Cmd.setOutputFile(LogFilePath);
Cmd.combineOutAndErr();
std::string CurrentFilePath = InputFilePath;
auto U = FileToVector(CurrentFilePath);
size_t Size = U.size();
const std::vector<uint8_t> ReplacementBytes = {' ', 0xff};
const Vector<uint8_t> ReplacementBytes = {' ', 0xff};
for (int NumAttempts = 0; NumAttempts < 5; NumAttempts++) {
bool Changed = false;
for (size_t Idx = 0; Idx < Size; Idx++) {
@ -349,28 +360,29 @@ int CleanseCrashInput(const std::vector<std::string> &Args,
return 0;
}
int MinimizeCrashInput(const std::vector<std::string> &Args,
int MinimizeCrashInput(const Vector<std::string> &Args,
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);
std::string BaseCmd =
CloneArgsWithoutX(Args, "minimize_crash", "exact_artifact_path");
auto InputPos = BaseCmd.find(" " + InputFilePath + " ");
assert(InputPos != std::string::npos);
BaseCmd.erase(InputPos, InputFilePath.size() + 1);
Command BaseCmd(Args);
BaseCmd.removeFlag("minimize_crash");
BaseCmd.removeFlag("exact_artifact_path");
assert(BaseCmd.hasArgument(InputFilePath));
BaseCmd.removeArgument(InputFilePath);
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");
BaseCmd += " -max_total_time=600";
BaseCmd.addFlag("max_total_time", "600");
}
auto LogFilePath = DirPlusFile(
TmpDir(), "libFuzzerTemp." + std::to_string(GetPid()) + ".txt");
auto LogFileRedirect = " > " + LogFilePath + " 2>&1 ";
BaseCmd.setOutputFile(LogFilePath);
BaseCmd.combineOutAndErr();
std::string CurrentFilePath = InputFilePath;
while (true) {
@ -378,9 +390,11 @@ int MinimizeCrashInput(const std::vector<std::string> &Args,
Printf("CRASH_MIN: minimizing crash input: '%s' (%zd bytes)\n",
CurrentFilePath.c_str(), U.size());
auto Cmd = BaseCmd + " " + CurrentFilePath + LogFileRedirect;
Command Cmd(BaseCmd);
Cmd.addArgument(CurrentFilePath);
Printf("CRASH_MIN: executing: %s\n", Cmd.c_str());
std::string CommandLine = Cmd.toString();
Printf("CRASH_MIN: executing: %s\n", CommandLine.c_str());
int ExitCode = ExecuteCommand(Cmd);
if (ExitCode == 0) {
Printf("ERROR: the input %s did not crash\n", CurrentFilePath.c_str());
@ -397,9 +411,10 @@ int MinimizeCrashInput(const std::vector<std::string> &Args,
Flags.exact_artifact_path
? Flags.exact_artifact_path
: Options.ArtifactPrefix + "minimized-from-" + Hash(U);
Cmd += " -minimize_crash_internal_step=1 -exact_artifact_path=" +
ArtifactPath;
Printf("CRASH_MIN: executing: %s\n", Cmd.c_str());
Cmd.addFlag("minimize_crash_internal_step", "1");
Cmd.addFlag("exact_artifact_path", ArtifactPath);
CommandLine = Cmd.toString();
Printf("CRASH_MIN: executing: %s\n", CommandLine.c_str());
ExitCode = ExecuteCommand(Cmd);
CopyFileToErr(LogFilePath);
if (ExitCode == 0) {
@ -441,7 +456,6 @@ int MinimizeCrashInputInternalStep(Fuzzer *F, InputCorpus *Corpus) {
Printf("INFO: The input is small enough, exiting\n");
exit(0);
}
Corpus->AddToCorpus(U, 0);
F->SetMaxInputLen(U.size());
F->SetMaxMutationLen(U.size() - 1);
F->MinimizeCrashLoop(U);
@ -450,28 +464,27 @@ int MinimizeCrashInputInternalStep(Fuzzer *F, InputCorpus *Corpus) {
return 0;
}
int AnalyzeDictionary(Fuzzer *F, const std::vector<Unit>& Dict,
int AnalyzeDictionary(Fuzzer *F, const Vector<Unit>& Dict,
UnitVector& Corpus) {
Printf("Started dictionary minimization (up to %d tests)\n",
Dict.size() * Corpus.size() * 2);
// Scores and usage count for each dictionary unit.
std::vector<int> Scores(Dict.size());
std::vector<int> Usages(Dict.size());
Vector<int> Scores(Dict.size());
Vector<int> Usages(Dict.size());
std::vector<size_t> InitialFeatures;
std::vector<size_t> ModifiedFeatures;
Vector<size_t> InitialFeatures;
Vector<size_t> ModifiedFeatures;
for (auto &C : Corpus) {
// Get coverage for the testcase without modifications.
F->ExecuteCallback(C.data(), C.size());
InitialFeatures.clear();
TPC.CollectFeatures([&](size_t Feature) -> bool {
TPC.CollectFeatures([&](size_t Feature) {
InitialFeatures.push_back(Feature);
return true;
});
for (size_t i = 0; i < Dict.size(); ++i) {
auto Data = C;
Vector<uint8_t> Data = C;
auto StartPos = std::search(Data.begin(), Data.end(),
Dict[i].begin(), Dict[i].end());
// Skip dictionary unit, if the testcase does not contain it.
@ -492,9 +505,8 @@ int AnalyzeDictionary(Fuzzer *F, const std::vector<Unit>& Dict,
// Get coverage for testcase with masked occurrences of dictionary unit.
F->ExecuteCallback(Data.data(), Data.size());
ModifiedFeatures.clear();
TPC.CollectFeatures([&](size_t Feature) -> bool {
TPC.CollectFeatures([&](size_t Feature) {
ModifiedFeatures.push_back(Feature);
return true;
});
if (InitialFeatures == ModifiedFeatures)
@ -525,7 +537,7 @@ int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) {
EF = new ExternalFunctions();
if (EF->LLVMFuzzerInitialize)
EF->LLVMFuzzerInitialize(argc, argv);
const std::vector<std::string> Args(*argv, *argv + *argc);
const Vector<std::string> Args(*argv, *argv + *argc);
assert(!Args.empty());
ProgName = new std::string(Args[0]);
if (Argv0 != *ProgName) {
@ -552,33 +564,34 @@ int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) {
if (Flags.workers > 0 && Flags.jobs > 0)
return RunInMultipleProcesses(Args, Flags.workers, Flags.jobs);
const size_t kMaxSaneLen = 1 << 20;
const size_t kMinDefaultLen = 64;
FuzzingOptions Options;
Options.Verbosity = Flags.verbosity;
Options.MaxLen = Flags.max_len;
Options.ExperimentalLenControl = Flags.experimental_len_control;
if (Flags.experimental_len_control && Flags.max_len == 64)
Options.MaxLen = 1 << 20;
Options.LenControl = Flags.len_control;
Options.UnitTimeoutSec = Flags.timeout;
Options.ErrorExitCode = Flags.error_exitcode;
Options.TimeoutExitCode = Flags.timeout_exitcode;
Options.MaxTotalTimeSec = Flags.max_total_time;
Options.DoCrossOver = Flags.cross_over;
Options.MutateDepth = Flags.mutate_depth;
Options.ReduceDepth = Flags.reduce_depth;
Options.UseCounters = Flags.use_counters;
Options.UseIndirCalls = Flags.use_indir_calls;
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;
Options.PurgeAllocatorIntervalSec = Flags.purge_allocator_interval;
Options.TraceMalloc = Flags.trace_malloc;
Options.RssLimitMb = Flags.rss_limit_mb;
Options.MallocLimitMb = Flags.malloc_limit_mb;
if (!Options.MallocLimitMb)
Options.MallocLimitMb = Options.RssLimitMb;
if (Flags.runs >= 0)
Options.MaxNumberOfRuns = Flags.runs;
if (!Inputs->empty() && !Flags.minimize_crash_internal_step)
@ -588,7 +601,7 @@ int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) {
Options.ArtifactPrefix = Flags.artifact_prefix;
if (Flags.exact_artifact_path)
Options.ExactArtifactPath = Flags.exact_artifact_path;
std::vector<Unit> Dictionary;
Vector<Unit> Dictionary;
if (Flags.dict)
if (!ParseDictionaryFile(FileToString(Flags.dict), &Dictionary))
return 1;
@ -598,10 +611,13 @@ int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) {
Options.SaveArtifacts =
!DoPlainRun || Flags.minimize_crash_internal_step;
Options.PrintNewCovPcs = Flags.print_pcs;
Options.PrintNewCovFuncs = Flags.print_funcs;
Options.PrintFinalStats = Flags.print_final_stats;
Options.PrintCorpusStats = Flags.print_corpus_stats;
Options.PrintCoverage = Flags.print_coverage;
Options.DumpCoverage = Flags.dump_coverage;
Options.UseClangCoverage = Flags.use_clang_coverage;
Options.UseFeatureFrequency = Flags.use_feature_frequency;
if (Flags.exit_on_src_pos)
Options.ExitOnSrcPos = Flags.exit_on_src_pos;
if (Flags.exit_on_item)
@ -634,8 +650,12 @@ int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) {
Options.HandleSegv = Flags.handle_segv;
Options.HandleTerm = Flags.handle_term;
Options.HandleXfsz = Flags.handle_xfsz;
Options.HandleUsr1 = Flags.handle_usr1;
Options.HandleUsr2 = Flags.handle_usr2;
SetSignalHandler(Options);
std::atexit(Fuzzer::StaticExitCallback);
if (Flags.minimize_crash)
return MinimizeCrashInput(Args, Options);
@ -657,7 +677,7 @@ int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) {
size_t Size = SMR.ReadByteArraySize();
SMR.WriteByteArray(nullptr, 0);
const Unit tmp(SMR.GetByteArray(), SMR.GetByteArray() + Size);
F->RunOne(tmp.data(), tmp.size());
F->ExecuteCallback(tmp.data(), tmp.size());
SMR.PostServer();
}
return 0;
@ -694,27 +714,31 @@ int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) {
}
if (Flags.merge) {
if (Options.MaxLen == 0)
F->SetMaxInputLen(kMaxSaneLen);
if (Flags.merge_control_file)
F->CrashResistantMergeInternalStep(Flags.merge_control_file);
else
F->CrashResistantMerge(Args, *Inputs,
Flags.load_coverage_summary,
Flags.save_coverage_summary);
F->CrashResistantMerge(Args, *Inputs,
Flags.load_coverage_summary,
Flags.save_coverage_summary,
Flags.merge_control_file);
exit(0);
}
size_t TemporaryMaxLen = Options.MaxLen ? Options.MaxLen : kMaxSaneLen;
UnitVector InitialCorpus;
for (auto &Inp : *Inputs) {
Printf("Loading corpus dir: %s\n", Inp.c_str());
ReadDirToVectorOfUnits(Inp.c_str(), &InitialCorpus, nullptr,
TemporaryMaxLen, /*ExitOnError=*/false);
if (Flags.merge_inner) {
const size_t kDefaultMaxMergeLen = 1 << 20;
if (Options.MaxLen == 0)
F->SetMaxInputLen(kDefaultMaxMergeLen);
assert(Flags.merge_control_file);
F->CrashResistantMergeInternalStep(Flags.merge_control_file);
exit(0);
}
if (Flags.analyze_dict) {
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);
}
if (Dictionary.empty() || Inputs->empty()) {
Printf("ERROR: can't analyze dict without dict and corpus provided\n");
return 1;
@ -723,25 +747,11 @@ int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) {
Printf("Dictionary analysis failed\n");
exit(1);
}
Printf("Dictionary analysis suceeded\n");
Printf("Dictionary analysis succeeded\n");
exit(0);
}
if (Options.MaxLen == 0) {
size_t MaxLen = 0;
for (auto &U : InitialCorpus)
MaxLen = std::max(U.size(), MaxLen);
F->SetMaxInputLen(std::min(std::max(kMinDefaultLen, MaxLen), kMaxSaneLen));
}
if (InitialCorpus.empty()) {
InitialCorpus.push_back(Unit({'\n'})); // Valid ASCII input.
if (Options.Verbosity)
Printf("INFO: A corpus is not provided, starting from an empty corpus\n");
}
F->ShuffleAndMinimize(&InitialCorpus);
InitialCorpus.clear(); // Don't need this memory any more.
F->Loop();
F->Loop(*Inputs);
if (Flags.verbosity)
Printf("Done %zd runs in %zd second(s)\n", F->getTotalNumberOfRuns(),

Просмотреть файл

@ -33,6 +33,7 @@ EXT_FUNC(__sanitizer_install_malloc_and_free_hooks, int,
(void (*malloc_hook)(const volatile void *, size_t),
void (*free_hook)(const volatile void *)),
false);
EXT_FUNC(__sanitizer_purge_allocator, void, (), false);
EXT_FUNC(__sanitizer_print_memory_profile, int, (size_t, size_t), false);
EXT_FUNC(__sanitizer_print_stack_trace, void, (), true);
EXT_FUNC(__sanitizer_symbolize_pc, void,

Просмотреть файл

@ -14,6 +14,8 @@
#include "FuzzerExtFunctions.h"
#include "FuzzerIO.h"
#include "Windows.h"
// This must be included after Windows.h.
#include "Psapi.h"
namespace fuzzer {

Просмотреть файл

@ -13,7 +13,7 @@
// to clients right now.
//===----------------------------------------------------------------------===//
#include "FuzzerDefs.h"
#if LIBFUZZER_LINUX
#if LIBFUZZER_LINUX || LIBFUZZER_NETBSD || LIBFUZZER_FUCHSIA || LIBFUZZER_FREEBSD
#include "FuzzerExtFunctions.h"
#include "FuzzerIO.h"
@ -41,7 +41,8 @@ namespace fuzzer {
ExternalFunctions::ExternalFunctions() {
#define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \
this->NAME = ::NAME; \
CheckFnPtr((void *)::NAME, #NAME, WARN);
CheckFnPtr(reinterpret_cast<void *>(reinterpret_cast<uintptr_t>(::NAME)), \
#NAME, WARN);
#include "FuzzerExtFunctions.def"
@ -50,4 +51,4 @@ ExternalFunctions::ExternalFunctions() {
} // namespace fuzzer
#endif // LIBFUZZER_LINUX
#endif // LIBFUZZER_LINUX || LIBFUZZER_NETBSD || LIBFUZZER_FUSCHIA || LIBFUZZER_FREEBSD

Просмотреть файл

@ -11,7 +11,7 @@
#include "FuzzerDefs.h"
#if LIBFUZZER_LINUX
#if LIBFUZZER_LINUX || LIBFUZZER_NETBSD || LIBFUZZER_FREEBSD
__attribute__((weak)) extern uint8_t __start___libfuzzer_extra_counters;
__attribute__((weak)) extern uint8_t __stop___libfuzzer_extra_counters;

Просмотреть файл

@ -17,10 +17,15 @@ FUZZER_FLAG_INT(runs, -1,
FUZZER_FLAG_INT(max_len, 0, "Maximum length of the test input. "
"If 0, libFuzzer tries to guess a good value based on the corpus "
"and reports it. ")
FUZZER_FLAG_INT(experimental_len_control, 0, "experimental flag")
FUZZER_FLAG_INT(len_control, 1000, "Try generating small inputs first, "
"then try larger inputs over time. Specifies the rate at which the length "
"limit is increased (smaller == faster). If 0, immediately try inputs with "
"size up to max_len.")
FUZZER_FLAG_INT(cross_over, 1, "If 1, cross over inputs.")
FUZZER_FLAG_INT(mutate_depth, 5,
"Apply this number of consecutive mutations to each input.")
FUZZER_FLAG_INT(reduce_depth, 0, "Experimental/internal. "
"Reduce depth if mutations lose unique features")
FUZZER_FLAG_INT(shuffle, 1, "Shuffle inputs at startup")
FUZZER_FLAG_INT(prefer_small, 1,
"If 1, always prefer smaller inputs during the corpus shuffle.")
@ -38,7 +43,12 @@ FUZZER_FLAG_INT(help, 0, "Print help.")
FUZZER_FLAG_INT(merge, 0, "If 1, the 2-nd, 3-rd, etc corpora will be "
"merged into the 1-st corpus. Only interesting units will be taken. "
"This flag can be used to minimize a corpus.")
FUZZER_FLAG_STRING(merge_control_file, "internal flag")
FUZZER_FLAG_STRING(merge_inner, "internal flag")
FUZZER_FLAG_STRING(merge_control_file,
"Specify a control file used for the merge process. "
"If a merge process gets killed it tries to leave this file "
"in a state suitable for resuming the merge. "
"By default a temporary file will be used.")
FUZZER_FLAG_STRING(save_coverage_summary, "Experimental:"
" save coverage summary to a given file."
" Used with -merge=1")
@ -59,13 +69,14 @@ FUZZER_FLAG_INT(cleanse_crash, 0, "If 1, tries to cleanse the provided"
)
FUZZER_FLAG_INT(minimize_crash_internal_step, 0, "internal flag")
FUZZER_FLAG_INT(use_counters, 1, "Use coverage counters")
FUZZER_FLAG_INT(use_indir_calls, 1, "Use indirect caller-callee counters")
FUZZER_FLAG_INT(use_memmem, 1,
"Use hints from intercepting memmem, strstr, etc")
FUZZER_FLAG_INT(use_value_profile, 0,
"Experimental. Use value profile to guide fuzzing.")
FUZZER_FLAG_INT(use_cmp, 1, "Use CMP traces to guide mutations")
FUZZER_FLAG_INT(shrink, 0, "Experimental. Try to shrink corpus elements.")
FUZZER_FLAG_INT(shrink, 0, "Experimental. Try to shrink corpus inputs.")
FUZZER_FLAG_INT(reduce_inputs, 1,
"Try to reduce the size of inputs while preserving their full feature sets")
FUZZER_FLAG_UNSIGNED(jobs, 0, "Number of jobs to run. If jobs >= 1 we spawn"
" this number of jobs in separate worker processes"
" with stdout/stderr redirected to fuzz-JOB.log.")
@ -89,12 +100,15 @@ FUZZER_FLAG_STRING(exact_artifact_path,
"and will not use checksum in the file name. Do not "
"use the same path for several parallel processes.")
FUZZER_FLAG_INT(print_pcs, 0, "If 1, print out newly covered PCs.")
FUZZER_FLAG_INT(print_funcs, 2, "If >=1, print out at most this number of "
"newly covered functions.")
FUZZER_FLAG_INT(print_final_stats, 0, "If 1, print statistics at exit.")
FUZZER_FLAG_INT(print_corpus_stats, 0,
"If 1, print statistics on corpus elements at exit.")
FUZZER_FLAG_INT(print_coverage, 0, "If 1, print coverage information as text"
" at exit.")
FUZZER_FLAG_INT(dump_coverage, 0, "If 1, dump coverage information as a"
FUZZER_FLAG_INT(dump_coverage, 0, "Deprecated."
" If 1, dump coverage information as a"
" .sancov file at exit.")
FUZZER_FLAG_INT(handle_segv, 1, "If 1, try to intercept SIGSEGV.")
FUZZER_FLAG_INT(handle_bus, 1, "If 1, try to intercept SIGBUS.")
@ -104,31 +118,36 @@ FUZZER_FLAG_INT(handle_fpe, 1, "If 1, try to intercept SIGFPE.")
FUZZER_FLAG_INT(handle_int, 1, "If 1, try to intercept SIGINT.")
FUZZER_FLAG_INT(handle_term, 1, "If 1, try to intercept SIGTERM.")
FUZZER_FLAG_INT(handle_xfsz, 1, "If 1, try to intercept SIGXFSZ.")
FUZZER_FLAG_INT(handle_usr1, 1, "If 1, try to intercept SIGUSR1.")
FUZZER_FLAG_INT(handle_usr2, 1, "If 1, try to intercept SIGUSR2.")
FUZZER_FLAG_INT(close_fd_mask, 0, "If 1, close stdout at startup; "
"if 2, close stderr; if 3, close both. "
"Be careful, this will also close e.g. asan's stderr/stdout.")
"Be careful, this will also close e.g. stderr of asan.")
FUZZER_FLAG_INT(detect_leaks, 1, "If 1, and if LeakSanitizer is enabled "
"try to detect memory leaks during fuzzing (i.e. not only at shut down).")
FUZZER_FLAG_INT(purge_allocator_interval, 1, "Purge allocator caches and "
"quarantines every <N> seconds. When rss_limit_mb is specified (>0), "
"purging starts when RSS exceeds 50% of rss_limit_mb. Pass "
"purge_allocator_interval=-1 to disable this functionality.")
FUZZER_FLAG_INT(trace_malloc, 0, "If >= 1 will print all mallocs/frees. "
"If >= 2 will also print stack traces.")
FUZZER_FLAG_INT(rss_limit_mb, 2048, "If non-zero, the fuzzer will exit upon"
"reaching this limit of RSS memory usage.")
FUZZER_FLAG_INT(malloc_limit_mb, 0, "If non-zero, the fuzzer will exit "
"if the target tries to allocate this number of Mb with one malloc call. "
"If zero (default) same limit as rss_limit_mb is applied.")
FUZZER_FLAG_STRING(exit_on_src_pos, "Exit if a newly found PC originates"
" from the given source location. Example: -exit_on_src_pos=foo.cc:123. "
"Used primarily for testing libFuzzer itself.")
FUZZER_FLAG_STRING(exit_on_item, "Exit if an item with a given sha1 sum"
" was added to the corpus. "
"Used primarily for testing libFuzzer itself.")
FUZZER_FLAG_INT(ignore_remaining_args, 0, "If 1, ignore all arguments passed "
"after this one. Useful for fuzzers that need to do their own "
"argument parsing.")
FUZZER_FLAG_STRING(run_equivalence_server, "Experimental")
FUZZER_FLAG_STRING(use_equivalence_server, "Experimental")
FUZZER_FLAG_INT(analyze_dict, 0, "Experimental")
FUZZER_DEPRECATED_FLAG(exit_on_first)
FUZZER_DEPRECATED_FLAG(save_minimized_corpus)
FUZZER_DEPRECATED_FLAG(sync_command)
FUZZER_DEPRECATED_FLAG(sync_timeout)
FUZZER_DEPRECATED_FLAG(test_single_input)
FUZZER_DEPRECATED_FLAG(drill)
FUZZER_DEPRECATED_FLAG(truncate_units)
FUZZER_DEPRECATED_FLAG(output_csv)
FUZZER_FLAG_INT(use_clang_coverage, 0, "Experimental")
FUZZER_FLAG_INT(use_feature_frequency, 0, "Experimental/internal")

Просмотреть файл

@ -1,187 +0,0 @@
//===- FuzzerAdapter.h - Arbitrary function Fuzzer adapter -------*- C++ -*===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// W A R N I N G : E X P E R I M E N T A L.
//
// Defines an adapter to fuzz functions with (almost) arbitrary signatures.
//===----------------------------------------------------------------------===//
#ifndef LLVM_FUZZER_ADAPTER_H
#define LLVM_FUZZER_ADAPTER_H
#include <stddef.h>
#include <stdint.h>
#include <algorithm>
#include <string>
#include <tuple>
#include <vector>
namespace fuzzer {
/// Unpacks bytes from \p Data according to \p F argument types
/// and calls the function.
/// Use to automatically adapt LLVMFuzzerTestOneInput interface to
/// a specific function.
/// Supported argument types: primitive types, std::vector<uint8_t>.
template <typename Fn> bool Adapt(Fn F, const uint8_t *Data, size_t Size);
// The implementation performs several steps:
// - function argument types are obtained (Args...)
// - data is unpacked into std::tuple<Args...> one by one
// - function is called with std::tuple<Args...> containing arguments.
namespace impl {
// Single argument unpacking.
template <typename T>
size_t UnpackPrimitive(const uint8_t *Data, size_t Size, T *Value) {
if (Size < sizeof(T))
return Size;
*Value = *reinterpret_cast<const T *>(Data);
return Size - sizeof(T);
}
/// Unpacks into a given Value and returns the Size - num_consumed_bytes.
/// Return value equal to Size signals inability to unpack the data (typically
/// because there are not enough bytes).
template <typename T>
size_t UnpackSingle(const uint8_t *Data, size_t Size, T *Value);
#define UNPACK_SINGLE_PRIMITIVE(Type) \
template <> \
size_t UnpackSingle<Type>(const uint8_t *Data, size_t Size, Type *Value) { \
return UnpackPrimitive(Data, Size, Value); \
}
UNPACK_SINGLE_PRIMITIVE(char)
UNPACK_SINGLE_PRIMITIVE(signed char)
UNPACK_SINGLE_PRIMITIVE(unsigned char)
UNPACK_SINGLE_PRIMITIVE(short int)
UNPACK_SINGLE_PRIMITIVE(unsigned short int)
UNPACK_SINGLE_PRIMITIVE(int)
UNPACK_SINGLE_PRIMITIVE(unsigned int)
UNPACK_SINGLE_PRIMITIVE(long int)
UNPACK_SINGLE_PRIMITIVE(unsigned long int)
UNPACK_SINGLE_PRIMITIVE(bool)
UNPACK_SINGLE_PRIMITIVE(wchar_t)
UNPACK_SINGLE_PRIMITIVE(float)
UNPACK_SINGLE_PRIMITIVE(double)
UNPACK_SINGLE_PRIMITIVE(long double)
#undef UNPACK_SINGLE_PRIMITIVE
template <>
size_t UnpackSingle<std::vector<uint8_t>>(const uint8_t *Data, size_t Size,
std::vector<uint8_t> *Value) {
if (Size < 1)
return Size;
size_t Len = std::min(static_cast<size_t>(*Data), Size - 1);
std::vector<uint8_t> V(Data + 1, Data + 1 + Len);
Value->swap(V);
return Size - Len - 1;
}
template <>
size_t UnpackSingle<std::string>(const uint8_t *Data, size_t Size,
std::string *Value) {
if (Size < 1)
return Size;
size_t Len = std::min(static_cast<size_t>(*Data), Size - 1);
std::string S(Data + 1, Data + 1 + Len);
Value->swap(S);
return Size - Len - 1;
}
// Unpacking into arbitrary tuple.
// Recursion guard.
template <int N, typename TupleT>
typename std::enable_if<N == std::tuple_size<TupleT>::value, bool>::type
UnpackImpl(const uint8_t *Data, size_t Size, TupleT *Tuple) {
return true;
}
// Unpack tuple elements starting from Nth.
template <int N, typename TupleT>
typename std::enable_if<N < std::tuple_size<TupleT>::value, bool>::type
UnpackImpl(const uint8_t *Data, size_t Size, TupleT *Tuple) {
size_t NewSize = UnpackSingle(Data, Size, &std::get<N>(*Tuple));
if (NewSize == Size) {
return false;
}
return UnpackImpl<N + 1, TupleT>(Data + (Size - NewSize), NewSize, Tuple);
}
// Unpacks into arbitrary tuple and returns true if successful.
template <typename... Args>
bool Unpack(const uint8_t *Data, size_t Size, std::tuple<Args...> *Tuple) {
return UnpackImpl<0, std::tuple<Args...>>(Data, Size, Tuple);
}
// Helper integer sequence templates.
template <int...> struct Seq {};
template <int N, int... S> struct GenSeq : GenSeq<N - 1, N - 1, S...> {};
// GenSeq<N>::type is Seq<0, 1, ..., N-1>
template <int... S> struct GenSeq<0, S...> { typedef Seq<S...> type; };
// Function signature introspection.
template <typename T> struct FnTraits {};
template <typename ReturnType, typename... Args>
struct FnTraits<ReturnType (*)(Args...)> {
enum { Arity = sizeof...(Args) };
typedef std::tuple<Args...> ArgsTupleT;
};
// Calling a function with arguments in a tuple.
template <typename Fn, int... S>
void ApplyImpl(Fn F, const typename FnTraits<Fn>::ArgsTupleT &Params,
Seq<S...>) {
F(std::get<S>(Params)...);
}
template <typename Fn>
void Apply(Fn F, const typename FnTraits<Fn>::ArgsTupleT &Params) {
// S is Seq<0, ..., Arity-1>
auto S = typename GenSeq<FnTraits<Fn>::Arity>::type();
ApplyImpl(F, Params, S);
}
// Unpacking data into arguments tuple of correct type and calling the function.
template <typename Fn>
bool UnpackAndApply(Fn F, const uint8_t *Data, size_t Size) {
typename FnTraits<Fn>::ArgsTupleT Tuple;
if (!Unpack(Data, Size, &Tuple))
return false;
Apply(F, Tuple);
return true;
}
} // namespace impl
template <typename Fn> bool Adapt(Fn F, const uint8_t *Data, size_t Size) {
return impl::UnpackAndApply(F, Data, Size);
}
} // namespace fuzzer
#endif

Просмотреть файл

@ -9,7 +9,6 @@
// IO functions.
//===----------------------------------------------------------------------===//
#include "mozilla/Unused.h"
#include "FuzzerIO.h"
#include "FuzzerDefs.h"
#include "FuzzerExtFunctions.h"
@ -39,7 +38,9 @@ Unit FileToVector(const std::string &Path, size_t MaxSize, bool ExitOnError) {
}
T.seekg(0, T.end);
size_t FileLen = T.tellg();
auto EndPos = T.tellg();
if (EndPos < 0) return {};
size_t FileLen = EndPos;
if (MaxSize)
FileLen = std::min(FileLen, MaxSize);
@ -63,14 +64,14 @@ void WriteToFile(const Unit &U, const std::string &Path) {
// Use raw C interface because this function may be called from a sig handler.
FILE *Out = fopen(Path.c_str(), "w");
if (!Out) return;
mozilla::Unused << fwrite(U.data(), sizeof(U[0]), U.size(), Out);
fwrite(U.data(), sizeof(U[0]), U.size(), Out);
fclose(Out);
}
void ReadDirToVectorOfUnits(const char *Path, std::vector<Unit> *V,
void ReadDirToVectorOfUnits(const char *Path, Vector<Unit> *V,
long *Epoch, size_t MaxSize, bool ExitOnError) {
long E = Epoch ? *Epoch : 0;
std::vector<std::string> Files;
Vector<std::string> Files;
ListFilesInDirRecursive(Path, Epoch, &Files, /*TopDir*/true);
size_t NumLoaded = 0;
for (size_t i = 0; i < Files.size(); i++) {
@ -85,6 +86,15 @@ void ReadDirToVectorOfUnits(const char *Path, std::vector<Unit> *V,
}
}
void GetSizedFilesFromDir(const std::string &Dir, Vector<SizedFile> *V) {
Vector<std::string> Files;
ListFilesInDirRecursive(Dir, 0, &Files, /*TopDir*/true);
for (auto &File : Files)
if (size_t Size = FileSize(File))
V->push_back({File, Size});
}
std::string DirPlusFile(const std::string &DirPath,
const std::string &FileName) {
return DirPath + GetSeparator() + FileName;

Просмотреть файл

@ -27,7 +27,7 @@ void CopyFileToErr(const std::string &Path);
void WriteToFile(const Unit &U, const std::string &Path);
void ReadDirToVectorOfUnits(const char *Path, std::vector<Unit> *V,
void ReadDirToVectorOfUnits(const char *Path, Vector<Unit> *V,
long *Epoch, size_t MaxSize, bool ExitOnError);
// Returns "Dir/FileName" or equivalent for the current OS.
@ -53,9 +53,18 @@ void RawPrint(const char *Str);
// Platform specific functions:
bool IsFile(const std::string &Path);
size_t FileSize(const std::string &Path);
void ListFilesInDirRecursive(const std::string &Dir, long *Epoch,
std::vector<std::string> *V, bool TopDir);
Vector<std::string> *V, bool TopDir);
struct SizedFile {
std::string File;
size_t Size;
bool operator<(const SizedFile &B) const { return Size < B.Size; }
};
void GetSizedFilesFromDir(const std::string &Dir, Vector<SizedFile> *V);
char GetSeparator();

Просмотреть файл

@ -9,9 +9,8 @@
// IO functions implementation using Posix API.
//===----------------------------------------------------------------------===//
#include "FuzzerDefs.h"
#if LIBFUZZER_POSIX
#if LIBFUZZER_POSIX || LIBFUZZER_FUCHSIA
#include "mozilla/Unused.h"
#include "FuzzerExtFunctions.h"
#include "FuzzerIO.h"
#include <cstdarg>
@ -33,8 +32,22 @@ bool IsFile(const std::string &Path) {
return S_ISREG(St.st_mode);
}
static bool IsDirectory(const std::string &Path) {
struct stat St;
if (stat(Path.c_str(), &St))
return false;
return S_ISDIR(St.st_mode);
}
size_t FileSize(const std::string &Path) {
struct stat St;
if (stat(Path.c_str(), &St))
return 0;
return St.st_size;
}
void ListFilesInDirRecursive(const std::string &Dir, long *Epoch,
std::vector<std::string> *V, bool TopDir) {
Vector<std::string> *V, bool TopDir) {
auto E = GetEpoch(Dir);
if (Epoch)
if (E && *Epoch >= E) return;
@ -46,9 +59,12 @@ void ListFilesInDirRecursive(const std::string &Dir, long *Epoch,
}
while (auto E = readdir(D)) {
std::string Path = DirPlusFile(Dir, E->d_name);
if (E->d_type == DT_REG || E->d_type == DT_LNK)
if (E->d_type == DT_REG || E->d_type == DT_LNK ||
(E->d_type == DT_UNKNOWN && IsFile(Path)))
V->push_back(Path);
else if (E->d_type == DT_DIR && *E->d_name != '.')
else if ((E->d_type == DT_DIR ||
(E->d_type == DT_UNKNOWN && IsDirectory(Path))) &&
*E->d_name != '.')
ListFilesInDirRecursive(Path, Epoch, V, false);
}
closedir(D);
@ -116,7 +132,7 @@ bool IsInterestingCoverageFile(const std::string &FileName) {
void RawPrint(const char *Str) {
mozilla::Unused << write(2, Str, strlen(Str));
write(2, Str, strlen(Str));
}
} // namespace fuzzer

Просмотреть файл

@ -73,7 +73,7 @@ bool IsFile(const std::string &Path) {
}
void ListFilesInDirRecursive(const std::string &Dir, long *Epoch,
std::vector<std::string> *V, bool TopDir) {
Vector<std::string> *V, bool TopDir) {
auto E = GetEpoch(Dir);
if (Epoch)
if (E && *Epoch >= E) return;
@ -182,7 +182,7 @@ static size_t ParseFileName(const std::string &FileName, const size_t Offset) {
return Pos - Offset;
}
// Parse a directory ending in separator, like: SomeDir\
// Parse a directory ending in separator, like: `SomeDir\`
// Returns number of characters considered if successful.
static size_t ParseDir(const std::string &FileName, const size_t Offset) {
size_t Pos = Offset;
@ -197,7 +197,7 @@ static size_t ParseDir(const std::string &FileName, const size_t Offset) {
return Pos - Offset;
}
// Parse a servername and share, like: SomeServer\SomeShare\
// Parse a servername and share, like: `SomeServer\SomeShare\`
// Returns number of characters considered if successful.
static size_t ParseServerAndShare(const std::string &FileName,
const size_t Offset) {

Просмотреть файл

@ -30,35 +30,39 @@ extern "C" {
// Executes the code under test with [Data, Data+Size) as the input.
// libFuzzer will invoke this function *many* times with different inputs.
// Must return 0.
int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size);
__attribute__((visibility("default"))) int
LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size);
// Optional user-provided initialization function.
// If provided, this function will be called by libFuzzer once at startup.
// It may read and modify argc/argv.
// Must return 0.
int LLVMFuzzerInitialize(int *argc, char ***argv);
__attribute__((visibility("default"))) int LLVMFuzzerInitialize(int *argc,
char ***argv);
// Optional user-provided custom mutator.
// Mutates raw data in [Data, Data+Size) inplace.
// Returns the new size, which is not greater than MaxSize.
// Given the same Seed produces the same mutation.
size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size, size_t MaxSize,
unsigned int Seed);
__attribute__((visibility("default"))) size_t
LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size, size_t MaxSize,
unsigned int Seed);
// Optional user-provided custom cross-over function.
// Combines pieces of Data1 & Data2 together into Out.
// Returns the new size, which is not greater than MaxOutSize.
// Should produce the same mutation given the same Seed.
size_t LLVMFuzzerCustomCrossOver(const uint8_t *Data1, size_t Size1,
const uint8_t *Data2, size_t Size2,
uint8_t *Out, size_t MaxOutSize,
unsigned int Seed);
__attribute__((visibility("default"))) size_t
LLVMFuzzerCustomCrossOver(const uint8_t *Data1, size_t Size1,
const uint8_t *Data2, size_t Size2, uint8_t *Out,
size_t MaxOutSize, unsigned int Seed);
// Experimental, may go away in future.
// libFuzzer-provided function to be used inside LLVMFuzzerCustomMutator.
// Mutates raw data in [Data, Data+Size) inplace.
// Returns the new size, which is not greater than MaxSize.
size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize);
__attribute__((visibility("default"))) size_t
LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize);
#ifdef __cplusplus
} // extern "C"

Просмотреть файл

@ -35,10 +35,9 @@ public:
Fuzzer(UserCallback CB, InputCorpus &Corpus, MutationDispatcher &MD,
FuzzingOptions Options);
~Fuzzer();
void Loop();
void Loop(const Vector<std::string> &CorpusDirs);
void ReadAndExecuteSeedCorpora(const Vector<std::string> &CorpusDirs);
void MinimizeCrashLoop(const Unit &U);
void ShuffleAndMinimize(UnitVector *V);
void InitializeTraceState();
void RereadOutputCorpus(size_t MaxSize);
size_t secondsSinceProcessStartUp() {
@ -61,18 +60,22 @@ public:
static void StaticAlarmCallback();
static void StaticCrashSignalCallback();
static void StaticExitCallback();
static void StaticInterruptCallback();
static void StaticFileSizeExceedCallback();
static void StaticGracefulExitCallback();
void ExecuteCallback(const uint8_t *Data, size_t Size);
size_t RunOne(const uint8_t *Data, size_t Size);
bool RunOne(const uint8_t *Data, size_t Size, bool MayDeleteFile = false,
InputInfo *II = nullptr, bool *FoundUniqFeatures = nullptr);
// Merge Corpora[1:] into Corpora[0].
void Merge(const std::vector<std::string> &Corpora);
void CrashResistantMerge(const std::vector<std::string> &Args,
const std::vector<std::string> &Corpora,
void Merge(const Vector<std::string> &Corpora);
void CrashResistantMerge(const Vector<std::string> &Args,
const Vector<std::string> &Corpora,
const char *CoverageSummaryInputPathOrNull,
const char *CoverageSummaryOutputPathOrNull);
const char *CoverageSummaryOutputPathOrNull,
const char *MergeControlFilePathOrNull);
void CrashResistantMergeInternalStep(const std::string &ControlFilePath);
MutationDispatcher &GetMD() { return MD; }
void PrintFinalStats();
@ -91,28 +94,20 @@ public:
private:
void AlarmCallback();
void CrashCallback();
void ExitCallback();
void MaybeExitGracefully();
void CrashOnOverwrittenData();
void InterruptCallback();
void MutateAndTestOne();
void PurgeAllocator();
void ReportNewCoverage(InputInfo *II, const Unit &U);
size_t RunOne(const Unit &U) { return RunOne(U.data(), U.size()); }
void PrintPulseAndReportSlowInput(const uint8_t *Data, size_t Size);
void WriteToOutputCorpus(const Unit &U);
void WriteUnitToFileWithPrefix(const Unit &U, const char *Prefix);
void PrintStats(const char *Where, const char *End = "\n", size_t Units = 0);
void PrintStatusForNewUnit(const Unit &U);
void ShuffleCorpus(UnitVector *V);
void AddToCorpus(const Unit &U);
void PrintStatusForNewUnit(const Unit &U, const char *Text);
void CheckExitOnSrcPosOrItem();
// Trace-based fuzzing: we run a unit with some kind of tracing
// enabled and record potentially useful mutations. Then
// We apply these mutations one by one to the unit and run it again.
// Start tracing; forget all previously proposed mutations.
void StartTraceRecording();
// Stop tracing.
void StopTraceRecording();
static void StaticDeathCallback();
void DumpCurrentUnit(const char *Prefix);
void DeathCallback();
@ -123,12 +118,18 @@ private:
uint8_t BaseSha1[kSHA1NumBytes]; // Checksum of the base unit.
bool RunningCB = false;
bool GracefulExitRequested = false;
size_t TotalNumberOfRuns = 0;
size_t NumberOfNewUnitsAdded = 0;
size_t LastCorpusUpdateRun = 0;
bool HasMoreMallocsThanFrees = false;
size_t NumberOfLeakDetectionAttempts = 0;
system_clock::time_point LastAllocatorPurgeAttemptTime = system_clock::now();
UserCallback CB;
InputCorpus &Corpus;
MutationDispatcher &MD;
@ -141,6 +142,9 @@ private:
size_t MaxInputLen = 0;
size_t MaxMutationLen = 0;
size_t TmpMaxMutationLen = 0;
Vector<uint32_t> UniqFeatureSetTmp;
// Need to know our own thread.
static thread_local bool IsMyThread;

Просмотреть файл

@ -10,8 +10,8 @@
//===----------------------------------------------------------------------===//
#include "FuzzerCorpus.h"
#include "FuzzerInternal.h"
#include "FuzzerIO.h"
#include "FuzzerInternal.h"
#include "FuzzerMutate.h"
#include "FuzzerRandom.h"
#include "FuzzerShmem.h"
@ -19,12 +19,10 @@
#include <algorithm>
#include <cstring>
#include <memory>
#include <mutex>
#include <set>
#if defined(__has_include)
#if __has_include(<sanitizer / coverage_interface.h>)
#include <sanitizer/coverage_interface.h>
#endif
#if __has_include(<sanitizer / lsan_interface.h>)
#include <sanitizer/lsan_interface.h>
#endif
@ -72,15 +70,39 @@ struct MallocFreeTracer {
std::atomic<size_t> Mallocs;
std::atomic<size_t> Frees;
int TraceLevel = 0;
std::recursive_mutex TraceMutex;
bool TraceDisabled = false;
};
static MallocFreeTracer AllocTracer;
// 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;
};
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) {
TraceLock Lock;
if (Lock.IsDisabled())
return;
Printf("MALLOC[%zd] %p %zd\n", N, ptr, size);
if (TraceLevel >= 2 && EF)
EF->__sanitizer_print_stack_trace();
@ -91,6 +113,9 @@ ATTRIBUTE_NO_SANITIZE_MEMORY
void FreeHook(const volatile void *ptr) {
size_t N = AllocTracer.Frees++;
if (int TraceLevel = AllocTracer.TraceLevel) {
TraceLock Lock;
if (Lock.IsDisabled())
return;
Printf("FREE[%zd] %p\n", N, ptr);
if (TraceLevel >= 2 && EF)
EF->__sanitizer_print_stack_trace();
@ -99,7 +124,7 @@ void FreeHook(const volatile void *ptr) {
// Crash on a single malloc that exceeds the rss limit.
void Fuzzer::HandleMalloc(size_t Size) {
if (!Options.RssLimitMb || (Size >> 20) < (size_t)Options.RssLimitMb)
if (!Options.MallocLimitMb || (Size >> 20) < (size_t)Options.MallocLimitMb)
return;
Printf("==%d== ERROR: libFuzzer: out-of-memory (malloc(%zd))\n", GetPid(),
Size);
@ -117,7 +142,6 @@ Fuzzer::Fuzzer(UserCallback CB, InputCorpus &Corpus, MutationDispatcher &MD,
: CB(CB), Corpus(Corpus), MD(MD), Options(Options) {
if (EF->__sanitizer_set_death_callback)
EF->__sanitizer_set_death_callback(StaticDeathCallback);
InitializeTraceState();
assert(!F);
F = this;
TPC.ResetMaps();
@ -126,22 +150,24 @@ Fuzzer::Fuzzer(UserCallback CB, InputCorpus &Corpus, MutationDispatcher &MD,
EF->__sanitizer_install_malloc_and_free_hooks(MallocHook, FreeHook);
TPC.SetUseCounters(Options.UseCounters);
TPC.SetUseValueProfile(Options.UseValueProfile);
TPC.SetPrintNewPCs(Options.PrintNewCovPcs);
TPC.SetUseClangCoverage(Options.UseClangCoverage);
if (Options.Verbosity)
TPC.PrintModuleInfo();
if (!Options.OutputCorpus.empty() && Options.ReloadIntervalSec)
EpochOfLastReadOfOutputCorpus = GetEpoch(Options.OutputCorpus);
MaxInputLen = MaxMutationLen = Options.MaxLen;
TmpMaxMutationLen = Max(size_t(4), Corpus.MaxInputSize());
AllocateCurrentUnitData();
CurrentUnitSize = 0;
memset(BaseSha1, 0, sizeof(BaseSha1));
}
Fuzzer::~Fuzzer() { }
Fuzzer::~Fuzzer() {}
void Fuzzer::AllocateCurrentUnitData() {
if (CurrentUnitData || MaxInputLen == 0) return;
if (CurrentUnitData || MaxInputLen == 0)
return;
CurrentUnitData = new uint8_t[MaxInputLen];
}
@ -151,7 +177,8 @@ void Fuzzer::StaticDeathCallback() {
}
void Fuzzer::DumpCurrentUnit(const char *Prefix) {
if (!CurrentUnitData) return; // Happens when running individual inputs.
if (!CurrentUnitData)
return; // Happens when running individual inputs.
MD.PrintMutationSequence();
Printf("; base unit: %s\n", Sha1ToString(BaseSha1).c_str());
size_t UnitSize = CurrentUnitSize;
@ -179,11 +206,22 @@ void Fuzzer::StaticCrashSignalCallback() {
F->CrashCallback();
}
void Fuzzer::StaticExitCallback() {
assert(F);
F->ExitCallback();
}
void Fuzzer::StaticInterruptCallback() {
assert(F);
F->InterruptCallback();
}
void Fuzzer::StaticGracefulExitCallback() {
assert(F);
F->GracefulExitRequested = true;
Printf("INFO: signal received, trying to exit gracefully\n");
}
void Fuzzer::StaticFileSizeExceedCallback() {
Printf("==%lu== ERROR: libFuzzer: file size exceeded\n", GetPid());
exit(1);
@ -199,13 +237,32 @@ void Fuzzer::CrashCallback() {
Printf("SUMMARY: libFuzzer: deadly signal\n");
DumpCurrentUnit("crash-");
PrintFinalStats();
_Exit(Options.ErrorExitCode); // Stop right now.
_Exit(Options.ErrorExitCode); // Stop right now.
}
void Fuzzer::ExitCallback() {
if (!RunningCB)
return; // This exit did not come from the user callback
Printf("==%lu== ERROR: libFuzzer: fuzz target exited\n", GetPid());
if (EF->__sanitizer_print_stack_trace)
EF->__sanitizer_print_stack_trace();
Printf("SUMMARY: libFuzzer: fuzz target exited\n");
DumpCurrentUnit("crash-");
PrintFinalStats();
_Exit(Options.ErrorExitCode);
}
void Fuzzer::MaybeExitGracefully() {
if (!GracefulExitRequested) return;
Printf("==%lu== INFO: libFuzzer: exiting as requested\n", GetPid());
PrintFinalStats();
_Exit(0);
}
void Fuzzer::InterruptCallback() {
Printf("==%lu== libFuzzer: run interrupted; exiting\n", GetPid());
PrintFinalStats();
_Exit(0); // Stop right now, don't perform any at-exit actions.
_Exit(0); // Stop right now, don't perform any at-exit actions.
}
NO_SANITIZE_MEMORY
@ -213,7 +270,8 @@ void Fuzzer::AlarmCallback() {
assert(Options.UnitTimeoutSec > 0);
// In Windows Alarm callback is executed by a different thread.
#if !LIBFUZZER_WINDOWS
if (!InFuzzingThread()) return;
if (!InFuzzingThread())
return;
#endif
if (!RunningCB)
return; // We have not started running units yet.
@ -259,11 +317,11 @@ void Fuzzer::PrintStats(const char *Where, const char *End, size_t Units) {
if (size_t N = TPC.GetTotalPCCoverage())
Printf(" cov: %zd", N);
if (size_t N = Corpus.NumFeatures())
Printf( " ft: %zd", N);
Printf(" ft: %zd", N);
if (!Corpus.empty()) {
Printf(" corp: %zd", Corpus.NumActiveUnits());
if (size_t N = Corpus.SizeInBytes()) {
if (N < (1<<14))
if (N < (1 << 14))
Printf("/%zdb", N);
else if (N < (1 << 24))
Printf("/%zdKb", N >> 10);
@ -271,6 +329,8 @@ void Fuzzer::PrintStats(const char *Where, const char *End, size_t Units) {
Printf("/%zdMb", N >> 20);
}
}
if (TmpMaxMutationLen)
Printf(" lim: %zd", TmpMaxMutationLen);
if (Units)
Printf(" units: %zd", Units);
@ -286,7 +346,8 @@ void Fuzzer::PrintFinalStats() {
TPC.DumpCoverage();
if (Options.PrintCorpusStats)
Corpus.PrintStats();
if (!Options.PrintFinalStats) return;
if (!Options.PrintFinalStats)
return;
size_t ExecPerSec = execPerSec();
Printf("stat::number_of_executed_units: %zd\n", TotalNumberOfRuns);
Printf("stat::average_exec_per_sec: %zd\n", ExecPerSec);
@ -301,7 +362,9 @@ void Fuzzer::SetMaxInputLen(size_t MaxInputLen) {
this->MaxInputLen = MaxInputLen;
this->MaxMutationLen = MaxInputLen;
AllocateCurrentUnitData();
Printf("INFO: -max_len is not provided, using %zd\n", MaxInputLen);
Printf("INFO: -max_len is not provided; "
"libFuzzer will not generate inputs larger than %zd bytes\n",
MaxInputLen);
}
void Fuzzer::SetMaxMutationLen(size_t MaxMutationLen) {
@ -311,18 +374,18 @@ void Fuzzer::SetMaxMutationLen(size_t MaxMutationLen) {
void Fuzzer::CheckExitOnSrcPosOrItem() {
if (!Options.ExitOnSrcPos.empty()) {
static auto *PCsSet = new std::set<uintptr_t>;
for (size_t i = 1, N = TPC.GetNumPCs(); i < N; i++) {
uintptr_t PC = TPC.GetPC(i);
if (!PC) continue;
if (!PCsSet->insert(PC).second) continue;
std::string Descr = DescribePC("%L", PC);
static auto *PCsSet = new Set<uintptr_t>;
auto HandlePC = [&](uintptr_t PC) {
if (!PCsSet->insert(PC).second)
return;
std::string Descr = DescribePC("%F %L", PC + 1);
if (Descr.find(Options.ExitOnSrcPos) != std::string::npos) {
Printf("INFO: found line matching '%s', exiting.\n",
Options.ExitOnSrcPos.c_str());
_Exit(0);
}
}
};
TPC.ForEachObservedPC(HandlePC);
}
if (!Options.ExitOnItem.empty()) {
if (Corpus.HasUnit(Options.ExitOnItem)) {
@ -334,8 +397,9 @@ void Fuzzer::CheckExitOnSrcPosOrItem() {
}
void Fuzzer::RereadOutputCorpus(size_t MaxSize) {
if (Options.OutputCorpus.empty() || !Options.ReloadIntervalSec) return;
std::vector<Unit> AdditionalCorpus;
if (Options.OutputCorpus.empty() || !Options.ReloadIntervalSec)
return;
Vector<Unit> AdditionalCorpus;
ReadDirToVectorOfUnits(Options.OutputCorpus.c_str(), &AdditionalCorpus,
&EpochOfLastReadOfOutputCorpus, MaxSize,
/*ExitOnError*/ false);
@ -346,9 +410,8 @@ void Fuzzer::RereadOutputCorpus(size_t MaxSize) {
if (U.size() > MaxSize)
U.resize(MaxSize);
if (!Corpus.HasUnit(U)) {
if (size_t NumFeatures = RunOne(U)) {
if (RunOne(U.data(), U.size())) {
CheckExitOnSrcPosOrItem();
Corpus.AddToCorpus(U, NumFeatures);
Reloaded = true;
}
}
@ -357,51 +420,7 @@ void Fuzzer::RereadOutputCorpus(size_t MaxSize) {
PrintStats("RELOAD");
}
void Fuzzer::ShuffleCorpus(UnitVector *V) {
std::shuffle(V->begin(), V->end(), MD.GetRand());
if (Options.PreferSmall)
std::stable_sort(V->begin(), V->end(), [](const Unit &A, const Unit &B) {
return A.size() < B.size();
});
}
void Fuzzer::ShuffleAndMinimize(UnitVector *InitialCorpus) {
Printf("#0\tREAD units: %zd\n", InitialCorpus->size());
if (Options.ShuffleAtStartUp)
ShuffleCorpus(InitialCorpus);
// Test the callback with empty input and never try it again.
uint8_t dummy;
ExecuteCallback(&dummy, 0);
for (const auto &U : *InitialCorpus) {
if (size_t NumFeatures = RunOne(U)) {
CheckExitOnSrcPosOrItem();
Corpus.AddToCorpus(U, NumFeatures);
}
TryDetectingAMemoryLeak(U.data(), U.size(),
/*DuringInitialCorpusExecution*/ true);
}
PrintStats("INITED");
if (Corpus.empty()) {
Printf("ERROR: no interesting inputs were found. "
"Is the code instrumented for coverage? Exiting.\n");
exit(1);
}
}
size_t Fuzzer::RunOne(const uint8_t *Data, size_t Size) {
if (!Size) return 0;
TotalNumberOfRuns++;
ExecuteCallback(Data, Size);
size_t NumUpdatesBefore = Corpus.NumFeatureUpdates();
TPC.CollectFeatures([&](size_t Feature) {
Corpus.AddFeature(Feature, Size, Options.Shrink);
});
size_t NumUpdatesAfter = Corpus.NumFeatureUpdates();
void Fuzzer::PrintPulseAndReportSlowInput(const uint8_t *Data, size_t Size) {
auto TimeOfUnit =
duration_cast<seconds>(UnitStopTime - UnitStartTime).count();
if (!(TotalNumberOfRuns & (TotalNumberOfRuns - 1)) &&
@ -413,7 +432,45 @@ size_t Fuzzer::RunOne(const uint8_t *Data, size_t Size) {
Printf("Slowest unit: %zd s:\n", TimeOfLongestUnitInSeconds);
WriteUnitToFileWithPrefix({Data, Data + Size}, "slow-unit-");
}
return NumUpdatesAfter - NumUpdatesBefore;
}
bool Fuzzer::RunOne(const uint8_t *Data, size_t Size, bool MayDeleteFile,
InputInfo *II, bool *FoundUniqFeatures) {
if (!Size)
return false;
ExecuteCallback(Data, Size);
UniqFeatureSetTmp.clear();
size_t FoundUniqFeaturesOfII = 0;
size_t NumUpdatesBefore = Corpus.NumFeatureUpdates();
TPC.CollectFeatures([&](size_t Feature) {
if (Options.UseFeatureFrequency)
Corpus.UpdateFeatureFrequency(Feature);
if (Corpus.AddFeature(Feature, Size, Options.Shrink))
UniqFeatureSetTmp.push_back(Feature);
if (Options.ReduceInputs && II)
if (std::binary_search(II->UniqFeatureSet.begin(),
II->UniqFeatureSet.end(), Feature))
FoundUniqFeaturesOfII++;
});
if (FoundUniqFeatures)
*FoundUniqFeatures = FoundUniqFeaturesOfII;
PrintPulseAndReportSlowInput(Data, Size);
size_t NumNewFeatures = Corpus.NumFeatureUpdates() - NumUpdatesBefore;
if (NumNewFeatures) {
TPC.UpdateObservedPCs();
Corpus.AddToCorpus({Data, Data + Size}, NumNewFeatures, MayDeleteFile,
UniqFeatureSetTmp);
return true;
}
if (II && FoundUniqFeaturesOfII &&
FoundUniqFeaturesOfII == II->UniqFeatureSet.size() &&
II->U.size() > Size) {
Corpus.Replace(II, {Data, Data + Size});
return true;
}
return false;
}
size_t Fuzzer::GetCurrentUnitInFuzzingThead(const uint8_t **Data) const {
@ -441,6 +498,8 @@ static bool LooseMemeq(const uint8_t *A, const uint8_t *B, size_t Size) {
}
void Fuzzer::ExecuteCallback(const uint8_t *Data, size_t Size) {
TPC.RecordInitialStack();
TotalNumberOfRuns++;
assert(InFuzzingThread());
if (SMR.IsClient())
SMR.WriteByteArray(Data, Size);
@ -475,7 +534,7 @@ void Fuzzer::WriteToOutputCorpus(const Unit &U) {
std::string Path = DirPlusFile(Options.OutputCorpus, Hash(U));
WriteToFile(U, Path);
if (Options.Verbosity >= 2)
Printf("Written to %s\n", Path.c_str());
Printf("Written %zd bytes to %s\n", U.size(), Path.c_str());
}
void Fuzzer::WriteUnitToFileWithPrefix(const Unit &U, const char *Prefix) {
@ -491,12 +550,12 @@ void Fuzzer::WriteUnitToFileWithPrefix(const Unit &U, const char *Prefix) {
Printf("Base64: %s\n", Base64(U).c_str());
}
void Fuzzer::PrintStatusForNewUnit(const Unit &U) {
void Fuzzer::PrintStatusForNewUnit(const Unit &U, const char *Text) {
if (!Options.PrintNEW)
return;
PrintStats("NEW ", "");
PrintStats(Text, "");
if (Options.Verbosity) {
Printf(" L: %zd ", U.size());
Printf(" L: %zd/%zd ", U.size(), Corpus.MaxInputSize());
MD.PrintMutationSequence();
Printf("\n");
}
@ -505,27 +564,34 @@ void Fuzzer::PrintStatusForNewUnit(const Unit &U) {
void Fuzzer::ReportNewCoverage(InputInfo *II, const Unit &U) {
II->NumSuccessfullMutations++;
MD.RecordSuccessfulMutationSequence();
PrintStatusForNewUnit(U);
PrintStatusForNewUnit(U, II->Reduced ? "REDUCE" : "NEW ");
WriteToOutputCorpus(U);
NumberOfNewUnitsAdded++;
TPC.PrintNewPCs();
CheckExitOnSrcPosOrItem(); // Check only after the unit is saved to corpus.
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) {
if (!HasMoreMallocsThanFrees) return; // mallocs==frees, a leak is unlikely.
if (!Options.DetectLeaks) return;
if (!HasMoreMallocsThanFrees)
return; // mallocs==frees, a leak is unlikely.
if (!Options.DetectLeaks)
return;
if (!DuringInitialCorpusExecution &&
TotalNumberOfRuns >= Options.MaxNumberOfRuns)
return;
if (!&(EF->__lsan_enable) || !&(EF->__lsan_disable) ||
!(EF->__lsan_do_recoverable_leak_check))
return; // No lsan.
return; // No lsan.
// Run the target once again, but with lsan disabled so that if there is
// a real leak we do not report it twice.
EF->__lsan_disable();
ExecuteCallback(Data, Size);
EF->__lsan_enable();
if (!HasMoreMallocsThanFrees) return; // a leak is unlikely.
if (!HasMoreMallocsThanFrees)
return; // a leak is unlikely.
if (NumberOfLeakDetectionAttempts++ > 1000) {
Options.DetectLeaks = false;
Printf("INFO: libFuzzer disabled leak detection after every mutation.\n"
@ -546,27 +612,16 @@ void Fuzzer::TryDetectingAMemoryLeak(const uint8_t *Data, size_t Size,
CurrentUnitSize = Size;
DumpCurrentUnit("leak-");
PrintFinalStats();
_Exit(Options.ErrorExitCode); // not exit() to disable lsan further on.
_Exit(Options.ErrorExitCode); // not exit() to disable lsan further on.
}
}
static size_t ComputeMutationLen(size_t MaxInputSize, size_t MaxMutationLen,
Random &Rand) {
assert(MaxInputSize <= MaxMutationLen);
if (MaxInputSize == MaxMutationLen) return MaxMutationLen;
size_t Result = MaxInputSize;
size_t R = Rand.Rand();
if ((R % (1U << 7)) == 0)
Result++;
if ((R % (1U << 15)) == 0)
Result += 10 + Result / 2;
return Min(Result, MaxMutationLen);
}
void Fuzzer::MutateAndTestOne() {
MD.StartMutationSequence();
auto &II = Corpus.ChooseUnitToMutate(MD.GetRand());
if (Options.UseFeatureFrequency)
Corpus.UpdateFeatureFrequencyScore(&II);
const auto &U = II.U;
memcpy(BaseSha1, II.Sha1, sizeof(BaseSha1));
assert(CurrentUnitData);
@ -577,36 +632,115 @@ void Fuzzer::MutateAndTestOne() {
assert(MaxMutationLen > 0);
size_t CurrentMaxMutationLen =
Options.ExperimentalLenControl
? ComputeMutationLen(Corpus.MaxInputSize(), MaxMutationLen,
MD.GetRand())
: MaxMutationLen;
Min(MaxMutationLen, Max(U.size(), TmpMaxMutationLen));
assert(CurrentMaxMutationLen > 0);
for (int i = 0; i < Options.MutateDepth; i++) {
if (TotalNumberOfRuns >= Options.MaxNumberOfRuns)
break;
MaybeExitGracefully();
size_t NewSize = 0;
NewSize = MD.Mutate(CurrentUnitData, Size, CurrentMaxMutationLen);
assert(NewSize > 0 && "Mutator returned empty unit");
assert(NewSize <= CurrentMaxMutationLen && "Mutator return overisized unit");
assert(NewSize <= CurrentMaxMutationLen && "Mutator return oversized unit");
Size = NewSize;
if (i == 0)
StartTraceRecording();
II.NumExecutedMutations++;
if (size_t NumFeatures = RunOne(CurrentUnitData, Size)) {
Corpus.AddToCorpus({CurrentUnitData, CurrentUnitData + Size}, NumFeatures,
/*MayDeleteFile=*/true);
ReportNewCoverage(&II, {CurrentUnitData, CurrentUnitData + Size});
CheckExitOnSrcPosOrItem();
}
StopTraceRecording();
bool FoundUniqFeatures = false;
bool NewCov = RunOne(CurrentUnitData, Size, /*MayDeleteFile=*/true, &II,
&FoundUniqFeatures);
TryDetectingAMemoryLeak(CurrentUnitData, Size,
/*DuringInitialCorpusExecution*/ false);
if (NewCov) {
ReportNewCoverage(&II, {CurrentUnitData, CurrentUnitData + Size});
break; // We will mutate this input more in the next rounds.
}
if (Options.ReduceDepth && !FoundUniqFeatures)
break;
}
}
void Fuzzer::Loop() {
TPC.InitializePrintNewPCs();
void Fuzzer::PurgeAllocator() {
if (Options.PurgeAllocatorIntervalSec < 0 || !EF->__sanitizer_purge_allocator)
return;
if (duration_cast<seconds>(system_clock::now() -
LastAllocatorPurgeAttemptTime)
.count() < Options.PurgeAllocatorIntervalSec)
return;
if (Options.RssLimitMb <= 0 ||
GetPeakRSSMb() > static_cast<size_t>(Options.RssLimitMb) / 2)
EF->__sanitizer_purge_allocator();
LastAllocatorPurgeAttemptTime = system_clock::now();
}
void Fuzzer::ReadAndExecuteSeedCorpora(const Vector<std::string> &CorpusDirs) {
const size_t kMaxSaneLen = 1 << 20;
const size_t kMinDefaultLen = 4096;
Vector<SizedFile> SizedFiles;
size_t MaxSize = 0;
size_t MinSize = -1;
size_t TotalSize = 0;
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 : SizedFiles) {
MaxSize = Max(File.Size, MaxSize);
MinSize = Min(File.Size, MinSize);
TotalSize += File.Size;
}
if (Options.MaxLen == 0)
SetMaxInputLen(std::min(std::max(kMinDefaultLen, MaxSize), kMaxSaneLen));
assert(MaxInputLen > 0);
// Test the callback with empty input and never try it again.
uint8_t dummy = 0;
ExecuteCallback(&dummy, 0);
if (SizedFiles.empty()) {
Printf("INFO: A corpus is not provided, starting from an empty corpus\n");
Unit U({'\n'}); // Valid ASCII input.
RunOne(U.data(), U.size());
} else {
Printf("INFO: seed corpus: files: %zd min: %zdb max: %zdb total: %zdb"
" rss: %zdMb\n",
SizedFiles.size(), MinSize, MaxSize, TotalSize, GetPeakRSSMb());
if (Options.ShuffleAtStartUp)
std::shuffle(SizedFiles.begin(), SizedFiles.end(), MD.GetRand());
if (Options.PreferSmall) {
std::stable_sort(SizedFiles.begin(), SizedFiles.end());
assert(SizedFiles.front().Size <= SizedFiles.back().Size);
}
// Load and execute inputs one by one.
for (auto &SF : SizedFiles) {
auto U = FileToVector(SF.File, MaxInputLen, /*ExitOnError=*/false);
assert(U.size() <= MaxInputLen);
RunOne(U.data(), U.size());
CheckExitOnSrcPosOrItem();
TryDetectingAMemoryLeak(U.data(), U.size(),
/*DuringInitialCorpusExecution*/ true);
}
}
PrintStats("INITED");
if (Corpus.empty()) {
Printf("ERROR: no interesting inputs were found. "
"Is the code instrumented for coverage? Exiting.\n");
exit(1);
}
}
void Fuzzer::Loop(const Vector<std::string> &CorpusDirs) {
ReadAndExecuteSeedCorpora(CorpusDirs);
TPC.SetPrintNewPCs(Options.PrintNewCovPcs);
TPC.SetPrintNewFuncs(Options.PrintNewCovFuncs);
system_clock::time_point LastCorpusReload = system_clock::now();
if (Options.DoCrossOver)
MD.SetCorpus(&Corpus);
@ -619,9 +753,26 @@ void Fuzzer::Loop() {
}
if (TotalNumberOfRuns >= Options.MaxNumberOfRuns)
break;
if (TimedOut()) break;
if (TimedOut())
break;
// Update TmpMaxMutationLen
if (Options.LenControl) {
if (TmpMaxMutationLen < MaxMutationLen &&
TotalNumberOfRuns - LastCorpusUpdateRun >
Options.LenControl * Log(TmpMaxMutationLen)) {
TmpMaxMutationLen =
Min(MaxMutationLen, TmpMaxMutationLen + Log(TmpMaxMutationLen));
LastCorpusUpdateRun = TotalNumberOfRuns;
}
} else {
TmpMaxMutationLen = MaxMutationLen;
}
// Perform several mutations and runs.
MutateAndTestOne();
PurgeAllocator();
}
PrintStats("DONE ", "\n");
@ -629,14 +780,16 @@ void Fuzzer::Loop() {
}
void Fuzzer::MinimizeCrashLoop(const Unit &U) {
if (U.size() <= 1) return;
if (U.size() <= 1)
return;
while (!TimedOut() && TotalNumberOfRuns < Options.MaxNumberOfRuns) {
MD.StartMutationSequence();
memcpy(CurrentUnitData, U.data(), U.size());
for (int i = 0; i < Options.MutateDepth; i++) {
size_t NewSize = MD.Mutate(CurrentUnitData, U.size(), MaxMutationLen);
assert(NewSize > 0 && NewSize <= MaxMutationLen);
RunOne(CurrentUnitData, NewSize);
ExecuteCallback(CurrentUnitData, NewSize);
PrintPulseAndReportSlowInput(CurrentUnitData, NewSize);
TryDetectingAMemoryLeak(CurrentUnitData, NewSize,
/*DuringInitialCorpusExecution*/ false);
}
@ -657,7 +810,8 @@ void Fuzzer::AnnounceOutput(const uint8_t *Data, size_t Size) {
if (Data[i] != OtherData[i])
break;
Printf("==%lu== ERROR: libFuzzer: equivalence-mismatch. Sizes: %zd %zd; "
"offset %zd\n", GetPid(), Size, OtherSize, i);
"offset %zd\n",
GetPid(), Size, OtherSize, i);
DumpCurrentUnit("mismatch-");
Printf("SUMMARY: libFuzzer: equivalence-mismatch\n");
PrintFinalStats();
@ -670,14 +824,16 @@ void Fuzzer::AnnounceOutput(const uint8_t *Data, size_t Size) {
extern "C" {
size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize) {
__attribute__((visibility("default"))) size_t
LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize) {
assert(fuzzer::F);
return fuzzer::F->GetMD().DefaultMutate(Data, Size, MaxSize);
}
// Experimental
void LLVMFuzzerAnnounceOutput(const uint8_t *Data, size_t Size) {
__attribute__((visibility("default"))) void
LLVMFuzzerAnnounceOutput(const uint8_t *Data, size_t Size) {
assert(fuzzer::F);
fuzzer::F->AnnounceOutput(Data, Size);
}
} // extern "C"
} // extern "C"

Просмотреть файл

@ -16,6 +16,6 @@ extern "C" {
int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size);
} // extern "C"
int main(int argc, char **argv) {
__attribute__((visibility("default"))) int main(int argc, char **argv) {
return fuzzer::FuzzerDriver(&argc, &argv, LLVMFuzzerTestOneInput);
}

Просмотреть файл

@ -9,9 +9,10 @@
// Merging corpora.
//===----------------------------------------------------------------------===//
#include "FuzzerInternal.h"
#include "FuzzerIO.h"
#include "FuzzerCommand.h"
#include "FuzzerMerge.h"
#include "FuzzerIO.h"
#include "FuzzerInternal.h"
#include "FuzzerTracePC.h"
#include "FuzzerUtil.h"
@ -74,7 +75,7 @@ bool Merger::Parse(std::istream &IS, bool ParseCoverage) {
size_t ExpectedStartMarker = 0;
const size_t kInvalidStartMarker = -1;
size_t LastSeenStartMarker = kInvalidStartMarker;
std::vector<uint32_t> TmpFeatures;
Vector<uint32_t> TmpFeatures;
while (std::getline(IS, Line, '\n')) {
std::istringstream ISS1(Line);
std::string Marker;
@ -122,11 +123,11 @@ size_t Merger::ApproximateMemoryConsumption() const {
// Decides which files need to be merged (add thost to NewFiles).
// Returns the number of new features added.
size_t Merger::Merge(const std::set<uint32_t> &InitialFeatures,
std::vector<std::string> *NewFiles) {
size_t Merger::Merge(const Set<uint32_t> &InitialFeatures,
Vector<std::string> *NewFiles) {
NewFiles->clear();
assert(NumFilesInFirstCorpus <= Files.size());
std::set<uint32_t> AllFeatures(InitialFeatures);
Set<uint32_t> AllFeatures(InitialFeatures);
// What features are in the initial corpus?
for (size_t i = 0; i < NumFilesInFirstCorpus; i++) {
@ -138,7 +139,7 @@ size_t Merger::Merge(const std::set<uint32_t> &InitialFeatures,
// Remove all features that we already know from all other inputs.
for (size_t i = NumFilesInFirstCorpus; i < Files.size(); i++) {
auto &Cur = Files[i].Features;
std::vector<uint32_t> Tmp;
Vector<uint32_t> Tmp;
std::set_difference(Cur.begin(), Cur.end(), AllFeatures.begin(),
AllFeatures.end(), std::inserter(Tmp, Tmp.begin()));
Cur.swap(Tmp);
@ -178,16 +179,16 @@ void Merger::PrintSummary(std::ostream &OS) {
}
}
std::set<uint32_t> Merger::AllFeatures() const {
std::set<uint32_t> S;
Set<uint32_t> Merger::AllFeatures() const {
Set<uint32_t> S;
for (auto &File : Files)
S.insert(File.Features.begin(), File.Features.end());
return S;
}
std::set<uint32_t> Merger::ParseSummary(std::istream &IS) {
Set<uint32_t> Merger::ParseSummary(std::istream &IS) {
std::string Line, Tmp;
std::set<uint32_t> Res;
Set<uint32_t> Res;
while (std::getline(IS, Line, '\n')) {
size_t N;
std::istringstream ISS1(Line);
@ -221,7 +222,9 @@ void Fuzzer::CrashResistantMergeInternalStep(const std::string &CFPath) {
M.Files.size() - M.FirstNotProcessedFile);
std::ofstream OF(CFPath, std::ofstream::out | std::ofstream::app);
Set<size_t> AllFeatures;
for (size_t i = M.FirstNotProcessedFile; i < M.Files.size(); i++) {
MaybeExitGracefully();
auto U = FileToVector(M.Files[i].Name);
if (U.size() > MaxInputLen) {
U.resize(MaxInputLen);
@ -230,70 +233,118 @@ void Fuzzer::CrashResistantMergeInternalStep(const std::string &CFPath) {
std::ostringstream StartedLine;
// Write the pre-run marker.
OF << "STARTED " << std::dec << i << " " << U.size() << "\n";
OF.flush(); // Flush is important since ExecuteCommand may crash.
OF.flush(); // Flush is important since Command::Execute may crash.
// Run.
TPC.ResetMaps();
ExecuteCallback(U.data(), U.size());
// Collect coverage.
std::set<size_t> Features;
TPC.CollectFeatures([&](size_t Feature) -> bool {
Features.insert(Feature);
return true;
// Collect coverage. We are iterating over the files in this order:
// * First, files in the initial corpus ordered by size, smallest first.
// * Then, all other files, smallest first.
// So it makes no sense to record all features for all files, instead we
// only record features that were not seen before.
Set<size_t> UniqFeatures;
TPC.CollectFeatures([&](size_t Feature) {
if (AllFeatures.insert(Feature).second)
UniqFeatures.insert(Feature);
});
// Show stats.
TotalNumberOfRuns++;
if (!(TotalNumberOfRuns & (TotalNumberOfRuns - 1)))
PrintStats("pulse ");
// Write the post-run marker and the coverage.
OF << "DONE " << i;
for (size_t F : Features)
for (size_t F : UniqFeatures)
OF << " " << std::hex << F;
OF << "\n";
OF.flush();
}
}
// Outer process. Does not call the target code and thus sohuld not fail.
void Fuzzer::CrashResistantMerge(const std::vector<std::string> &Args,
const std::vector<std::string> &Corpora,
const char *CoverageSummaryInputPathOrNull,
const char *CoverageSummaryOutputPathOrNull) {
if (Corpora.size() <= 1) {
Printf("Merge requires two or more corpus dirs\n");
return;
}
std::vector<std::string> AllFiles;
ListFilesInDirRecursive(Corpora[0], nullptr, &AllFiles, /*TopDir*/true);
size_t NumFilesInFirstCorpus = AllFiles.size();
for (size_t i = 1; i < Corpora.size(); i++)
ListFilesInDirRecursive(Corpora[i], nullptr, &AllFiles, /*TopDir*/true);
Printf("MERGE-OUTER: %zd files, %zd in the initial corpus\n",
AllFiles.size(), NumFilesInFirstCorpus);
auto CFPath = DirPlusFile(TmpDir(),
"libFuzzerTemp." + std::to_string(GetPid()) + ".txt");
// Write the control file.
static void WriteNewControlFile(const std::string &CFPath,
const Vector<SizedFile> &AllFiles,
size_t NumFilesInFirstCorpus) {
RemoveFile(CFPath);
std::ofstream ControlFile(CFPath);
ControlFile << AllFiles.size() << "\n";
ControlFile << NumFilesInFirstCorpus << "\n";
for (auto &Path: AllFiles)
ControlFile << Path << "\n";
for (auto &SF: AllFiles)
ControlFile << SF.File << "\n";
if (!ControlFile) {
Printf("MERGE-OUTER: failed to write to the control file: %s\n",
CFPath.c_str());
exit(1);
}
ControlFile.close();
}
// Execute the inner process untill it passes.
// Outer process. Does not call the target code and thus sohuld not fail.
void Fuzzer::CrashResistantMerge(const Vector<std::string> &Args,
const Vector<std::string> &Corpora,
const char *CoverageSummaryInputPathOrNull,
const char *CoverageSummaryOutputPathOrNull,
const char *MergeControlFilePathOrNull) {
if (Corpora.size() <= 1) {
Printf("Merge requires two or more corpus dirs\n");
return;
}
auto CFPath =
MergeControlFilePathOrNull
? MergeControlFilePathOrNull
: DirPlusFile(TmpDir(),
"libFuzzerTemp." + std::to_string(GetPid()) + ".txt");
size_t NumAttempts = 0;
if (MergeControlFilePathOrNull && FileSize(MergeControlFilePathOrNull)) {
Printf("MERGE-OUTER: non-empty control file provided: '%s'\n",
MergeControlFilePathOrNull);
Merger M;
std::ifstream IF(MergeControlFilePathOrNull);
if (M.Parse(IF, /*ParseCoverage=*/false)) {
Printf("MERGE-OUTER: control file ok, %zd files total,"
" first not processed file %zd\n",
M.Files.size(), M.FirstNotProcessedFile);
if (!M.LastFailure.empty())
Printf("MERGE-OUTER: '%s' will be skipped as unlucky "
"(merge has stumbled on it the last time)\n",
M.LastFailure.c_str());
if (M.FirstNotProcessedFile >= M.Files.size()) {
Printf("MERGE-OUTER: nothing to do, merge has been completed before\n");
exit(0);
}
NumAttempts = M.Files.size() - M.FirstNotProcessedFile;
} else {
Printf("MERGE-OUTER: bad control file, will overwrite it\n");
}
}
if (!NumAttempts) {
// The supplied control file is empty or bad, create a fresh one.
Vector<SizedFile> AllFiles;
GetSizedFilesFromDir(Corpora[0], &AllFiles);
size_t NumFilesInFirstCorpus = AllFiles.size();
std::sort(AllFiles.begin(), AllFiles.end());
for (size_t i = 1; i < Corpora.size(); i++)
GetSizedFilesFromDir(Corpora[i], &AllFiles);
std::sort(AllFiles.begin() + NumFilesInFirstCorpus, AllFiles.end());
Printf("MERGE-OUTER: %zd files, %zd in the initial corpus\n",
AllFiles.size(), NumFilesInFirstCorpus);
WriteNewControlFile(CFPath, AllFiles, NumFilesInFirstCorpus);
NumAttempts = AllFiles.size();
}
// Execute the inner process until it passes.
// Every inner process should execute at least one input.
std::string BaseCmd = CloneArgsWithoutX(Args, "keep-all-flags");
Command BaseCmd(Args);
BaseCmd.removeFlag("merge");
bool Success = false;
for (size_t i = 1; i <= AllFiles.size(); i++) {
Printf("MERGE-OUTER: attempt %zd\n", i);
auto ExitCode =
ExecuteCommand(BaseCmd + " -merge_control_file=" + CFPath);
for (size_t Attempt = 1; Attempt <= NumAttempts; Attempt++) {
MaybeExitGracefully();
Printf("MERGE-OUTER: attempt %zd\n", Attempt);
Command Cmd(BaseCmd);
Cmd.addFlag("merge_control_file", CFPath);
Cmd.addFlag("merge_inner", "1");
auto ExitCode = ExecuteCommand(Cmd);
if (!ExitCode) {
Printf("MERGE-OUTER: succesfull in %zd attempt(s)\n", i);
Printf("MERGE-OUTER: succesfull in %zd attempt(s)\n", Attempt);
Success = true;
break;
}
@ -318,8 +369,8 @@ void Fuzzer::CrashResistantMerge(const std::vector<std::string> &Args,
std::ofstream SummaryOut(CoverageSummaryOutputPathOrNull);
M.PrintSummary(SummaryOut);
}
std::vector<std::string> NewFiles;
std::set<uint32_t> InitialFeatures;
Vector<std::string> NewFiles;
Set<uint32_t> InitialFeatures;
if (CoverageSummaryInputPathOrNull) {
std::ifstream SummaryIn(CoverageSummaryInputPathOrNull);
InitialFeatures = M.ParseSummary(SummaryIn);
@ -330,9 +381,10 @@ void Fuzzer::CrashResistantMerge(const std::vector<std::string> &Args,
Printf("MERGE-OUTER: %zd new files with %zd new features added\n",
NewFiles.size(), NumNewFeatures);
for (auto &F: NewFiles)
WriteToOutputCorpus(FileToVector(F));
// We are done, delete the control file.
RemoveFile(CFPath);
WriteToOutputCorpus(FileToVector(F, MaxInputLen));
// We are done, delete the control file if it was a temporary one.
if (!MergeControlFilePathOrNull)
RemoveFile(CFPath);
}
} // namespace fuzzer

Просмотреть файл

@ -52,11 +52,11 @@ namespace fuzzer {
struct MergeFileInfo {
std::string Name;
size_t Size = 0;
std::vector<uint32_t> Features;
Vector<uint32_t> Features;
};
struct Merger {
std::vector<MergeFileInfo> Files;
Vector<MergeFileInfo> Files;
size_t NumFilesInFirstCorpus = 0;
size_t FirstNotProcessedFile = 0;
std::string LastFailure;
@ -65,14 +65,14 @@ struct Merger {
bool Parse(const std::string &Str, bool ParseCoverage);
void ParseOrExit(std::istream &IS, bool ParseCoverage);
void PrintSummary(std::ostream &OS);
std::set<uint32_t> ParseSummary(std::istream &IS);
size_t Merge(const std::set<uint32_t> &InitialFeatures,
std::vector<std::string> *NewFiles);
size_t Merge(std::vector<std::string> *NewFiles) {
return Merge(std::set<uint32_t>{}, NewFiles);
Set<uint32_t> ParseSummary(std::istream &IS);
size_t Merge(const Set<uint32_t> &InitialFeatures,
Vector<std::string> *NewFiles);
size_t Merge(Vector<std::string> *NewFiles) {
return Merge(Set<uint32_t>{}, NewFiles);
}
size_t ApproximateMemoryConsumption() const;
std::set<uint32_t> AllFeatures() const;
Set<uint32_t> AllFeatures() const;
};
} // namespace fuzzer

Просмотреть файл

@ -9,11 +9,11 @@
// Mutate a test input.
//===----------------------------------------------------------------------===//
#include "FuzzerMutate.h"
#include "FuzzerCorpus.h"
#include "FuzzerDefs.h"
#include "FuzzerExtFunctions.h"
#include "FuzzerIO.h"
#include "FuzzerMutate.h"
#include "FuzzerOptions.h"
namespace fuzzer {
@ -43,8 +43,6 @@ MutationDispatcher::MutationDispatcher(Random &Rand,
{&MutationDispatcher::Mutate_CrossOver, "CrossOver"},
{&MutationDispatcher::Mutate_AddWordFromManualDictionary,
"ManualDict"},
{&MutationDispatcher::Mutate_AddWordFromTemporaryAutoDictionary,
"TempAutoDict"},
{&MutationDispatcher::Mutate_AddWordFromPersistentAutoDictionary,
"PersAutoDict"},
});
@ -64,7 +62,7 @@ MutationDispatcher::MutationDispatcher(Random &Rand,
static char RandCh(Random &Rand) {
if (Rand.RandBool()) return Rand(256);
const char *Special = "!*'();:@&=+$,/?%#[]012Az-`~.\xff\x00";
const char Special[] = "!*'();:@&=+$,/?%#[]012Az-`~.\xff\x00";
return Special[Rand(sizeof(Special) - 1)];
}
@ -165,11 +163,6 @@ size_t MutationDispatcher::Mutate_AddWordFromManualDictionary(uint8_t *Data,
return AddWordFromDictionary(ManualDictionary, Data, Size, MaxSize);
}
size_t MutationDispatcher::Mutate_AddWordFromTemporaryAutoDictionary(
uint8_t *Data, size_t Size, size_t MaxSize) {
return AddWordFromDictionary(TempAutoDictionary, Data, Size, MaxSize);
}
size_t MutationDispatcher::ApplyDictionaryEntry(uint8_t *Data, size_t Size,
size_t MaxSize,
DictionaryEntry &DE) {
@ -251,7 +244,7 @@ size_t MutationDispatcher::Mutate_AddWordFromTORC(
uint8_t *Data, size_t Size, size_t MaxSize) {
Word W;
DictionaryEntry DE;
switch (Rand(3)) {
switch (Rand(4)) {
case 0: {
auto X = TPC.TORC8.Get(Rand.Rand());
DE = MakeDictionaryEntryFromCMP(X.A, X.B, Data, Size);
@ -267,6 +260,10 @@ size_t MutationDispatcher::Mutate_AddWordFromTORC(
auto X = TPC.TORCW.Get(Rand.Rand());
DE = MakeDictionaryEntryFromCMP(X.A, X.B, Data, Size);
} break;
case 3: if (Options.UseMemmem) {
auto X = TPC.MMT.Get(Rand.Rand());
DE = DictionaryEntry(X);
} break;
default:
assert(0);
}
@ -469,7 +466,7 @@ void MutationDispatcher::RecordSuccessfulMutationSequence() {
}
void MutationDispatcher::PrintRecommendedDictionary() {
std::vector<DictionaryEntry> V;
Vector<DictionaryEntry> V;
for (auto &DE : PersistentAutoDictionary)
if (!ManualDictionary.ContainsWord(DE.GetW()))
V.push_back(DE);
@ -509,7 +506,7 @@ size_t MutationDispatcher::DefaultMutate(uint8_t *Data, size_t Size,
// Mutates Data in place, returns new size.
size_t MutationDispatcher::MutateImpl(uint8_t *Data, size_t Size,
size_t MaxSize,
const std::vector<Mutator> &Mutators) {
Vector<Mutator> &Mutators) {
assert(MaxSize > 0);
// Some mutations may fail (e.g. can't insert more bytes if Size == MaxSize),
// in which case they will return 0.
@ -533,14 +530,4 @@ void MutationDispatcher::AddWordToManualDictionary(const Word &W) {
{W, std::numeric_limits<size_t>::max()});
}
void MutationDispatcher::AddWordToAutoDictionary(DictionaryEntry DE) {
static const size_t kMaxAutoDictSize = 1 << 14;
if (TempAutoDictionary.size() >= kMaxAutoDictSize) return;
TempAutoDictionary.push_back(DE);
}
void MutationDispatcher::ClearAutoDictionary() {
TempAutoDictionary.clear();
}
} // namespace fuzzer

Просмотреть файл

@ -27,7 +27,7 @@ public:
void StartMutationSequence();
/// Print the current sequence of mutations.
void PrintMutationSequence();
/// Indicate that the current sequence of mutations was successfull.
/// Indicate that the current sequence of mutations was successful.
void RecordSuccessfulMutationSequence();
/// Mutates data by invoking user-provided mutator.
size_t Mutate_Custom(uint8_t *Data, size_t Size, size_t MaxSize);
@ -52,10 +52,6 @@ public:
size_t Mutate_AddWordFromManualDictionary(uint8_t *Data, size_t Size,
size_t MaxSize);
/// Mutates data by adding a word from the temporary automatic dictionary.
size_t Mutate_AddWordFromTemporaryAutoDictionary(uint8_t *Data, size_t Size,
size_t MaxSize);
/// Mutates data by adding a word from the TORC.
size_t Mutate_AddWordFromTORC(uint8_t *Data, size_t Size, size_t MaxSize);
@ -84,8 +80,6 @@ public:
void AddWordToManualDictionary(const Word &W);
void AddWordToAutoDictionary(DictionaryEntry DE);
void ClearAutoDictionary();
void PrintRecommendedDictionary();
void SetCorpus(const InputCorpus *Corpus) { this->Corpus = Corpus; }
@ -102,7 +96,7 @@ private:
size_t AddWordFromDictionary(Dictionary &D, uint8_t *Data, size_t Size,
size_t MaxSize);
size_t MutateImpl(uint8_t *Data, size_t Size, size_t MaxSize,
const std::vector<Mutator> &Mutators);
Vector<Mutator> &Mutators);
size_t InsertPartOf(const uint8_t *From, size_t FromSize, uint8_t *To,
size_t ToSize, size_t MaxToSize);
@ -131,24 +125,24 @@ private:
// recreated periodically.
Dictionary TempAutoDictionary;
// Persistent dictionary modified by the fuzzer, consists of
// entries that led to successfull discoveries in the past mutations.
// entries that led to successful discoveries in the past mutations.
Dictionary PersistentAutoDictionary;
std::vector<Mutator> CurrentMutatorSequence;
std::vector<DictionaryEntry *> CurrentDictionaryEntrySequence;
Vector<Mutator> CurrentMutatorSequence;
Vector<DictionaryEntry *> CurrentDictionaryEntrySequence;
static const size_t kCmpDictionaryEntriesDequeSize = 16;
DictionaryEntry CmpDictionaryEntriesDeque[kCmpDictionaryEntriesDequeSize];
size_t CmpDictionaryEntriesDequeIdx = 0;
const InputCorpus *Corpus = nullptr;
std::vector<uint8_t> MutateInPlaceHere;
Vector<uint8_t> MutateInPlaceHere;
// CustomCrossOver needs its own buffer as a custom implementation may call
// LLVMFuzzerMutate, which in turn may resize MutateInPlaceHere.
std::vector<uint8_t> CustomCrossOverInPlaceHere;
Vector<uint8_t> CustomCrossOverInPlaceHere;
std::vector<Mutator> Mutators;
std::vector<Mutator> DefaultMutators;
Vector<Mutator> Mutators;
Vector<Mutator> DefaultMutators;
};
} // namespace fuzzer

Просмотреть файл

@ -18,20 +18,22 @@ namespace fuzzer {
struct FuzzingOptions {
int Verbosity = 1;
size_t MaxLen = 0;
bool ExperimentalLenControl = false;
size_t LenControl = 1000;
int UnitTimeoutSec = 300;
int TimeoutExitCode = 77;
int ErrorExitCode = 77;
int MaxTotalTimeSec = 0;
int RssLimitMb = 0;
int MallocLimitMb = 0;
bool DoCrossOver = true;
int MutateDepth = 5;
bool ReduceDepth = false;
bool UseCounters = false;
bool UseIndirCalls = true;
bool UseMemmem = true;
bool UseCmp = false;
bool UseValueProfile = false;
bool Shrink = false;
bool ReduceInputs = false;
int ReloadIntervalSec = 1;
bool ShuffleAtStartUp = true;
bool PreferSmall = true;
@ -46,11 +48,15 @@ struct FuzzingOptions {
bool SaveArtifacts = true;
bool PrintNEW = true; // Print a status line when new units are found;
bool PrintNewCovPcs = false;
int PrintNewCovFuncs = 0;
bool PrintFinalStats = false;
bool PrintCorpusStats = false;
bool PrintCoverage = false;
bool DumpCoverage = false;
bool UseClangCoverage = false;
bool DetectLeaks = true;
int PurgeAllocatorIntervalSec = 1;
int UseFeatureFrequency = false;
int TraceMalloc = 0;
bool HandleAbrt = false;
bool HandleBus = false;
@ -60,6 +66,8 @@ struct FuzzingOptions {
bool HandleSegv = false;
bool HandleTerm = false;
bool HandleXfsz = false;
bool HandleUsr1 = false;
bool HandleUsr2 = false;
};
} // namespace fuzzer

Просмотреть файл

@ -0,0 +1,38 @@
//===- FuzzerShmemPosix.cpp - Posix shared memory ---------------*- C++ -* ===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
// SharedMemoryRegion. For Fuchsia, this is just stubs as equivalence servers
// are not currently supported.
//===----------------------------------------------------------------------===//
#include "FuzzerDefs.h"
#if LIBFUZZER_FUCHSIA
#include "FuzzerShmem.h"
namespace fuzzer {
bool SharedMemoryRegion::Create(const char *Name) {
return false;
}
bool SharedMemoryRegion::Open(const char *Name) {
return false;
}
bool SharedMemoryRegion::Destroy(const char *Name) {
return false;
}
void SharedMemoryRegion::Post(int Idx) {}
void SharedMemoryRegion::Wait(int Idx) {}
} // namespace fuzzer
#endif // LIBFUZZER_FUCHSIA

Просмотреть файл

@ -14,14 +14,14 @@
#include "FuzzerIO.h"
#include "FuzzerShmem.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <semaphore.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
namespace fuzzer {

Просмотреть файл

@ -14,10 +14,10 @@
#include "FuzzerIO.h"
#include "FuzzerShmem.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
namespace fuzzer {

Просмотреть файл

@ -12,17 +12,15 @@
//
//===----------------------------------------------------------------------===//
#include "FuzzerTracePC.h"
#include "FuzzerCorpus.h"
#include "FuzzerDefs.h"
#include "FuzzerDictionary.h"
#include "FuzzerExtFunctions.h"
#include "FuzzerIO.h"
#include "FuzzerTracePC.h"
#include "FuzzerUtil.h"
#include "FuzzerValueBitMap.h"
#include <map>
#include <set>
#include <sstream>
// The coverage counters and PCs.
// These are declared as global variables named "__sancov_*" to simplify
@ -33,10 +31,16 @@ uint8_t __sancov_trace_pc_guard_8bit_counters[fuzzer::TracePC::kNumPCs];
ATTRIBUTE_INTERFACE
uintptr_t __sancov_trace_pc_pcs[fuzzer::TracePC::kNumPCs];
// Used by -fsanitize-coverage=stack-depth to track stack depth
ATTRIBUTE_INTERFACE __attribute__((tls_model("initial-exec")))
thread_local uintptr_t __sancov_lowest_stack;
namespace fuzzer {
TracePC TPC;
int ScopedDoingMyOwnMemOrStr::DoingMyOwnMemOrStr;
uint8_t *TracePC::Counters() const {
return __sancov_trace_pc_guard_8bit_counters;
}
@ -46,6 +50,8 @@ uintptr_t *TracePC::PCs() const {
}
size_t TracePC::GetTotalPCCoverage() {
if (ObservedPCs.size())
return ObservedPCs.size();
size_t Res = 0;
for (size_t i = 1, N = GetNumPCs(); i < N; i++)
if (PCs()[i])
@ -53,6 +59,26 @@ size_t TracePC::GetTotalPCCoverage() {
return Res;
}
void TracePC::HandleInline8bitCountersInit(uint8_t *Start, uint8_t *Stop) {
if (Start == Stop) return;
if (NumModulesWithInline8bitCounters &&
ModuleCounters[NumModulesWithInline8bitCounters-1].Start == Start) return;
assert(NumModulesWithInline8bitCounters <
sizeof(ModuleCounters) / sizeof(ModuleCounters[0]));
ModuleCounters[NumModulesWithInline8bitCounters++] = {Start, Stop};
NumInline8bitCounters += Stop - Start;
}
void TracePC::HandlePCsInit(const uintptr_t *Start, const uintptr_t *Stop) {
const PCTableEntry *B = reinterpret_cast<const PCTableEntry *>(Start);
const PCTableEntry *E = reinterpret_cast<const PCTableEntry *>(Stop);
if (NumPCTables && ModulePCTable[NumPCTables - 1].Start == B) return;
assert(NumPCTables < sizeof(ModulePCTable) / sizeof(ModulePCTable[0]));
ModulePCTable[NumPCTables++] = {B, E};
NumPCsInPCTables += E - B;
}
void TracePC::HandleInit(uint32_t *Start, uint32_t *Stop) {
if (Start == Stop || *Start) return;
assert(NumModules < sizeof(Modules) / sizeof(Modules[0]));
@ -72,10 +98,42 @@ void TracePC::HandleInit(uint32_t *Start, uint32_t *Stop) {
}
void TracePC::PrintModuleInfo() {
Printf("INFO: Loaded %zd modules (%zd guards): ", NumModules, NumGuards);
for (size_t i = 0; i < NumModules; i++)
Printf("[%p, %p), ", Modules[i].Start, Modules[i].Stop);
Printf("\n");
if (NumGuards) {
Printf("INFO: Loaded %zd modules (%zd guards): ", NumModules, NumGuards);
for (size_t i = 0; i < NumModules; i++)
Printf("%zd [%p, %p), ", Modules[i].Stop - Modules[i].Start,
Modules[i].Start, Modules[i].Stop);
Printf("\n");
}
if (NumModulesWithInline8bitCounters) {
Printf("INFO: Loaded %zd modules (%zd inline 8-bit counters): ",
NumModulesWithInline8bitCounters, NumInline8bitCounters);
for (size_t i = 0; i < NumModulesWithInline8bitCounters; i++)
Printf("%zd [%p, %p), ", ModuleCounters[i].Stop - ModuleCounters[i].Start,
ModuleCounters[i].Start, ModuleCounters[i].Stop);
Printf("\n");
}
if (NumPCTables) {
Printf("INFO: Loaded %zd PC tables (%zd PCs): ", NumPCTables,
NumPCsInPCTables);
for (size_t i = 0; i < NumPCTables; i++) {
Printf("%zd [%p,%p), ", ModulePCTable[i].Stop - ModulePCTable[i].Start,
ModulePCTable[i].Start, ModulePCTable[i].Stop);
}
Printf("\n");
if ((NumGuards && NumGuards != NumPCsInPCTables) ||
(NumInline8bitCounters && NumInline8bitCounters != NumPCsInPCTables)) {
Printf("ERROR: The size of coverage PC tables does not match the\n"
"number of instrumented PCs. This might be a compiler bug,\n"
"please contact the libFuzzer developers.\n"
"Also check https://bugs.llvm.org/show_bug.cgi?id=34636\n"
"for possible workarounds (tl;dr: don't use the old GNU ld)\n");
_Exit(1);
}
}
if (size_t NumClangCounters = ClangCountersEnd() - ClangCountersBegin())
Printf("INFO: %zd Clang Coverage Counters\n", NumClangCounters);
}
ATTRIBUTE_NO_SANITIZE_ALL
@ -86,21 +144,78 @@ void TracePC::HandleCallerCallee(uintptr_t Caller, uintptr_t Callee) {
ValueProfileMap.AddValueModPrime(Idx);
}
void TracePC::InitializePrintNewPCs() {
if (!DoPrintNewPCs) return;
assert(!PrintedPCs);
PrintedPCs = new std::set<uintptr_t>;
for (size_t i = 1; i < GetNumPCs(); i++)
if (PCs()[i])
PrintedPCs->insert(PCs()[i]);
void TracePC::UpdateObservedPCs() {
Vector<uintptr_t> CoveredFuncs;
auto ObservePC = [&](uintptr_t PC) {
if (ObservedPCs.insert(PC).second && DoPrintNewPCs)
PrintPC("\tNEW_PC: %p %F %L\n", "\tNEW_PC: %p\n", PC + 1);
};
auto Observe = [&](const PCTableEntry &TE) {
if (TE.PCFlags & 1)
if (ObservedFuncs.insert(TE.PC).second && NumPrintNewFuncs)
CoveredFuncs.push_back(TE.PC);
ObservePC(TE.PC);
};
if (NumPCsInPCTables) {
if (NumInline8bitCounters == NumPCsInPCTables) {
for (size_t i = 0; i < NumModulesWithInline8bitCounters; i++) {
uint8_t *Beg = ModuleCounters[i].Start;
size_t Size = ModuleCounters[i].Stop - Beg;
assert(Size ==
(size_t)(ModulePCTable[i].Stop - ModulePCTable[i].Start));
for (size_t j = 0; j < Size; j++)
if (Beg[j])
Observe(ModulePCTable[i].Start[j]);
}
} else if (NumGuards == NumPCsInPCTables) {
size_t GuardIdx = 1;
for (size_t i = 0; i < NumModules; i++) {
uint32_t *Beg = Modules[i].Start;
size_t Size = Modules[i].Stop - Beg;
assert(Size ==
(size_t)(ModulePCTable[i].Stop - ModulePCTable[i].Start));
for (size_t j = 0; j < Size; j++, GuardIdx++)
if (Counters()[GuardIdx])
Observe(ModulePCTable[i].Start[j]);
}
}
}
if (size_t NumClangCounters =
ClangCountersEnd() - ClangCountersBegin()) {
auto P = ClangCountersBegin();
for (size_t Idx = 0; Idx < NumClangCounters; Idx++)
if (P[Idx])
ObservePC((uintptr_t)Idx);
}
for (size_t i = 0, N = Min(CoveredFuncs.size(), NumPrintNewFuncs); i < N; i++) {
Printf("\tNEW_FUNC[%zd/%zd]: ", i, CoveredFuncs.size());
PrintPC("%p %F %L\n", "%p\n", CoveredFuncs[i] + 1);
}
}
void TracePC::PrintNewPCs() {
if (!DoPrintNewPCs) return;
assert(PrintedPCs);
for (size_t i = 1; i < GetNumPCs(); i++)
if (PCs()[i] && PrintedPCs->insert(PCs()[i]).second)
PrintPC("\tNEW_PC: %p %F %L\n", "\tNEW_PC: %p\n", PCs()[i]);
inline ALWAYS_INLINE uintptr_t GetPreviousInstructionPc(uintptr_t PC) {
// TODO: this implementation is x86 only.
// see sanitizer_common GetPreviousInstructionPc for full implementation.
return PC - 1;
}
inline ALWAYS_INLINE uintptr_t GetNextInstructionPc(uintptr_t PC) {
// TODO: this implementation is x86 only.
// see sanitizer_common GetPreviousInstructionPc for full implementation.
return PC + 1;
}
static std::string GetModuleName(uintptr_t PC) {
char ModulePathRaw[4096] = ""; // What's PATH_MAX in portable C++?
void *OffsetRaw = nullptr;
if (!EF->__sanitizer_get_module_and_offset_for_pc(
reinterpret_cast<void *>(PC), ModulePathRaw,
sizeof(ModulePathRaw), &OffsetRaw))
return "";
return ModulePathRaw;
}
void TracePC::PrintCoverage() {
@ -111,112 +226,59 @@ void TracePC::PrintCoverage() {
" not printing coverage\n");
return;
}
std::map<std::string, std::vector<uintptr_t>> CoveredPCsPerModule;
std::map<std::string, uintptr_t> ModuleOffsets;
std::set<std::string> CoveredDirs, CoveredFiles, CoveredFunctions,
CoveredLines;
Printf("COVERAGE:\n");
for (size_t i = 1; i < GetNumPCs(); i++) {
uintptr_t PC = PCs()[i];
if (!PC) continue;
std::string FileStr = DescribePC("%s", PC);
if (!IsInterestingCoverageFile(FileStr)) continue;
std::string FixedPCStr = DescribePC("%p", PC);
std::string FunctionStr = DescribePC("%F", PC);
std::string LineStr = DescribePC("%l", PC);
char ModulePathRaw[4096] = ""; // What's PATH_MAX in portable C++?
void *OffsetRaw = nullptr;
if (!EF->__sanitizer_get_module_and_offset_for_pc(
reinterpret_cast<void *>(PC), ModulePathRaw,
sizeof(ModulePathRaw), &OffsetRaw))
continue;
std::string Module = ModulePathRaw;
uintptr_t FixedPC = std::stoull(FixedPCStr, 0, 16);
uintptr_t PcOffset = reinterpret_cast<uintptr_t>(OffsetRaw);
ModuleOffsets[Module] = FixedPC - PcOffset;
CoveredPCsPerModule[Module].push_back(PcOffset);
CoveredFunctions.insert(FunctionStr);
CoveredFiles.insert(FileStr);
CoveredDirs.insert(DirName(FileStr));
if (!CoveredLines.insert(FileStr + ":" + LineStr).second)
continue;
Printf("COVERED: %s %s:%s\n", FunctionStr.c_str(),
FileStr.c_str(), LineStr.c_str());
}
std::string LastFunctionName = "";
std::string LastFileStr = "";
Set<size_t> UncoveredLines;
Set<size_t> CoveredLines;
std::string CoveredDirsStr;
for (auto &Dir : CoveredDirs) {
if (!CoveredDirsStr.empty())
CoveredDirsStr += ",";
CoveredDirsStr += Dir;
}
Printf("COVERED_DIRS: %s\n", CoveredDirsStr.c_str());
for (auto &M : CoveredPCsPerModule) {
std::set<std::string> UncoveredFiles, UncoveredFunctions;
std::map<std::string, std::set<int> > UncoveredLines; // Func+File => lines
auto &ModuleName = M.first;
auto &CoveredOffsets = M.second;
uintptr_t ModuleOffset = ModuleOffsets[ModuleName];
std::sort(CoveredOffsets.begin(), CoveredOffsets.end());
Printf("MODULE_WITH_COVERAGE: %s\n", ModuleName.c_str());
// sancov does not yet fully support DSOs.
// std::string Cmd = "sancov -print-coverage-pcs " + ModuleName;
std::string Cmd = DisassembleCmd(ModuleName) + " | " +
SearchRegexCmd("call.*__sanitizer_cov_trace_pc_guard");
std::string SanCovOutput;
if (!ExecuteCommandAndReadOutput(Cmd, &SanCovOutput)) {
Printf("INFO: Command failed: %s\n", Cmd.c_str());
continue;
}
std::istringstream ISS(SanCovOutput);
std::string S;
while (std::getline(ISS, S, '\n')) {
size_t PcOffsetEnd = S.find(':');
if (PcOffsetEnd == std::string::npos)
continue;
S.resize(PcOffsetEnd);
uintptr_t PcOffset = std::stoull(S, 0, 16);
if (!std::binary_search(CoveredOffsets.begin(), CoveredOffsets.end(),
PcOffset)) {
uintptr_t PC = ModuleOffset + PcOffset;
auto FileStr = DescribePC("%s", PC);
if (!IsInterestingCoverageFile(FileStr)) continue;
if (CoveredFiles.count(FileStr) == 0) {
UncoveredFiles.insert(FileStr);
continue;
auto FunctionEndCallback = [&](const std::string &CurrentFunc,
const std::string &CurrentFile) {
if (LastFunctionName != CurrentFunc) {
if (CoveredLines.empty() && !UncoveredLines.empty()) {
Printf("UNCOVERED_FUNC: %s\n", LastFunctionName.c_str());
} else {
for (auto Line : UncoveredLines) {
if (!CoveredLines.count(Line))
Printf("UNCOVERED_LINE: %s %s:%zd\n", LastFunctionName.c_str(),
LastFileStr.c_str(), Line);
}
auto FunctionStr = DescribePC("%F", PC);
if (CoveredFunctions.count(FunctionStr) == 0) {
UncoveredFunctions.insert(FunctionStr);
continue;
}
std::string LineStr = DescribePC("%l", PC);
uintptr_t Line = std::stoi(LineStr);
std::string FileLineStr = FileStr + ":" + LineStr;
if (CoveredLines.count(FileLineStr) == 0)
UncoveredLines[FunctionStr + " " + FileStr].insert(Line);
}
}
for (auto &FileLine: UncoveredLines)
for (int Line : FileLine.second)
Printf("UNCOVERED_LINE: %s:%d\n", FileLine.first.c_str(), Line);
for (auto &Func : UncoveredFunctions)
Printf("UNCOVERED_FUNC: %s\n", Func.c_str());
for (auto &File : UncoveredFiles)
Printf("UNCOVERED_FILE: %s\n", File.c_str());
}
}
inline ALWAYS_INLINE uintptr_t GetPreviousInstructionPc(uintptr_t PC) {
// TODO: this implementation is x86 only.
// see sanitizer_common GetPreviousInstructionPc for full implementation.
return PC - 1;
UncoveredLines.clear();
CoveredLines.clear();
LastFunctionName = CurrentFunc;
LastFileStr = CurrentFile;
}
};
for (size_t i = 0; i < NumPCTables; i++) {
auto &M = ModulePCTable[i];
assert(M.Start < M.Stop);
auto ModuleName = GetModuleName(M.Start->PC);
for (auto Ptr = M.Start; Ptr < M.Stop; Ptr++) {
auto PC = Ptr->PC;
auto VisualizePC = GetNextInstructionPc(PC);
bool IsObserved = ObservedPCs.count(PC);
std::string FileStr = DescribePC("%s", VisualizePC);
if (!IsInterestingCoverageFile(FileStr)) continue;
std::string FunctionStr = DescribePC("%F", VisualizePC);
FunctionEndCallback(FunctionStr, FileStr);
std::string LineStr = DescribePC("%l", VisualizePC);
size_t Line = std::stoul(LineStr);
if (IsObserved && CoveredLines.insert(Line).second)
Printf("COVERED: %s %s:%zd\n", FunctionStr.c_str(), FileStr.c_str(),
Line);
else
UncoveredLines.insert(Line);
}
}
FunctionEndCallback("", "");
}
void TracePC::DumpCoverage() {
if (EF->__sanitizer_dump_coverage) {
std::vector<uintptr_t> PCsCopy(GetNumPCs());
Vector<uintptr_t> PCsCopy(GetNumPCs());
for (size_t i = 0; i < GetNumPCs(); i++)
PCsCopy[i] = PCs()[i] ? GetPreviousInstructionPc(PCs()[i]) : 0;
EF->__sanitizer_dump_coverage(PCsCopy.data(), PCsCopy.size());
@ -275,6 +337,38 @@ void TracePC::HandleCmp(uintptr_t PC, T Arg1, T Arg2) {
ValueProfileMap.AddValue(Idx);
}
static size_t InternalStrnlen(const char *S, size_t MaxLen) {
size_t Len = 0;
for (; Len < MaxLen && S[Len]; Len++) {}
return Len;
}
// Finds min of (strlen(S1), strlen(S2)).
// Needed bacause one of these strings may actually be non-zero terminated.
static size_t InternalStrnlen2(const char *S1, const char *S2) {
size_t Len = 0;
for (; S1[Len] && S2[Len]; Len++) {}
return Len;
}
void TracePC::ClearInlineCounters() {
for (size_t i = 0; i < NumModulesWithInline8bitCounters; i++) {
uint8_t *Beg = ModuleCounters[i].Start;
size_t Size = ModuleCounters[i].Stop - Beg;
memset(Beg, 0, Size);
}
}
ATTRIBUTE_NO_SANITIZE_ALL
void TracePC::RecordInitialStack() {
int stack;
__sancov_lowest_stack = InitialStack = reinterpret_cast<uintptr_t>(&stack);
}
uintptr_t TracePC::GetMaxStackOffset() const {
return InitialStack - __sancov_lowest_stack; // Stack grows down
}
} // namespace fuzzer
extern "C" {
@ -303,6 +397,17 @@ void __sanitizer_cov_trace_pc_guard_init(uint32_t *Start, uint32_t *Stop) {
fuzzer::TPC.HandleInit(Start, Stop);
}
ATTRIBUTE_INTERFACE
void __sanitizer_cov_8bit_counters_init(uint8_t *Start, uint8_t *Stop) {
fuzzer::TPC.HandleInline8bitCountersInit(Start, Stop);
}
ATTRIBUTE_INTERFACE
void __sanitizer_cov_pcs_init(const uintptr_t *pcs_beg,
const uintptr_t *pcs_end) {
fuzzer::TPC.HandlePCsInit(pcs_beg, pcs_end);
}
ATTRIBUTE_INTERFACE
ATTRIBUTE_NO_SANITIZE_ALL
void __sanitizer_cov_trace_pc_indir(uintptr_t Callee) {
@ -318,6 +423,17 @@ void __sanitizer_cov_trace_cmp8(uint64_t Arg1, uint64_t Arg2) {
fuzzer::TPC.HandleCmp(PC, Arg1, Arg2);
}
ATTRIBUTE_INTERFACE
ATTRIBUTE_NO_SANITIZE_ALL
ATTRIBUTE_TARGET_POPCNT
// Now the __sanitizer_cov_trace_const_cmp[1248] callbacks just mimic
// the behaviour of __sanitizer_cov_trace_cmp[1248] ones. This, however,
// should be changed later to make full use of instrumentation.
void __sanitizer_cov_trace_const_cmp8(uint64_t Arg1, uint64_t Arg2) {
uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0));
fuzzer::TPC.HandleCmp(PC, Arg1, Arg2);
}
ATTRIBUTE_INTERFACE
ATTRIBUTE_NO_SANITIZE_ALL
ATTRIBUTE_TARGET_POPCNT
@ -326,6 +442,14 @@ void __sanitizer_cov_trace_cmp4(uint32_t Arg1, uint32_t Arg2) {
fuzzer::TPC.HandleCmp(PC, Arg1, Arg2);
}
ATTRIBUTE_INTERFACE
ATTRIBUTE_NO_SANITIZE_ALL
ATTRIBUTE_TARGET_POPCNT
void __sanitizer_cov_trace_const_cmp4(uint32_t Arg1, uint32_t Arg2) {
uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0));
fuzzer::TPC.HandleCmp(PC, Arg1, Arg2);
}
ATTRIBUTE_INTERFACE
ATTRIBUTE_NO_SANITIZE_ALL
ATTRIBUTE_TARGET_POPCNT
@ -334,6 +458,14 @@ void __sanitizer_cov_trace_cmp2(uint16_t Arg1, uint16_t Arg2) {
fuzzer::TPC.HandleCmp(PC, Arg1, Arg2);
}
ATTRIBUTE_INTERFACE
ATTRIBUTE_NO_SANITIZE_ALL
ATTRIBUTE_TARGET_POPCNT
void __sanitizer_cov_trace_const_cmp2(uint16_t Arg1, uint16_t Arg2) {
uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0));
fuzzer::TPC.HandleCmp(PC, Arg1, Arg2);
}
ATTRIBUTE_INTERFACE
ATTRIBUTE_NO_SANITIZE_ALL
ATTRIBUTE_TARGET_POPCNT
@ -342,6 +474,14 @@ void __sanitizer_cov_trace_cmp1(uint8_t Arg1, uint8_t Arg2) {
fuzzer::TPC.HandleCmp(PC, Arg1, Arg2);
}
ATTRIBUTE_INTERFACE
ATTRIBUTE_NO_SANITIZE_ALL
ATTRIBUTE_TARGET_POPCNT
void __sanitizer_cov_trace_const_cmp1(uint8_t Arg1, uint8_t Arg2) {
uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0));
fuzzer::TPC.HandleCmp(PC, Arg1, Arg2);
}
ATTRIBUTE_INTERFACE
ATTRIBUTE_NO_SANITIZE_ALL
ATTRIBUTE_TARGET_POPCNT
@ -392,4 +532,71 @@ void __sanitizer_cov_trace_gep(uintptr_t Idx) {
uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0));
fuzzer::TPC.HandleCmp(PC, Idx, (uintptr_t)0);
}
ATTRIBUTE_INTERFACE ATTRIBUTE_NO_SANITIZE_MEMORY
void __sanitizer_weak_hook_memcmp(void *caller_pc, const void *s1,
const void *s2, size_t n, int result) {
if (fuzzer::ScopedDoingMyOwnMemOrStr::DoingMyOwnMemOrStr) return;
if (result == 0) return; // No reason to mutate.
if (n <= 1) return; // Not interesting.
fuzzer::TPC.AddValueForMemcmp(caller_pc, s1, s2, n, /*StopAtZero*/false);
}
ATTRIBUTE_INTERFACE ATTRIBUTE_NO_SANITIZE_MEMORY
void __sanitizer_weak_hook_strncmp(void *caller_pc, const char *s1,
const char *s2, size_t n, int result) {
if (fuzzer::ScopedDoingMyOwnMemOrStr::DoingMyOwnMemOrStr) return;
if (result == 0) return; // No reason to mutate.
size_t Len1 = fuzzer::InternalStrnlen(s1, n);
size_t Len2 = fuzzer::InternalStrnlen(s2, n);
n = std::min(n, Len1);
n = std::min(n, Len2);
if (n <= 1) return; // Not interesting.
fuzzer::TPC.AddValueForMemcmp(caller_pc, s1, s2, n, /*StopAtZero*/true);
}
ATTRIBUTE_INTERFACE ATTRIBUTE_NO_SANITIZE_MEMORY
void __sanitizer_weak_hook_strcmp(void *caller_pc, const char *s1,
const char *s2, int result) {
if (fuzzer::ScopedDoingMyOwnMemOrStr::DoingMyOwnMemOrStr) return;
if (result == 0) return; // No reason to mutate.
size_t N = fuzzer::InternalStrnlen2(s1, s2);
if (N <= 1) return; // Not interesting.
fuzzer::TPC.AddValueForMemcmp(caller_pc, s1, s2, N, /*StopAtZero*/true);
}
ATTRIBUTE_INTERFACE ATTRIBUTE_NO_SANITIZE_MEMORY
void __sanitizer_weak_hook_strncasecmp(void *called_pc, const char *s1,
const char *s2, size_t n, int result) {
if (fuzzer::ScopedDoingMyOwnMemOrStr::DoingMyOwnMemOrStr) return;
return __sanitizer_weak_hook_strncmp(called_pc, s1, s2, n, result);
}
ATTRIBUTE_INTERFACE ATTRIBUTE_NO_SANITIZE_MEMORY
void __sanitizer_weak_hook_strcasecmp(void *called_pc, const char *s1,
const char *s2, int result) {
if (fuzzer::ScopedDoingMyOwnMemOrStr::DoingMyOwnMemOrStr) return;
return __sanitizer_weak_hook_strcmp(called_pc, s1, s2, result);
}
ATTRIBUTE_INTERFACE ATTRIBUTE_NO_SANITIZE_MEMORY
void __sanitizer_weak_hook_strstr(void *called_pc, const char *s1,
const char *s2, char *result) {
if (fuzzer::ScopedDoingMyOwnMemOrStr::DoingMyOwnMemOrStr) return;
fuzzer::TPC.MMT.Add(reinterpret_cast<const uint8_t *>(s2), strlen(s2));
}
ATTRIBUTE_INTERFACE ATTRIBUTE_NO_SANITIZE_MEMORY
void __sanitizer_weak_hook_strcasestr(void *called_pc, const char *s1,
const char *s2, char *result) {
if (fuzzer::ScopedDoingMyOwnMemOrStr::DoingMyOwnMemOrStr) return;
fuzzer::TPC.MMT.Add(reinterpret_cast<const uint8_t *>(s2), strlen(s2));
}
ATTRIBUTE_INTERFACE ATTRIBUTE_NO_SANITIZE_MEMORY
void __sanitizer_weak_hook_memmem(void *called_pc, const void *s1, size_t len1,
const void *s2, size_t len2, void *result) {
if (fuzzer::ScopedDoingMyOwnMemOrStr::DoingMyOwnMemOrStr) return;
fuzzer::TPC.MMT.Add(reinterpret_cast<const uint8_t *>(s2), len2);
}
} // extern "C"

Просмотреть файл

@ -45,27 +45,60 @@ struct TableOfRecentCompares {
Pair Table[kSize];
};
template <size_t kSizeT>
struct MemMemTable {
static const size_t kSize = kSizeT;
Word MemMemWords[kSize];
Word EmptyWord;
void Add(const uint8_t *Data, size_t Size) {
if (Size <= 2) return;
Size = std::min(Size, Word::GetMaxSize());
size_t Idx = SimpleFastHash(Data, Size) % kSize;
MemMemWords[Idx].Set(Data, Size);
}
const Word &Get(size_t Idx) {
for (size_t i = 0; i < kSize; i++) {
const Word &W = MemMemWords[(Idx + i) % kSize];
if (W.size()) return W;
}
EmptyWord.Set(nullptr, 0);
return EmptyWord;
}
};
class TracePC {
public:
static const size_t kNumPCs = 1 << 21;
// How many bits of PC are used from __sanitizer_cov_trace_pc.
static const size_t kTracePcBits = 18;
void HandleInit(uint32_t *start, uint32_t *stop);
void HandleInit(uint32_t *Start, uint32_t *Stop);
void HandleInline8bitCountersInit(uint8_t *Start, uint8_t *Stop);
void HandlePCsInit(const uintptr_t *Start, const uintptr_t *Stop);
void HandleCallerCallee(uintptr_t Caller, uintptr_t Callee);
template <class T> void HandleCmp(uintptr_t PC, T Arg1, T Arg2);
size_t GetTotalPCCoverage();
void SetUseCounters(bool UC) { UseCounters = UC; }
void SetUseClangCoverage(bool UCC) { UseClangCoverage = UCC; }
void SetUseValueProfile(bool VP) { UseValueProfile = VP; }
void SetPrintNewPCs(bool P) { DoPrintNewPCs = P; }
void SetPrintNewFuncs(size_t P) { NumPrintNewFuncs = P; }
void UpdateObservedPCs();
template <class Callback> void CollectFeatures(Callback CB) const;
void ResetMaps() {
ValueProfileMap.Reset();
memset(Counters(), 0, GetNumPCs());
if (NumModules)
memset(Counters(), 0, GetNumPCs());
ClearExtraCounters();
ClearInlineCounters();
if (UseClangCoverage)
ClearClangCounters();
}
void ClearInlineCounters();
void UpdateFeatureSet(size_t CurrentElementIdx, size_t CurrentElementSize);
void PrintFeatureSet();
@ -80,9 +113,8 @@ class TracePC {
TableOfRecentCompares<uint32_t, 32> TORC4;
TableOfRecentCompares<uint64_t, 32> TORC8;
TableOfRecentCompares<Word, 32> TORCW;
MemMemTable<1024> MMT;
void PrintNewPCs();
void InitializePrintNewPCs();
size_t GetNumPCs() const {
return NumGuards == 0 ? (1 << kTracePcBits) : Min(kNumPCs, NumGuards + 1);
}
@ -91,10 +123,21 @@ class TracePC {
return PCs()[Idx];
}
void RecordInitialStack();
uintptr_t GetMaxStackOffset() const;
template<class CallBack>
void ForEachObservedPC(CallBack CB) {
for (auto PC : ObservedPCs)
CB(PC);
}
private:
bool UseCounters = false;
bool UseValueProfile = false;
bool UseClangCoverage = false;
bool DoPrintNewPCs = false;
size_t NumPrintNewFuncs = 0;
struct Module {
uint32_t *Start, *Stop;
@ -104,35 +147,69 @@ private:
size_t NumModules; // linker-initialized.
size_t NumGuards; // linker-initialized.
struct { uint8_t *Start, *Stop; } ModuleCounters[4096];
size_t NumModulesWithInline8bitCounters; // linker-initialized.
size_t NumInline8bitCounters;
struct PCTableEntry {
uintptr_t PC, PCFlags;
};
struct { const PCTableEntry *Start, *Stop; } ModulePCTable[4096];
size_t NumPCTables;
size_t NumPCsInPCTables;
uint8_t *Counters() const;
uintptr_t *PCs() const;
std::set<uintptr_t> *PrintedPCs;
Set<uintptr_t> ObservedPCs;
Set<uintptr_t> ObservedFuncs;
ValueBitMap ValueProfileMap;
uintptr_t InitialStack;
};
template <class Callback> // void Callback(size_t Idx, uint8_t Value);
template <class Callback>
// void Callback(size_t FirstFeature, size_t Idx, uint8_t Value);
ATTRIBUTE_NO_SANITIZE_ALL
void ForEachNonZeroByte(const uint8_t *Begin, const uint8_t *End,
size_t FirstFeature, Callback Handle8bitCounter) {
typedef uintptr_t LargeType;
const size_t Step = sizeof(LargeType) / sizeof(uint8_t);
assert(!(reinterpret_cast<uintptr_t>(Begin) % 64));
for (auto P = Begin; P < End; P += Step)
const size_t StepMask = Step - 1;
auto P = Begin;
// Iterate by 1 byte until either the alignment boundary or the end.
for (; reinterpret_cast<uintptr_t>(P) & StepMask && P < End; P++)
if (uint8_t V = *P)
Handle8bitCounter(FirstFeature, P - Begin, V);
// Iterate by Step bytes at a time.
for (; P < End; P += Step)
if (LargeType Bundle = *reinterpret_cast<const LargeType *>(P))
for (size_t I = 0; I < Step; I++, Bundle >>= 8)
if (uint8_t V = Bundle & 0xff)
Handle8bitCounter(FirstFeature + P - Begin + I, V);
Handle8bitCounter(FirstFeature, P - Begin + I, V);
// Iterate by 1 byte until the end.
for (; P < End; P++)
if (uint8_t V = *P)
Handle8bitCounter(FirstFeature, P - Begin, V);
}
template <class Callback> // bool Callback(size_t Feature)
ATTRIBUTE_NO_SANITIZE_ALL
__attribute__((noinline))
void TracePC::CollectFeatures(Callback HandleFeature) const {
uint8_t *Counters = this->Counters();
size_t N = GetNumPCs();
auto Handle8bitCounter = [&](size_t Idx, uint8_t Counter) {
// Given a non-zero Counter returns a number in the range [0,7].
template<class T>
unsigned CounterToFeature(T Counter) {
// Returns a feature number by placing Counters into buckets as illustrated
// below.
//
// Counter bucket: [1] [2] [3] [4-7] [8-15] [16-31] [32-127] [128+]
// Feature number: 0 1 2 3 4 5 6 7
//
// This is a heuristic taken from AFL (see
// http://lcamtuf.coredump.cx/afl/technical_details.txt).
//
// This implementation may change in the future so clients should
// not rely on it.
assert(Counter);
unsigned Bit = 0;
/**/ if (Counter >= 128) Bit = 7;
@ -142,17 +219,75 @@ void TracePC::CollectFeatures(Callback HandleFeature) const {
else if (Counter >= 4) Bit = 3;
else if (Counter >= 3) Bit = 2;
else if (Counter >= 2) Bit = 1;
HandleFeature(Idx * 8 + Bit);
return Bit;
}
template <class Callback> // void Callback(size_t Feature)
ATTRIBUTE_NO_SANITIZE_ADDRESS
__attribute__((noinline))
void TracePC::CollectFeatures(Callback HandleFeature) const {
uint8_t *Counters = this->Counters();
size_t N = GetNumPCs();
auto Handle8bitCounter = [&](size_t FirstFeature,
size_t Idx, uint8_t Counter) {
if (UseCounters)
HandleFeature(FirstFeature + Idx * 8 + CounterToFeature(Counter));
else
HandleFeature(FirstFeature + Idx);
};
ForEachNonZeroByte(Counters, Counters + N, 0, Handle8bitCounter);
ForEachNonZeroByte(ExtraCountersBegin(), ExtraCountersEnd(), N * 8,
Handle8bitCounter);
size_t FirstFeature = 0;
if (UseValueProfile)
if (!NumInline8bitCounters) {
ForEachNonZeroByte(Counters, Counters + N, FirstFeature, Handle8bitCounter);
FirstFeature += N * 8;
}
if (NumInline8bitCounters) {
for (size_t i = 0; i < NumModulesWithInline8bitCounters; i++) {
ForEachNonZeroByte(ModuleCounters[i].Start, ModuleCounters[i].Stop,
FirstFeature, Handle8bitCounter);
FirstFeature += 8 * (ModuleCounters[i].Stop - ModuleCounters[i].Start);
}
}
if (size_t NumClangCounters = ClangCountersEnd() - ClangCountersBegin()) {
auto P = ClangCountersBegin();
for (size_t Idx = 0; Idx < NumClangCounters; Idx++)
if (auto Cnt = P[Idx]) {
if (UseCounters)
HandleFeature(FirstFeature + Idx * 8 + CounterToFeature(Cnt));
else
HandleFeature(FirstFeature + Idx);
}
FirstFeature += NumClangCounters;
}
ForEachNonZeroByte(ExtraCountersBegin(), ExtraCountersEnd(), FirstFeature,
Handle8bitCounter);
FirstFeature += (ExtraCountersEnd() - ExtraCountersBegin()) * 8;
if (UseValueProfile) {
ValueProfileMap.ForEach([&](size_t Idx) {
HandleFeature(N * 8 + Idx);
HandleFeature(FirstFeature + Idx);
});
FirstFeature += ValueProfileMap.SizeInBits();
}
// Step function, grows similar to 8 * Log_2(A).
auto StackDepthStepFunction = [](uint32_t A) -> uint32_t {
if (!A) return A;
uint32_t Log2 = Log(A);
if (Log2 < 3) return A;
Log2 -= 3;
return (Log2 + 1) * 8 + ((A >> Log2) & 7);
};
assert(StackDepthStepFunction(1024) == 64);
assert(StackDepthStepFunction(1024 * 4) == 80);
assert(StackDepthStepFunction(1024 * 1024) == 144);
if (auto MaxStackOffset = GetMaxStackOffset())
HandleFeature(FirstFeature + StackDepthStepFunction(MaxStackOffset / 8));
}
extern TracePC TPC;

Просмотреть файл

@ -1,181 +0,0 @@
//===- FuzzerTraceState.cpp - Trace-based fuzzer mutator ------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
// Data tracing.
//===----------------------------------------------------------------------===//
#include "FuzzerDictionary.h"
#include "FuzzerInternal.h"
#include "FuzzerIO.h"
#include "FuzzerMutate.h"
#include "FuzzerTracePC.h"
#include <algorithm>
#include <cstring>
#include <map>
#include <set>
#include <thread>
namespace fuzzer {
// Declared as static globals for faster checks inside the hooks.
static bool RecordingMemmem = false;
int ScopedDoingMyOwnMemOrStr::DoingMyOwnMemOrStr;
class TraceState {
public:
TraceState(MutationDispatcher &MD, const FuzzingOptions &Options,
const Fuzzer *F)
: MD(MD), Options(Options), F(F) {}
void StartTraceRecording() {
if (!Options.UseMemmem)
return;
RecordingMemmem = true;
InterestingWords.clear();
MD.ClearAutoDictionary();
}
void StopTraceRecording() {
if (!RecordingMemmem)
return;
for (auto &W : InterestingWords)
MD.AddWordToAutoDictionary({W});
}
void AddInterestingWord(const uint8_t *Data, size_t Size) {
if (!RecordingMemmem || !F->InFuzzingThread()) return;
if (Size <= 1) return;
Size = std::min(Size, Word::GetMaxSize());
Word W(Data, Size);
InterestingWords.insert(W);
}
private:
// TODO: std::set is too inefficient, need to have a custom DS here.
std::set<Word> InterestingWords;
MutationDispatcher &MD;
const FuzzingOptions Options;
const Fuzzer *F;
};
static TraceState *TS;
void Fuzzer::StartTraceRecording() {
if (!TS) return;
TS->StartTraceRecording();
}
void Fuzzer::StopTraceRecording() {
if (!TS) return;
TS->StopTraceRecording();
}
void Fuzzer::InitializeTraceState() {
if (!Options.UseMemmem) return;
TS = new TraceState(MD, Options, this);
}
static size_t InternalStrnlen(const char *S, size_t MaxLen) {
size_t Len = 0;
for (; Len < MaxLen && S[Len]; Len++) {}
return Len;
}
// Finds min of (strlen(S1), strlen(S2)).
// Needed bacause one of these strings may actually be non-zero terminated.
static size_t InternalStrnlen2(const char *S1, const char *S2) {
size_t Len = 0;
for (; S1[Len] && S2[Len]; Len++) {}
return Len;
}
} // namespace fuzzer
using fuzzer::TS;
extern "C" {
// We may need to avoid defining weak hooks to stay compatible with older clang.
#ifndef LLVM_FUZZER_DEFINES_SANITIZER_WEAK_HOOOKS
# define LLVM_FUZZER_DEFINES_SANITIZER_WEAK_HOOOKS 1
#endif
#if LLVM_FUZZER_DEFINES_SANITIZER_WEAK_HOOOKS
ATTRIBUTE_INTERFACE ATTRIBUTE_NO_SANITIZE_MEMORY
void __sanitizer_weak_hook_memcmp(void *caller_pc, const void *s1,
const void *s2, size_t n, int result) {
if (fuzzer::ScopedDoingMyOwnMemOrStr::DoingMyOwnMemOrStr) return;
if (result == 0) return; // No reason to mutate.
if (n <= 1) return; // Not interesting.
fuzzer::TPC.AddValueForMemcmp(caller_pc, s1, s2, n, /*StopAtZero*/false);
}
ATTRIBUTE_INTERFACE ATTRIBUTE_NO_SANITIZE_MEMORY
void __sanitizer_weak_hook_strncmp(void *caller_pc, const char *s1,
const char *s2, size_t n, int result) {
if (fuzzer::ScopedDoingMyOwnMemOrStr::DoingMyOwnMemOrStr) return;
if (result == 0) return; // No reason to mutate.
size_t Len1 = fuzzer::InternalStrnlen(s1, n);
size_t Len2 = fuzzer::InternalStrnlen(s2, n);
n = std::min(n, Len1);
n = std::min(n, Len2);
if (n <= 1) return; // Not interesting.
fuzzer::TPC.AddValueForMemcmp(caller_pc, s1, s2, n, /*StopAtZero*/true);
}
ATTRIBUTE_INTERFACE ATTRIBUTE_NO_SANITIZE_MEMORY
void __sanitizer_weak_hook_strcmp(void *caller_pc, const char *s1,
const char *s2, int result) {
if (fuzzer::ScopedDoingMyOwnMemOrStr::DoingMyOwnMemOrStr) return;
if (result == 0) return; // No reason to mutate.
size_t N = fuzzer::InternalStrnlen2(s1, s2);
if (N <= 1) return; // Not interesting.
fuzzer::TPC.AddValueForMemcmp(caller_pc, s1, s2, N, /*StopAtZero*/true);
}
ATTRIBUTE_INTERFACE ATTRIBUTE_NO_SANITIZE_MEMORY
void __sanitizer_weak_hook_strncasecmp(void *called_pc, const char *s1,
const char *s2, size_t n, int result) {
if (fuzzer::ScopedDoingMyOwnMemOrStr::DoingMyOwnMemOrStr) return;
return __sanitizer_weak_hook_strncmp(called_pc, s1, s2, n, result);
}
ATTRIBUTE_INTERFACE ATTRIBUTE_NO_SANITIZE_MEMORY
void __sanitizer_weak_hook_strcasecmp(void *called_pc, const char *s1,
const char *s2, int result) {
if (fuzzer::ScopedDoingMyOwnMemOrStr::DoingMyOwnMemOrStr) return;
return __sanitizer_weak_hook_strcmp(called_pc, s1, s2, result);
}
ATTRIBUTE_INTERFACE ATTRIBUTE_NO_SANITIZE_MEMORY
void __sanitizer_weak_hook_strstr(void *called_pc, const char *s1,
const char *s2, char *result) {
if (fuzzer::ScopedDoingMyOwnMemOrStr::DoingMyOwnMemOrStr) return;
TS->AddInterestingWord(reinterpret_cast<const uint8_t *>(s2), strlen(s2));
}
ATTRIBUTE_INTERFACE ATTRIBUTE_NO_SANITIZE_MEMORY
void __sanitizer_weak_hook_strcasestr(void *called_pc, const char *s1,
const char *s2, char *result) {
if (fuzzer::ScopedDoingMyOwnMemOrStr::DoingMyOwnMemOrStr) return;
TS->AddInterestingWord(reinterpret_cast<const uint8_t *>(s2), strlen(s2));
}
ATTRIBUTE_INTERFACE ATTRIBUTE_NO_SANITIZE_MEMORY
void __sanitizer_weak_hook_memmem(void *called_pc, const void *s1, size_t len1,
const void *s2, size_t len2, void *result) {
if (fuzzer::ScopedDoingMyOwnMemOrStr::DoingMyOwnMemOrStr) return;
TS->AddInterestingWord(reinterpret_cast<const uint8_t *>(s2), len2);
}
#endif // LLVM_FUZZER_DEFINES_SANITIZER_WEAK_HOOOKS
} // extern "C"

Просмотреть файл

@ -124,7 +124,7 @@ bool ParseOneDictionaryEntry(const std::string &Str, Unit *U) {
return true;
}
bool ParseDictionaryFile(const std::string &Text, std::vector<Unit> *Units) {
bool ParseDictionaryFile(const std::string &Text, Vector<Unit> *Units) {
if (Text.empty()) {
Printf("ParseDictionaryFile: file does not exist or is empty\n");
return false;
@ -181,7 +181,7 @@ std::string Base64(const Unit &U) {
std::string DescribePC(const char *SymbolizedFMT, uintptr_t PC) {
if (!EF->__sanitizer_symbolize_pc) return "<can not symbolize>";
char PcDescr[1024];
char PcDescr[1024] = {};
EF->__sanitizer_symbolize_pc(reinterpret_cast<void*>(PC),
SymbolizedFMT, PcDescr, sizeof(PcDescr));
PcDescr[sizeof(PcDescr) - 1] = 0; // Just in case.
@ -205,14 +205,11 @@ unsigned NumberOfCpuCores() {
return N;
}
bool ExecuteCommandAndReadOutput(const std::string &Command, std::string *Out) {
FILE *Pipe = OpenProcessPipe(Command.c_str(), "r");
if (!Pipe) return false;
char Buff[1024];
size_t N;
while ((N = fread(Buff, 1, sizeof(Buff), Pipe)) > 0)
Out->append(Buff, N);
return true;
size_t SimpleFastHash(const uint8_t *Data, size_t Size) {
size_t Res = 0;
for (size_t i = 0; i < Size; i++)
Res = Res * 11 + Data[i];
return Res;
}
} // namespace fuzzer

Просмотреть файл

@ -13,6 +13,7 @@
#define LLVM_FUZZER_UTIL_H
#include "FuzzerDefs.h"
#include "FuzzerCommand.h"
namespace fuzzer {
@ -41,8 +42,6 @@ std::string DescribePC(const char *SymbolizedFMT, uintptr_t PC);
unsigned NumberOfCpuCores();
bool ExecuteCommandAndReadOutput(const std::string &Command, std::string *Out);
// Platform specific functions.
void SetSignalHandler(const FuzzingOptions& Options);
@ -52,25 +51,37 @@ unsigned long GetPid();
size_t GetPeakRSSMb();
int ExecuteCommand(const std::string &Command);
int ExecuteCommand(const Command &Cmd);
FILE *OpenProcessPipe(const char *Command, const char *Mode);
const void *SearchMemory(const void *haystack, size_t haystacklen,
const void *needle, size_t needlelen);
std::string CloneArgsWithoutX(const std::vector<std::string> &Args,
std::string CloneArgsWithoutX(const Vector<std::string> &Args,
const char *X1, const char *X2);
inline std::string CloneArgsWithoutX(const std::vector<std::string> &Args,
inline std::string CloneArgsWithoutX(const Vector<std::string> &Args,
const char *X) {
return CloneArgsWithoutX(Args, X, X);
}
inline std::pair<std::string, std::string> SplitBefore(std::string X,
std::string S) {
auto Pos = S.find(X);
if (Pos == std::string::npos)
return std::make_pair(S, "");
return std::make_pair(S.substr(0, Pos), S.substr(Pos));
}
std::string DisassembleCmd(const std::string &FileName);
std::string SearchRegexCmd(const std::string &Regex);
size_t SimpleFastHash(const uint8_t *Data, size_t Size);
inline uint32_t Log(uint32_t X) { return 32 - __builtin_clz(X) - 1; }
} // namespace fuzzer
#endif // LLVM_FUZZER_UTIL_H

Просмотреть файл

@ -10,11 +10,13 @@
//===----------------------------------------------------------------------===//
#include "FuzzerDefs.h"
#if LIBFUZZER_APPLE
#include "FuzzerCommand.h"
#include "FuzzerIO.h"
#include <mutex>
#include <signal.h>
#include <spawn.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
// There is no header for this on macOS so declare here
@ -36,7 +38,8 @@ static sigset_t OldBlockedSignalsSet;
// signal handlers when the first thread enters and restores them when the last
// thread finishes execution of the function and ensures this is not racey by
// using a mutex.
int ExecuteCommand(const std::string &Command) {
int ExecuteCommand(const Command &Cmd) {
std::string CmdLine = Cmd.toString();
posix_spawnattr_t SpawnAttributes;
if (posix_spawnattr_init(&SpawnAttributes))
return -1;
@ -96,12 +99,17 @@ int ExecuteCommand(const std::string &Command) {
pid_t Pid;
char **Environ = environ; // Read from global
const char *CommandCStr = Command.c_str();
const char *Argv[] = {"sh", "-c", CommandCStr, NULL};
const char *CommandCStr = CmdLine.c_str();
char *const Argv[] = {
strdup("sh"),
strdup("-c"),
strdup(CommandCStr),
NULL
};
int ErrorCode = 0, ProcessStatus = 0;
// FIXME: We probably shouldn't hardcode the shell path.
ErrorCode = posix_spawn(&Pid, "/bin/sh", NULL, &SpawnAttributes,
(char *const *)Argv, Environ);
Argv, Environ);
(void)posix_spawnattr_destroy(&SpawnAttributes);
if (!ErrorCode) {
pid_t SavedPid = Pid;
@ -120,6 +128,8 @@ int ExecuteCommand(const std::string &Command) {
// Shell execution failure.
ProcessStatus = W_EXITCODE(127, 0);
}
for (unsigned i = 0, n = sizeof(Argv) / sizeof(Argv[0]); i < n; ++i)
free(Argv[i]);
// Restore the signal handlers of the current process when the last thread
// using this function finishes.

Просмотреть файл

@ -0,0 +1,240 @@
//===- FuzzerUtilFuchsia.cpp - Misc utils for Fuchsia. --------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
// Misc utils implementation using Fuchsia/Zircon APIs.
//===----------------------------------------------------------------------===//
#include "FuzzerDefs.h"
#if LIBFUZZER_FUCHSIA
#include "FuzzerInternal.h"
#include "FuzzerUtil.h"
#include <cerrno>
#include <cinttypes>
#include <cstdint>
#include <fcntl.h>
#include <launchpad/launchpad.h>
#include <string>
#include <thread>
#include <unistd.h>
#include <zircon/errors.h>
#include <zircon/process.h>
#include <zircon/status.h>
#include <zircon/syscalls.h>
#include <zircon/syscalls/port.h>
#include <zircon/types.h>
namespace fuzzer {
namespace {
// A magic value for the Zircon exception port, chosen to spell 'FUZZING'
// when interpreted as a byte sequence on little-endian platforms.
const uint64_t kFuzzingCrash = 0x474e495a5a5546;
void AlarmHandler(int Seconds) {
while (true) {
SleepSeconds(Seconds);
Fuzzer::StaticAlarmCallback();
}
}
void InterruptHandler() {
// Ctrl-C sends ETX in Zircon.
while (getchar() != 0x03);
Fuzzer::StaticInterruptCallback();
}
void CrashHandler(zx_handle_t *Port) {
std::unique_ptr<zx_handle_t> ExceptionPort(Port);
zx_port_packet_t Packet;
_zx_port_wait(*ExceptionPort, ZX_TIME_INFINITE, &Packet, 1);
// Unbind as soon as possible so we don't receive exceptions from this thread.
if (_zx_task_bind_exception_port(ZX_HANDLE_INVALID, ZX_HANDLE_INVALID,
kFuzzingCrash, 0) != ZX_OK) {
// Shouldn't happen; if it does the safest option is to just exit.
Printf("libFuzzer: unable to unbind exception port; aborting!\n");
exit(1);
}
if (Packet.key != kFuzzingCrash) {
Printf("libFuzzer: invalid crash key: %" PRIx64 "; aborting!\n",
Packet.key);
exit(1);
}
// CrashCallback should not return from this call
Fuzzer::StaticCrashSignalCallback();
}
} // namespace
// Platform specific functions.
void SetSignalHandler(const FuzzingOptions &Options) {
zx_status_t rc;
// Set up alarm handler if needed.
if (Options.UnitTimeoutSec > 0) {
std::thread T(AlarmHandler, Options.UnitTimeoutSec / 2 + 1);
T.detach();
}
// Set up interrupt handler if needed.
if (Options.HandleInt || Options.HandleTerm) {
std::thread T(InterruptHandler);
T.detach();
}
// Early exit if no crash handler needed.
if (!Options.HandleSegv && !Options.HandleBus && !Options.HandleIll &&
!Options.HandleFpe && !Options.HandleAbrt)
return;
// Create an exception port
zx_handle_t *ExceptionPort = new zx_handle_t;
if ((rc = _zx_port_create(0, ExceptionPort)) != ZX_OK) {
Printf("libFuzzer: zx_port_create failed: %s\n", _zx_status_get_string(rc));
exit(1);
}
// Bind the port to receive exceptions from our process
if ((rc = _zx_task_bind_exception_port(_zx_process_self(), *ExceptionPort,
kFuzzingCrash, 0)) != ZX_OK) {
Printf("libFuzzer: unable to bind exception port: %s\n",
zx_status_get_string(rc));
exit(1);
}
// Set up the crash handler.
std::thread T(CrashHandler, ExceptionPort);
T.detach();
}
void SleepSeconds(int Seconds) {
_zx_nanosleep(_zx_deadline_after(ZX_SEC(Seconds)));
}
unsigned long GetPid() {
zx_status_t rc;
zx_info_handle_basic_t Info;
if ((rc = zx_object_get_info(_zx_process_self(), ZX_INFO_HANDLE_BASIC, &Info,
sizeof(Info), NULL, NULL)) != ZX_OK) {
Printf("libFuzzer: unable to get info about self: %s\n",
zx_status_get_string(rc));
exit(1);
}
return Info.koid;
}
size_t GetPeakRSSMb() {
zx_status_t rc;
zx_info_task_stats_t Info;
if ((rc = _zx_object_get_info(_zx_process_self(), ZX_INFO_TASK_STATS, &Info,
sizeof(Info), NULL, NULL)) != ZX_OK) {
Printf("libFuzzer: unable to get info about self: %s\n",
_zx_status_get_string(rc));
exit(1);
}
return (Info.mem_private_bytes + Info.mem_shared_bytes) >> 20;
}
template <typename Fn>
class RunOnDestruction {
public:
explicit RunOnDestruction(Fn fn) : fn_(fn) {}
~RunOnDestruction() { fn_(); }
private:
Fn fn_;
};
template <typename Fn>
RunOnDestruction<Fn> at_scope_exit(Fn fn) {
return RunOnDestruction<Fn>(fn);
}
int ExecuteCommand(const Command &Cmd) {
zx_status_t rc;
// Convert arguments to C array
auto Args = Cmd.getArguments();
size_t Argc = Args.size();
assert(Argc != 0);
std::unique_ptr<const char *[]> Argv(new const char *[Argc]);
for (size_t i = 0; i < Argc; ++i)
Argv[i] = Args[i].c_str();
// Create the basic launchpad. Clone everything except stdio.
launchpad_t *lp;
launchpad_create(ZX_HANDLE_INVALID, Argv[0], &lp);
launchpad_load_from_file(lp, Argv[0]);
launchpad_set_args(lp, Argc, Argv.get());
launchpad_clone(lp, LP_CLONE_ALL & (~LP_CLONE_FDIO_STDIO));
// Determine stdout
int FdOut = STDOUT_FILENO;
if (Cmd.hasOutputFile()) {
auto Filename = Cmd.getOutputFile();
FdOut = open(Filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0);
if (FdOut == -1) {
Printf("libFuzzer: failed to open %s: %s\n", Filename.c_str(),
strerror(errno));
return ZX_ERR_IO;
}
}
auto CloseFdOut = at_scope_exit([&]() { close(FdOut); } );
// Determine stderr
int FdErr = STDERR_FILENO;
if (Cmd.isOutAndErrCombined())
FdErr = FdOut;
// Clone the file descriptors into the new process
if ((rc = launchpad_clone_fd(lp, STDIN_FILENO, STDIN_FILENO)) != ZX_OK ||
(rc = launchpad_clone_fd(lp, FdOut, STDOUT_FILENO)) != ZX_OK ||
(rc = launchpad_clone_fd(lp, FdErr, STDERR_FILENO)) != ZX_OK) {
Printf("libFuzzer: failed to clone FDIO: %s\n", _zx_status_get_string(rc));
return rc;
}
// Start the process
zx_handle_t ProcessHandle = ZX_HANDLE_INVALID;
const char *ErrorMsg = nullptr;
if ((rc = launchpad_go(lp, &ProcessHandle, &ErrorMsg)) != ZX_OK) {
Printf("libFuzzer: failed to launch '%s': %s, %s\n", Argv[0], ErrorMsg,
_zx_status_get_string(rc));
return rc;
}
auto CloseHandle = at_scope_exit([&]() { _zx_handle_close(ProcessHandle); });
// Now join the process and return the exit status.
if ((rc = _zx_object_wait_one(ProcessHandle, ZX_PROCESS_TERMINATED,
ZX_TIME_INFINITE, nullptr)) != ZX_OK) {
Printf("libFuzzer: failed to join '%s': %s\n", Argv[0],
_zx_status_get_string(rc));
return rc;
}
zx_info_process_t Info;
if ((rc = _zx_object_get_info(ProcessHandle, ZX_INFO_PROCESS, &Info,
sizeof(Info), nullptr, nullptr)) != ZX_OK) {
Printf("libFuzzer: unable to get return code from '%s': %s\n", Argv[0],
zx_status_get_string(rc));
return rc;
}
return Info.return_code;
}
const void *SearchMemory(const void *Data, size_t DataLen, const void *Patt,
size_t PattLen) {
return memmem(Data, DataLen, Patt, PattLen);
}
} // namespace fuzzer
#endif // LIBFUZZER_FUCHSIA

Просмотреть файл

@ -9,16 +9,18 @@
// Misc utils for Linux.
//===----------------------------------------------------------------------===//
#include "FuzzerDefs.h"
#if LIBFUZZER_LINUX
#if LIBFUZZER_LINUX || LIBFUZZER_NETBSD || LIBFUZZER_FREEBSD
#include "FuzzerCommand.h"
#include <stdlib.h>
namespace fuzzer {
int ExecuteCommand(const std::string &Command) {
return system(Command.c_str());
int ExecuteCommand(const Command &Cmd) {
std::string CmdLine = Cmd.toString();
return system(CmdLine.c_str());
}
} // namespace fuzzer
#endif // LIBFUZZER_LINUX
#endif // LIBFUZZER_LINUX || LIBFUZZER_NETBSD || LIBFUZZER_FREEBSD

Просмотреть файл

@ -18,7 +18,6 @@
#include <errno.h>
#include <iomanip>
#include <signal.h>
#include <sstream>
#include <stdio.h>
#include <sys/resource.h>
#include <sys/syscall.h>
@ -41,14 +40,31 @@ static void InterruptHandler(int, siginfo_t *, void *) {
Fuzzer::StaticInterruptCallback();
}
static void GracefulExitHandler(int, siginfo_t *, void *) {
Fuzzer::StaticGracefulExitCallback();
}
static void FileSizeExceedHandler(int, siginfo_t *, void *) {
Fuzzer::StaticFileSizeExceedCallback();
}
static void SetSigaction(int signum,
void (*callback)(int, siginfo_t *, void *)) {
struct sigaction sigact;
memset(&sigact, 0, sizeof(sigact));
struct sigaction sigact = {};
if (sigaction(signum, nullptr, &sigact)) {
Printf("libFuzzer: sigaction failed with %d\n", errno);
exit(1);
}
if (sigact.sa_flags & SA_SIGINFO) {
if (sigact.sa_sigaction)
return;
} else {
if (sigact.sa_handler != SIG_DFL && sigact.sa_handler != SIG_IGN &&
sigact.sa_handler != SIG_ERR)
return;
}
sigact = {};
sigact.sa_sigaction = callback;
if (sigaction(signum, &sigact, 0)) {
Printf("libFuzzer: sigaction failed with %d\n", errno);
@ -86,6 +102,10 @@ void SetSignalHandler(const FuzzingOptions& Options) {
SetSigaction(SIGFPE, CrashHandler);
if (Options.HandleXfsz)
SetSigaction(SIGXFSZ, FileSizeExceedHandler);
if (Options.HandleUsr1)
SetSigaction(SIGUSR1, GracefulExitHandler);
if (Options.HandleUsr2)
SetSigaction(SIGUSR2, GracefulExitHandler);
}
void SleepSeconds(int Seconds) {
@ -98,7 +118,7 @@ size_t GetPeakRSSMb() {
struct rusage usage;
if (getrusage(RUSAGE_SELF, &usage))
return 0;
if (LIBFUZZER_LINUX) {
if (LIBFUZZER_LINUX || LIBFUZZER_FREEBSD || LIBFUZZER_NETBSD) {
// ru_maxrss is in KiB
return usage.ru_maxrss >> 10;
} else if (LIBFUZZER_APPLE) {

Просмотреть файл

@ -10,6 +10,7 @@
//===----------------------------------------------------------------------===//
#include "FuzzerDefs.h"
#if LIBFUZZER_WINDOWS
#include "FuzzerCommand.h"
#include "FuzzerIO.h"
#include "FuzzerInternal.h"
#include <cassert>
@ -18,11 +19,12 @@
#include <errno.h>
#include <iomanip>
#include <signal.h>
#include <sstream>
#include <stdio.h>
#include <sys/types.h>
#include <windows.h>
#include <psapi.h>
// This must be included after windows.h.
#include <Psapi.h>
namespace fuzzer {
@ -150,8 +152,9 @@ FILE *OpenProcessPipe(const char *Command, const char *Mode) {
return _popen(Command, Mode);
}
int ExecuteCommand(const std::string &Command) {
return system(Command.c_str());
int ExecuteCommand(const Command &Cmd) {
std::string CmdLine = Cmd.toString();
return system(CmdLine.c_str());
}
const void *SearchMemory(const void *Data, size_t DataLen, const void *Patt,

Просмотреть файл

@ -52,27 +52,7 @@ struct ValueBitMap {
return Map[WordIdx] & (1UL << BitIdx);
}
size_t GetNumBitsSinceLastMerge() const { return NumBits; }
// Merges 'Other' into 'this', clears 'Other', updates NumBits,
// returns true if new bits were added.
ATTRIBUTE_TARGET_POPCNT
bool MergeFrom(ValueBitMap &Other) {
uintptr_t Res = 0;
size_t OldNumBits = NumBits;
for (size_t i = 0; i < kMapSizeInWords; i++) {
auto O = Other.Map[i];
auto M = Map[i];
if (O) {
Map[i] = (M |= O);
Other.Map[i] = 0;
}
if (M)
Res += __builtin_popcountll(M);
}
NumBits = Res;
return OldNumBits < NumBits;
}
size_t SizeInBits() const { return kMapSizeInBits; }
template <class Callback>
ATTRIBUTE_NO_SANITIZE_ALL
@ -85,7 +65,6 @@ struct ValueBitMap {
}
private:
size_t NumBits = 0;
uintptr_t Map[kMapSizeInWords] __attribute__((aligned(512)));
};

Просмотреть файл

@ -11,6 +11,7 @@ EXPORTS += [
]
SOURCES += [
'FuzzerClangCounters.cpp',
'FuzzerCrossOver.cpp',
'FuzzerDriver.cpp',
'FuzzerExtFunctionsDlsym.cpp',
@ -28,7 +29,6 @@ SOURCES += [
'FuzzerShmemPosix.cpp',
'FuzzerShmemWindows.cpp',
'FuzzerTracePC.cpp',
'FuzzerTraceState.cpp',
'FuzzerUtil.cpp',
'FuzzerUtilDarwin.cpp',
'FuzzerUtilLinux.cpp',