зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
94ed3ca79d
Коммит
4259227b9c
|
@ -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',
|
||||
|
|
Загрузка…
Ссылка в новой задаче