added libFuzzer

This commit is contained in:
Niels Lohmann 2016-12-22 09:08:50 +01:00
parent 7107072f71
commit 69ed19e483
156 changed files with 9535 additions and 0 deletions

1
.gitignore vendored
View file

@ -20,3 +20,4 @@ cmake-build-debug
test/test-*
test/fuzz_test/Fuzzer/.svn

View file

@ -0,0 +1,45 @@
set(LIBFUZZER_FLAGS_BASE "${CMAKE_CXX_FLAGS}")
# Disable the coverage and sanitizer instrumentation for the fuzzer itself.
set(CMAKE_CXX_FLAGS "${LIBFUZZER_FLAGS_BASE} -fno-sanitize-coverage=trace-pc-guard,edge,trace-cmp,indirect-calls,8bit-counters -Werror")
if( LLVM_USE_SANITIZE_COVERAGE )
if(NOT "${LLVM_USE_SANITIZER}" STREQUAL "Address")
message(FATAL_ERROR
"LibFuzzer and its tests require LLVM_USE_SANITIZER=Address and "
"LLVM_USE_SANITIZE_COVERAGE=YES to be set."
)
endif()
add_library(LLVMFuzzerNoMainObjects OBJECT
FuzzerCrossOver.cpp
FuzzerDriver.cpp
FuzzerExtFunctionsDlsym.cpp
FuzzerExtFunctionsWeak.cpp
FuzzerExtFunctionsWeakAlias.cpp
FuzzerIO.cpp
FuzzerIOPosix.cpp
FuzzerIOWindows.cpp
FuzzerLoop.cpp
FuzzerMerge.cpp
FuzzerMutate.cpp
FuzzerSHA1.cpp
FuzzerTracePC.cpp
FuzzerTraceState.cpp
FuzzerUtil.cpp
FuzzerUtilDarwin.cpp
FuzzerUtilLinux.cpp
FuzzerUtilPosix.cpp
FuzzerUtilWindows.cpp
)
add_library(LLVMFuzzerNoMain STATIC
$<TARGET_OBJECTS:LLVMFuzzerNoMainObjects>
)
target_link_libraries(LLVMFuzzerNoMain ${PTHREAD_LIB})
add_library(LLVMFuzzer STATIC
FuzzerMain.cpp
$<TARGET_OBJECTS:LLVMFuzzerNoMainObjects>
)
target_link_libraries(LLVMFuzzer ${PTHREAD_LIB})
if( LLVM_INCLUDE_TESTS )
add_subdirectory(test)
endif()
endif()

View file

@ -0,0 +1,217 @@
//===- FuzzerCorpus.h - Internal header for the Fuzzer ----------*- C++ -* ===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
// fuzzer::InputCorpus
//===----------------------------------------------------------------------===//
#ifndef LLVM_FUZZER_CORPUS
#define LLVM_FUZZER_CORPUS
#include "FuzzerDefs.h"
#include "FuzzerIO.h"
#include "FuzzerRandom.h"
#include "FuzzerSHA1.h"
#include "FuzzerTracePC.h"
#include <numeric>
#include <random>
#include <unordered_set>
namespace fuzzer {
struct InputInfo {
Unit U; // The actual input data.
uint8_t Sha1[kSHA1NumBytes]; // Checksum.
// Number of features that this input has and no smaller input has.
size_t NumFeatures = 0;
size_t Tmp = 0; // Used by ValidateFeatureSet.
// Stats.
size_t NumExecutedMutations = 0;
size_t NumSuccessfullMutations = 0;
bool MayDeleteFile = false;
};
class InputCorpus {
public:
static const size_t kFeatureSetSize = 1 << 16;
InputCorpus(const std::string &OutputCorpus) : OutputCorpus(OutputCorpus) {
memset(InputSizesPerFeature, 0, sizeof(InputSizesPerFeature));
memset(SmallestElementPerFeature, 0, sizeof(SmallestElementPerFeature));
}
~InputCorpus() {
for (auto II : Inputs)
delete II;
}
size_t size() const { return Inputs.size(); }
size_t SizeInBytes() const {
size_t Res = 0;
for (auto II : Inputs)
Res += II->U.size();
return Res;
}
size_t NumActiveUnits() const {
size_t Res = 0;
for (auto II : Inputs)
Res += !II->U.empty();
return Res;
}
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) {
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);
UpdateCorpusDistribution();
ValidateFeatureSet();
}
bool HasUnit(const Unit &U) { return Hashes.count(Hash(U)); }
bool HasUnit(const std::string &H) { return Hashes.count(H); }
InputInfo &ChooseUnitToMutate(Random &Rand) {
InputInfo &II = *Inputs[ChooseUnitIdxToMutate(Rand)];
assert(!II.U.empty());
return II;
};
// 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.Get_mt19937()));
assert(Idx < Inputs.size());
return Idx;
}
void PrintStats() {
for (size_t i = 0; i < Inputs.size(); i++) {
const auto &II = *Inputs[i];
Printf(" [%zd %s]\tsz: %zd\truns: %zd\tsucc: %zd\n", i,
Sha1ToString(II.Sha1).c_str(), II.U.size(),
II.NumExecutedMutations, II.NumSuccessfullMutations);
}
}
void PrintFeatureSet() {
for (size_t i = 0; i < kFeatureSetSize; i++) {
if(size_t Sz = GetFeature(i))
Printf("[%zd: id %zd sz%zd] ", i, SmallestElementPerFeature[i], Sz);
}
Printf("\n\t");
for (size_t i = 0; i < Inputs.size(); i++)
if (size_t N = Inputs[i]->NumFeatures)
Printf(" %zd=>%zd ", i, N);
Printf("\n");
}
void DeleteInput(size_t Idx) {
InputInfo &II = *Inputs[Idx];
if (!OutputCorpus.empty() && II.MayDeleteFile)
RemoveFile(DirPlusFile(OutputCorpus, Sha1ToString(II.Sha1)));
Unit().swap(II.U);
if (FeatureDebug)
Printf("EVICTED %zd\n", Idx);
}
bool AddFeature(size_t Idx, uint32_t NewSize, bool Shrink) {
assert(NewSize);
Idx = Idx % kFeatureSetSize;
uint32_t OldSize = GetFeature(Idx);
if (OldSize == 0 || (Shrink && OldSize > NewSize)) {
if (OldSize > 0) {
size_t OldIdx = SmallestElementPerFeature[Idx];
InputInfo &II = *Inputs[OldIdx];
assert(II.NumFeatures > 0);
II.NumFeatures--;
if (II.NumFeatures == 0)
DeleteInput(OldIdx);
}
if (FeatureDebug)
Printf("ADD FEATURE %zd sz %d\n", Idx, NewSize);
SmallestElementPerFeature[Idx] = Inputs.size();
InputSizesPerFeature[Idx] = NewSize;
CountingFeatures = true;
return true;
}
return false;
}
size_t NumFeatures() const {
size_t Res = 0;
for (size_t i = 0; i < kFeatureSetSize; i++)
Res += GetFeature(i) != 0;
return Res;
}
void ResetFeatureSet() {
assert(Inputs.empty());
memset(InputSizesPerFeature, 0, sizeof(InputSizesPerFeature));
memset(SmallestElementPerFeature, 0, sizeof(SmallestElementPerFeature));
}
private:
static const bool FeatureDebug = false;
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++)
if (GetFeature(Idx))
Inputs[SmallestElementPerFeature[Idx]]->Tmp++;
for (auto II: Inputs) {
if (II->Tmp != II->NumFeatures)
Printf("ZZZ %zd %zd\n", II->Tmp, II->NumFeatures);
assert(II->Tmp == II->NumFeatures);
II->Tmp = 0;
}
}
// Updates the probability distribution for the units in the corpus.
// Must be called whenever the corpus or unit weights are changed.
void UpdateCorpusDistribution() {
size_t N = Inputs.size();
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);
else
std::iota(Weights.begin(), Weights.end(), 1);
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;
std::unordered_set<std::string> Hashes;
std::vector<InputInfo*> Inputs;
bool CountingFeatures = false;
uint32_t InputSizesPerFeature[kFeatureSetSize];
uint32_t SmallestElementPerFeature[kFeatureSetSize];
std::string OutputCorpus;
};
} // namespace fuzzer
#endif // LLVM_FUZZER_CORPUS

View file

@ -0,0 +1,52 @@
//===- FuzzerCrossOver.cpp - Cross over two test inputs -------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
// Cross over test inputs.
//===----------------------------------------------------------------------===//
#include "FuzzerDefs.h"
#include "FuzzerMutate.h"
#include "FuzzerRandom.h"
#include <cstring>
namespace fuzzer {
// Cross Data1 and Data2, store the result (up to MaxOutSize bytes) in Out.
size_t MutationDispatcher::CrossOver(const uint8_t *Data1, size_t Size1,
const uint8_t *Data2, size_t Size2,
uint8_t *Out, size_t MaxOutSize) {
assert(Size1 || Size2);
MaxOutSize = Rand(MaxOutSize) + 1;
size_t OutPos = 0;
size_t Pos1 = 0;
size_t Pos2 = 0;
size_t *InPos = &Pos1;
size_t InSize = Size1;
const uint8_t *Data = Data1;
bool CurrentlyUsingFirstData = true;
while (OutPos < MaxOutSize && (Pos1 < Size1 || Pos2 < Size2)) {
// Merge a part of Data into Out.
size_t OutSizeLeft = MaxOutSize - OutPos;
if (*InPos < InSize) {
size_t InSizeLeft = InSize - *InPos;
size_t MaxExtraSize = std::min(OutSizeLeft, InSizeLeft);
size_t ExtraSize = Rand(MaxExtraSize) + 1;
memcpy(Out + OutPos, Data + *InPos, ExtraSize);
OutPos += ExtraSize;
(*InPos) += ExtraSize;
}
// Use the other input data on the next iteration.
InPos = CurrentlyUsingFirstData ? &Pos2 : &Pos1;
InSize = CurrentlyUsingFirstData ? Size2 : Size1;
Data = CurrentlyUsingFirstData ? Data2 : Data1;
CurrentlyUsingFirstData = !CurrentlyUsingFirstData;
}
return OutPos;
}
} // namespace fuzzer

View file

@ -0,0 +1,89 @@
//===- FuzzerDefs.h - Internal header for the Fuzzer ------------*- C++ -* ===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
// Basic definitions.
//===----------------------------------------------------------------------===//
#ifndef LLVM_FUZZER_DEFS_H
#define LLVM_FUZZER_DEFS_H
#include <cassert>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <string>
#include <vector>
// Platform detection.
#ifdef __linux__
#define LIBFUZZER_APPLE 0
#define LIBFUZZER_LINUX 1
#define LIBFUZZER_WINDOWS 0
#elif __APPLE__
#define LIBFUZZER_APPLE 1
#define LIBFUZZER_LINUX 0
#define LIBFUZZER_WINDOWS 0
#elif _WIN32
#define LIBFUZZER_APPLE 0
#define LIBFUZZER_LINUX 0
#define LIBFUZZER_WINDOWS 1
#else
#error "Support for your platform has not been implemented"
#endif
#define LIBFUZZER_POSIX LIBFUZZER_APPLE || LIBFUZZER_LINUX
#ifdef __x86_64
#define ATTRIBUTE_TARGET_POPCNT __attribute__((target("popcnt")))
#else
#define ATTRIBUTE_TARGET_POPCNT
#endif
#ifdef __clang__ // avoid gcc warning.
# define ATTRIBUTE_NO_SANITIZE_MEMORY __attribute__((no_sanitize("memory")))
#else
# define ATTRIBUTE_NO_SANITIZE_MEMORY
#endif
namespace fuzzer {
template <class T> T Min(T a, T b) { return a < b ? a : b; }
template <class T> T Max(T a, T b) { return a > b ? a : b; }
class Random;
class Dictionary;
class DictionaryEntry;
class MutationDispatcher;
struct FuzzingOptions;
class InputCorpus;
struct InputInfo;
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;
typedef int (*UserCallback)(const uint8_t *Data, size_t Size);
int FuzzerDriver(int *argc, char ***argv, UserCallback Callback);
struct ScopedDoingMyOwnMemmem {
ScopedDoingMyOwnMemmem();
~ScopedDoingMyOwnMemmem();
};
inline uint8_t Bswap(uint8_t x) { return x; }
inline uint16_t Bswap(uint16_t x) { return __builtin_bswap16(x); }
inline uint32_t Bswap(uint32_t x) { return __builtin_bswap32(x); }
inline uint64_t Bswap(uint64_t x) { return __builtin_bswap64(x); }
} // namespace fuzzer
#endif // LLVM_FUZZER_DEFS_H

View file

@ -0,0 +1,124 @@
//===- FuzzerDictionary.h - Internal header for the Fuzzer ------*- C++ -* ===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
// fuzzer::Dictionary
//===----------------------------------------------------------------------===//
#ifndef LLVM_FUZZER_DICTIONARY_H
#define LLVM_FUZZER_DICTIONARY_H
#include "FuzzerDefs.h"
#include "FuzzerIO.h"
#include "FuzzerUtil.h"
#include <algorithm>
#include <limits>
namespace fuzzer {
// A simple POD sized array of bytes.
template <size_t kMaxSize> class FixedWord {
public:
FixedWord() {}
FixedWord(const uint8_t *B, uint8_t S) { Set(B, S); }
void Set(const uint8_t *B, uint8_t S) {
assert(S <= kMaxSize);
memcpy(Data, B, S);
Size = S;
}
bool operator==(const FixedWord<kMaxSize> &w) const {
return Size == w.Size && 0 == memcmp(Data, w.Data, Size);
}
bool operator<(const FixedWord<kMaxSize> &w) const {
if (Size != w.Size)
return Size < w.Size;
return memcmp(Data, w.Data, Size) < 0;
}
static size_t GetMaxSize() { return kMaxSize; }
const uint8_t *data() const { return Data; }
uint8_t size() const { return Size; }
private:
uint8_t Size = 0;
uint8_t Data[kMaxSize];
};
typedef FixedWord<27> Word; // 28 bytes.
class DictionaryEntry {
public:
DictionaryEntry() {}
DictionaryEntry(Word W) : W(W) {}
DictionaryEntry(Word W, size_t PositionHint) : W(W), PositionHint(PositionHint) {}
const Word &GetW() const { return W; }
bool HasPositionHint() const { return PositionHint != std::numeric_limits<size_t>::max(); }
size_t GetPositionHint() const {
assert(HasPositionHint());
return PositionHint;
}
void IncUseCount() { UseCount++; }
void IncSuccessCount() { SuccessCount++; }
size_t GetUseCount() const { return UseCount; }
size_t GetSuccessCount() const {return SuccessCount; }
void Print(const char *PrintAfter = "\n") {
PrintASCII(W.data(), W.size());
if (HasPositionHint())
Printf("@%zd", GetPositionHint());
Printf("%s", PrintAfter);
}
private:
Word W;
size_t PositionHint = std::numeric_limits<size_t>::max();
size_t UseCount = 0;
size_t SuccessCount = 0;
};
class Dictionary {
public:
static const size_t kMaxDictSize = 1 << 14;
bool ContainsWord(const Word &W) const {
return std::any_of(begin(), end(), [&](const DictionaryEntry &DE) {
return DE.GetW() == W;
});
}
const DictionaryEntry *begin() const { return &DE[0]; }
const DictionaryEntry *end() const { return begin() + Size; }
DictionaryEntry & operator[] (size_t Idx) {
assert(Idx < Size);
return DE[Idx];
}
void push_back(DictionaryEntry DE) {
if (Size < kMaxDictSize)
this->DE[Size++] = DE;
}
void clear() { Size = 0; }
bool empty() const { return Size == 0; }
size_t size() const { return Size; }
private:
DictionaryEntry DE[kMaxDictSize];
size_t Size = 0;
};
// Parses one dictionary entry.
// If successfull, 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);
} // namespace fuzzer
#endif // LLVM_FUZZER_DICTIONARY_H

View file

@ -0,0 +1,545 @@
//===- FuzzerDriver.cpp - FuzzerDriver function and flags -----------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
// FuzzerDriver and flag parsing.
//===----------------------------------------------------------------------===//
#include "FuzzerCorpus.h"
#include "FuzzerInterface.h"
#include "FuzzerInternal.h"
#include "FuzzerIO.h"
#include "FuzzerMutate.h"
#include "FuzzerRandom.h"
#include "FuzzerTracePC.h"
#include <algorithm>
#include <atomic>
#include <chrono>
#include <cstring>
#include <mutex>
#include <string>
#include <thread>
// This function should be present in the libFuzzer so that the client
// binary can test for its existence.
extern "C" __attribute__((used)) void __libfuzzer_is_present() {}
namespace fuzzer {
// Program arguments.
struct FlagDescription {
const char *Name;
const char *Description;
int Default;
int *IntFlag;
const char **StrFlag;
unsigned int *UIntFlag;
};
struct {
#define FUZZER_DEPRECATED_FLAG(Name)
#define FUZZER_FLAG_INT(Name, Default, Description) int Name;
#define FUZZER_FLAG_UNSIGNED(Name, Default, Description) unsigned int Name;
#define FUZZER_FLAG_STRING(Name, Description) const char *Name;
#include "FuzzerFlags.def"
#undef FUZZER_DEPRECATED_FLAG
#undef FUZZER_FLAG_INT
#undef FUZZER_FLAG_UNSIGNED
#undef FUZZER_FLAG_STRING
} Flags;
static const FlagDescription FlagDescriptions [] {
#define FUZZER_DEPRECATED_FLAG(Name) \
{#Name, "Deprecated; don't use", 0, nullptr, nullptr, nullptr},
#define FUZZER_FLAG_INT(Name, Default, Description) \
{#Name, Description, Default, &Flags.Name, nullptr, nullptr},
#define FUZZER_FLAG_UNSIGNED(Name, Default, Description) \
{#Name, Description, static_cast<int>(Default), \
nullptr, nullptr, &Flags.Name},
#define FUZZER_FLAG_STRING(Name, Description) \
{#Name, Description, 0, nullptr, &Flags.Name, nullptr},
#include "FuzzerFlags.def"
#undef FUZZER_DEPRECATED_FLAG
#undef FUZZER_FLAG_INT
#undef FUZZER_FLAG_UNSIGNED
#undef FUZZER_FLAG_STRING
};
static const size_t kNumFlags =
sizeof(FlagDescriptions) / sizeof(FlagDescriptions[0]);
static std::vector<std::string> *Inputs;
static std::string *ProgName;
static void PrintHelp() {
Printf("Usage:\n");
auto Prog = ProgName->c_str();
Printf("\nTo run fuzzing pass 0 or more directories.\n");
Printf("%s [-flag1=val1 [-flag2=val2 ...] ] [dir1 [dir2 ...] ]\n", Prog);
Printf("\nTo run individual tests without fuzzing pass 1 or more files:\n");
Printf("%s [-flag1=val1 [-flag2=val2 ...] ] file1 [file2 ...]\n", Prog);
Printf("\nFlags: (strictly in form -flag=value)\n");
size_t MaxFlagLen = 0;
for (size_t F = 0; F < kNumFlags; F++)
MaxFlagLen = std::max(strlen(FlagDescriptions[F].Name), MaxFlagLen);
for (size_t F = 0; F < kNumFlags; F++) {
const auto &D = FlagDescriptions[F];
if (strstr(D.Description, "internal flag") == D.Description) continue;
Printf(" %s", D.Name);
for (size_t i = 0, n = MaxFlagLen - strlen(D.Name); i < n; i++)
Printf(" ");
Printf("\t");
Printf("%d\t%s\n", D.Default, D.Description);
}
Printf("\nFlags starting with '--' will be ignored and "
"will be passed verbatim to subprocesses.\n");
}
static const char *FlagValue(const char *Param, const char *Name) {
size_t Len = strlen(Name);
if (Param[0] == '-' && strstr(Param + 1, Name) == Param + 1 &&
Param[Len + 1] == '=')
return &Param[Len + 2];
return nullptr;
}
// Avoid calling stol as it triggers a bug in clang/glibc build.
static long MyStol(const char *Str) {
long Res = 0;
long Sign = 1;
if (*Str == '-') {
Str++;
Sign = -1;
}
for (size_t i = 0; Str[i]; i++) {
char Ch = Str[i];
if (Ch < '0' || Ch > '9')
return Res;
Res = Res * 10 + (Ch - '0');
}
return Res * Sign;
}
static bool ParseOneFlag(const char *Param) {
if (Param[0] != '-') return false;
if (Param[1] == '-') {
static bool PrintedWarning = false;
if (!PrintedWarning) {
PrintedWarning = true;
Printf("INFO: libFuzzer ignores flags that start with '--'\n");
}
for (size_t F = 0; F < kNumFlags; F++)
if (FlagValue(Param + 1, FlagDescriptions[F].Name))
Printf("WARNING: did you mean '%s' (single dash)?\n", Param + 1);
return true;
}
for (size_t F = 0; F < kNumFlags; F++) {
const char *Name = FlagDescriptions[F].Name;
const char *Str = FlagValue(Param, Name);
if (Str) {
if (FlagDescriptions[F].IntFlag) {
int Val = MyStol(Str);
*FlagDescriptions[F].IntFlag = Val;
if (Flags.verbosity >= 2)
Printf("Flag: %s %d\n", Name, Val);;
return true;
} else if (FlagDescriptions[F].UIntFlag) {
unsigned int Val = std::stoul(Str);
*FlagDescriptions[F].UIntFlag = Val;
if (Flags.verbosity >= 2)
Printf("Flag: %s %u\n", Name, Val);
return true;
} else if (FlagDescriptions[F].StrFlag) {
*FlagDescriptions[F].StrFlag = Str;
if (Flags.verbosity >= 2)
Printf("Flag: %s %s\n", Name, Str);
return true;
} else { // Deprecated flag.
Printf("Flag: %s: deprecated, don't use\n", Name);
return true;
}
}
}
Printf("\n\nWARNING: unrecognized flag '%s'; "
"use -help=1 to list all flags\n\n", Param);
return true;
}
// We don't use any library to minimize dependencies.
static void ParseFlags(const std::vector<std::string> &Args) {
for (size_t F = 0; F < kNumFlags; F++) {
if (FlagDescriptions[F].IntFlag)
*FlagDescriptions[F].IntFlag = FlagDescriptions[F].Default;
if (FlagDescriptions[F].UIntFlag)
*FlagDescriptions[F].UIntFlag =
static_cast<unsigned int>(FlagDescriptions[F].Default);
if (FlagDescriptions[F].StrFlag)
*FlagDescriptions[F].StrFlag = nullptr;
}
Inputs = new std::vector<std::string>;
for (size_t A = 1; A < Args.size(); A++) {
if (ParseOneFlag(Args[A].c_str())) continue;
Inputs->push_back(Args[A]);
}
}
static std::mutex Mu;
static void PulseThread() {
while (true) {
SleepSeconds(600);
std::lock_guard<std::mutex> Lock(Mu);
Printf("pulse...\n");
}
}
static void WorkerThread(const std::string &Cmd, 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);
if (ExitCode != 0)
*HasErrors = true;
std::lock_guard<std::mutex> Lock(Mu);
Printf("================== Job %u exited with exit code %d ============\n",
C, ExitCode);
fuzzer::CopyFileToErr(Log);
}
}
std::string CloneArgsWithoutX(const std::vector<std::string> &Args,
const char *X1, const char *X2) {
std::string Cmd;
for (auto &S : Args) {
if (FlagValue(S.c_str(), X1) || FlagValue(S.c_str(), X2))
continue;
Cmd += S + " ";
}
return Cmd;
}
static int RunInMultipleProcesses(const std::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;
std::thread Pulse(PulseThread);
Pulse.detach();
for (unsigned i = 0; i < NumWorkers; i++)
V.push_back(std::thread(WorkerThread, Cmd, &Counter, NumJobs, &HasErrors));
for (auto &T : V)
T.join();
return HasErrors ? 1 : 0;
}
static void RssThread(Fuzzer *F, size_t RssLimitMb) {
while (true) {
SleepSeconds(1);
size_t Peak = GetPeakRSSMb();
if (Peak > RssLimitMb)
F->RssLimitCallback();
}
}
static void StartRssThread(Fuzzer *F, size_t RssLimitMb) {
if (!RssLimitMb) return;
std::thread T(RssThread, F, RssLimitMb);
T.detach();
}
int RunOneTest(Fuzzer *F, const char *InputFilePath, size_t MaxLen) {
Unit U = FileToVector(InputFilePath);
if (MaxLen && MaxLen < U.size())
U.resize(MaxLen);
F->RunOne(U.data(), U.size());
F->TryDetectingAMemoryLeak(U.data(), U.size(), true);
return 0;
}
static bool AllInputsAreFiles() {
if (Inputs->empty()) return false;
for (auto &Path : *Inputs)
if (!IsFile(Path))
return false;
return true;
}
int MinimizeCrashInput(const std::vector<std::string> &Args) {
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);
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 += " > /dev/null 2>&1 ";
std::string CurrentFilePath = InputFilePath;
while (true) {
Unit U = FileToVector(CurrentFilePath);
if (U.size() < 2) {
Printf("CRASH_MIN: '%s' is small enough\n", CurrentFilePath.c_str());
return 0;
}
Printf("CRASH_MIN: minimizing crash input: '%s' (%zd bytes)\n",
CurrentFilePath.c_str(), U.size());
auto Cmd = BaseCmd + " " + CurrentFilePath;
Printf("CRASH_MIN: executing: %s\n", Cmd.c_str());
int ExitCode = ExecuteCommand(Cmd);
if (ExitCode == 0) {
Printf("ERROR: the input %s did not crash\n", CurrentFilePath.c_str());
exit(1);
}
Printf("CRASH_MIN: '%s' (%zd bytes) caused a crash. Will try to minimize "
"it further\n",
CurrentFilePath.c_str(), U.size());
std::string ArtifactPath = "minimized-from-" + Hash(U);
Cmd += " -minimize_crash_internal_step=1 -exact_artifact_path=" +
ArtifactPath;
Printf("CRASH_MIN: executing: %s\n", Cmd.c_str());
ExitCode = ExecuteCommand(Cmd);
if (ExitCode == 0) {
if (Flags.exact_artifact_path) {
CurrentFilePath = Flags.exact_artifact_path;
WriteToFile(U, CurrentFilePath);
}
Printf("CRASH_MIN: failed to minimize beyond %s (%d bytes), exiting\n",
CurrentFilePath.c_str(), U.size());
return 0;
}
CurrentFilePath = ArtifactPath;
Printf("\n\n\n\n\n\n*********************************\n");
}
return 0;
}
int MinimizeCrashInputInternalStep(Fuzzer *F, InputCorpus *Corpus) {
assert(Inputs->size() == 1);
std::string InputFilePath = Inputs->at(0);
Unit U = FileToVector(InputFilePath);
assert(U.size() > 2);
Printf("INFO: Starting MinimizeCrashInputInternalStep: %zd\n", U.size());
Corpus->AddToCorpus(U, 0);
F->SetMaxInputLen(U.size());
F->SetMaxMutationLen(U.size() - 1);
F->MinimizeCrashLoop(U);
Printf("INFO: Done MinimizeCrashInputInternalStep, no crashes found\n");
exit(0);
return 0;
}
int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) {
using namespace fuzzer;
assert(argc && argv && "Argument pointers cannot be nullptr");
EF = new ExternalFunctions();
if (EF->LLVMFuzzerInitialize)
EF->LLVMFuzzerInitialize(argc, argv);
const std::vector<std::string> Args(*argv, *argv + *argc);
assert(!Args.empty());
ProgName = new std::string(Args[0]);
ParseFlags(Args);
if (Flags.help) {
PrintHelp();
return 0;
}
if (Flags.minimize_crash)
return MinimizeCrashInput(Args);
if (Flags.close_fd_mask & 2)
DupAndCloseStderr();
if (Flags.close_fd_mask & 1)
CloseStdout();
if (Flags.jobs > 0 && Flags.workers == 0) {
Flags.workers = std::min(NumberOfCpuCores() / 2, Flags.jobs);
if (Flags.workers > 1)
Printf("Running %u workers\n", Flags.workers);
}
if (Flags.workers > 0 && Flags.jobs > 0)
return RunInMultipleProcesses(Args, Flags.workers, Flags.jobs);
const size_t kMaxSaneLen = 1 << 20;
const size_t kMinDefaultLen = 64;
FuzzingOptions Options;
Options.Verbosity = Flags.verbosity;
Options.MaxLen = Flags.max_len;
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.UseCounters = Flags.use_counters;
Options.UseIndirCalls = Flags.use_indir_calls;
Options.UseMemcmp = Flags.use_memcmp;
Options.UseMemmem = Flags.use_memmem;
Options.UseCmp = Flags.use_cmp;
Options.UseValueProfile = Flags.use_value_profile;
Options.Shrink = Flags.shrink;
Options.ShuffleAtStartUp = Flags.shuffle;
Options.PreferSmall = Flags.prefer_small;
Options.ReloadIntervalSec = Flags.reload;
Options.OnlyASCII = Flags.only_ascii;
Options.OutputCSV = Flags.output_csv;
Options.DetectLeaks = Flags.detect_leaks;
Options.TraceMalloc = Flags.trace_malloc;
Options.RssLimitMb = Flags.rss_limit_mb;
if (Flags.runs >= 0)
Options.MaxNumberOfRuns = Flags.runs;
if (!Inputs->empty() && !Flags.minimize_crash_internal_step)
Options.OutputCorpus = (*Inputs)[0];
Options.ReportSlowUnits = Flags.report_slow_units;
if (Flags.artifact_prefix)
Options.ArtifactPrefix = Flags.artifact_prefix;
if (Flags.exact_artifact_path)
Options.ExactArtifactPath = Flags.exact_artifact_path;
std::vector<Unit> Dictionary;
if (Flags.dict)
if (!ParseDictionaryFile(FileToString(Flags.dict), &Dictionary))
return 1;
if (Flags.verbosity > 0 && !Dictionary.empty())
Printf("Dictionary: %zd entries\n", Dictionary.size());
bool DoPlainRun = AllInputsAreFiles();
Options.SaveArtifacts =
!DoPlainRun || Flags.minimize_crash_internal_step;
Options.PrintNewCovPcs = Flags.print_pcs;
Options.PrintFinalStats = Flags.print_final_stats;
Options.PrintCorpusStats = Flags.print_corpus_stats;
Options.PrintCoverage = Flags.print_coverage;
Options.DumpCoverage = Flags.dump_coverage;
if (Flags.exit_on_src_pos)
Options.ExitOnSrcPos = Flags.exit_on_src_pos;
if (Flags.exit_on_item)
Options.ExitOnItem = Flags.exit_on_item;
unsigned Seed = Flags.seed;
// Initialize Seed.
if (Seed == 0)
Seed = (std::chrono::system_clock::now().time_since_epoch().count() << 10) +
GetPid();
if (Flags.verbosity)
Printf("INFO: Seed: %u\n", Seed);
Random Rand(Seed);
auto *MD = new MutationDispatcher(Rand, Options);
auto *Corpus = new InputCorpus(Options.OutputCorpus);
auto *F = new Fuzzer(Callback, *Corpus, *MD, Options);
for (auto &U: Dictionary)
if (U.size() <= Word::GetMaxSize())
MD->AddWordToManualDictionary(Word(U.data(), U.size()));
StartRssThread(F, Flags.rss_limit_mb);
Options.HandleAbrt = Flags.handle_abrt;
Options.HandleBus = Flags.handle_bus;
Options.HandleFpe = Flags.handle_fpe;
Options.HandleIll = Flags.handle_ill;
Options.HandleInt = Flags.handle_int;
Options.HandleSegv = Flags.handle_segv;
Options.HandleTerm = Flags.handle_term;
SetSignalHandler(Options);
if (Flags.minimize_crash_internal_step)
return MinimizeCrashInputInternalStep(F, Corpus);
if (DoPlainRun) {
Options.SaveArtifacts = false;
int Runs = std::max(1, Flags.runs);
Printf("%s: Running %zd inputs %d time(s) each.\n", ProgName->c_str(),
Inputs->size(), Runs);
for (auto &Path : *Inputs) {
auto StartTime = system_clock::now();
Printf("Running: %s\n", Path.c_str());
for (int Iter = 0; Iter < Runs; Iter++)
RunOneTest(F, Path.c_str(), Options.MaxLen);
auto StopTime = system_clock::now();
auto MS = duration_cast<milliseconds>(StopTime - StartTime).count();
Printf("Executed %s in %zd ms\n", Path.c_str(), (long)MS);
}
Printf("***\n"
"*** NOTE: fuzzing was not performed, you have only\n"
"*** executed the target code on a fixed set of inputs.\n"
"***\n");
F->PrintFinalStats();
exit(0);
}
if (Flags.merge) {
if (Options.MaxLen == 0)
F->SetMaxInputLen(kMaxSaneLen);
if (TPC.UsingTracePcGuard()) {
if (Flags.merge_control_file)
F->CrashResistantMergeInternalStep(Flags.merge_control_file);
else
F->CrashResistantMerge(Args, *Inputs);
} else {
F->Merge(*Inputs);
}
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 (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();
if (Flags.verbosity)
Printf("Done %d runs in %zd second(s)\n", F->getTotalNumberOfRuns(),
F->secondsSinceProcessStartUp());
F->PrintFinalStats();
exit(0); // Don't let F destroy itself.
}
// Storage for global ExternalFunctions object.
ExternalFunctions *EF = nullptr;
} // namespace fuzzer

View file

@ -0,0 +1,50 @@
//===- FuzzerExtFunctions.def - External functions --------------*- C++ -* ===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
// This defines the external function pointers that
// ``fuzzer::ExternalFunctions`` should contain and try to initialize. The
// EXT_FUNC macro must be defined at the point of inclusion. The signature of
// the macro is:
//
// EXT_FUNC(<name>, <return_type>, <function_signature>, <warn_if_missing>)
//===----------------------------------------------------------------------===//
// Optional user functions
EXT_FUNC(LLVMFuzzerInitialize, int, (int *argc, char ***argv), false);
EXT_FUNC(LLVMFuzzerCustomMutator, size_t,
(uint8_t * Data, size_t Size, size_t MaxSize, unsigned int Seed),
false);
EXT_FUNC(LLVMFuzzerCustomCrossOver, size_t,
(const uint8_t * Data1, size_t Size1,
const uint8_t * Data2, size_t Size2,
uint8_t * Out, size_t MaxOutSize, unsigned int Seed),
false);
// Sanitizer functions
EXT_FUNC(__lsan_enable, void, (), false);
EXT_FUNC(__lsan_disable, void, (), false);
EXT_FUNC(__lsan_do_recoverable_leak_check, int, (), false);
EXT_FUNC(__sanitizer_get_number_of_counters, size_t, (), false);
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_get_total_unique_caller_callee_pairs, size_t, (), false);
EXT_FUNC(__sanitizer_get_total_unique_coverage, size_t, (), true);
EXT_FUNC(__sanitizer_print_memory_profile, int, (size_t), false);
EXT_FUNC(__sanitizer_print_stack_trace, void, (), true);
EXT_FUNC(__sanitizer_symbolize_pc, void,
(void *, const char *fmt, char *out_buf, size_t out_buf_size), false);
EXT_FUNC(__sanitizer_get_module_and_offset_for_pc, int,
(void *pc, char *module_path,
size_t module_path_len,void **pc_offset), false);
EXT_FUNC(__sanitizer_reset_coverage, void, (), true);
EXT_FUNC(__sanitizer_set_death_callback, void, (void (*)(void)), true);
EXT_FUNC(__sanitizer_set_report_fd, void, (void*), false);
EXT_FUNC(__sanitizer_update_counter_bitset_and_clear_counters, uintptr_t,
(uint8_t*), false);

View file

@ -0,0 +1,35 @@
//===- FuzzerExtFunctions.h - Interface to external functions ---*- C++ -* ===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
// Defines an interface to (possibly optional) functions.
//===----------------------------------------------------------------------===//
#ifndef LLVM_FUZZER_EXT_FUNCTIONS_H
#define LLVM_FUZZER_EXT_FUNCTIONS_H
#include <stddef.h>
#include <stdint.h>
namespace fuzzer {
struct ExternalFunctions {
// Initialize function pointers. Functions that are not available will be set
// to nullptr. Do not call this constructor before ``main()`` has been
// entered.
ExternalFunctions();
#define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \
RETURN_TYPE(*NAME) FUNC_SIG = nullptr
#include "FuzzerExtFunctions.def"
#undef EXT_FUNC
};
} // namespace fuzzer
#endif

View file

@ -0,0 +1,52 @@
//===- FuzzerExtFunctionsDlsym.cpp - Interface to external functions ------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
// Implementation for operating systems that support dlsym(). We only use it on
// Apple platforms for now. We don't use this approach on Linux because it
// requires that clients of LibFuzzer pass ``--export-dynamic`` to the linker.
// That is a complication we don't wish to expose to clients right now.
//===----------------------------------------------------------------------===//
#include "FuzzerDefs.h"
#if LIBFUZZER_APPLE
#include "FuzzerExtFunctions.h"
#include "FuzzerIO.h"
#include <dlfcn.h>
using namespace fuzzer;
template <typename T>
static T GetFnPtr(const char *FnName, bool WarnIfMissing) {
dlerror(); // Clear any previous errors.
void *Fn = dlsym(RTLD_DEFAULT, FnName);
if (Fn == nullptr) {
if (WarnIfMissing) {
const char *ErrorMsg = dlerror();
Printf("WARNING: Failed to find function \"%s\".", FnName);
if (ErrorMsg)
Printf(" Reason %s.", ErrorMsg);
Printf("\n");
}
}
return reinterpret_cast<T>(Fn);
}
namespace fuzzer {
ExternalFunctions::ExternalFunctions() {
#define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \
this->NAME = GetFnPtr<decltype(ExternalFunctions::NAME)>(#NAME, WARN)
#include "FuzzerExtFunctions.def"
#undef EXT_FUNC
}
} // namespace fuzzer
#endif // LIBFUZZER_APPLE

View file

@ -0,0 +1,53 @@
//===- FuzzerExtFunctionsWeak.cpp - Interface to external functions -------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
// Implementation for Linux. This relies on the linker's support for weak
// symbols. We don't use this approach on Apple platforms because it requires
// clients of LibFuzzer to pass ``-U _<symbol_name>`` to the linker to allow
// weak symbols to be undefined. That is a complication we don't want to expose
// to clients right now.
//===----------------------------------------------------------------------===//
#include "FuzzerDefs.h"
#if LIBFUZZER_LINUX
#include "FuzzerExtFunctions.h"
#include "FuzzerIO.h"
extern "C" {
// Declare these symbols as weak to allow them to be optionally defined.
#define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \
__attribute__((weak)) RETURN_TYPE NAME FUNC_SIG
#include "FuzzerExtFunctions.def"
#undef EXT_FUNC
}
using namespace fuzzer;
static void CheckFnPtr(void *FnPtr, const char *FnName, bool WarnIfMissing) {
if (FnPtr == nullptr && WarnIfMissing) {
Printf("WARNING: Failed to find function \"%s\".\n", FnName);
}
}
namespace fuzzer {
ExternalFunctions::ExternalFunctions() {
#define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \
this->NAME = ::NAME; \
CheckFnPtr((void *)::NAME, #NAME, WARN);
#include "FuzzerExtFunctions.def"
#undef EXT_FUNC
}
} // namespace fuzzer
#endif // LIBFUZZER_LINUX

View file

@ -0,0 +1,56 @@
//===- FuzzerExtFunctionsWeakAlias.cpp - Interface to external functions --===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
// Implementation using weak aliases. Works for Windows.
//===----------------------------------------------------------------------===//
#include "FuzzerDefs.h"
#if LIBFUZZER_WINDOWS
#include "FuzzerExtFunctions.h"
#include "FuzzerIO.h"
using namespace fuzzer;
extern "C" {
// Declare these symbols as weak to allow them to be optionally defined.
#define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \
RETURN_TYPE NAME##Def FUNC_SIG { \
Printf("ERROR: Function \"%s\" not defined.\n", #NAME); \
exit(1); \
} \
RETURN_TYPE NAME FUNC_SIG __attribute__((weak, alias(#NAME "Def")));
#include "FuzzerExtFunctions.def"
#undef EXT_FUNC
}
template <typename T>
static T *GetFnPtr(T *Fun, T *FunDef, const char *FnName, bool WarnIfMissing) {
if (Fun == FunDef) {
if (WarnIfMissing)
Printf("WARNING: Failed to find function \"%s\".\n", FnName);
return nullptr;
}
return Fun;
}
namespace fuzzer {
ExternalFunctions::ExternalFunctions() {
#define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \
this->NAME = GetFnPtr<decltype(::NAME)>(::NAME, ::NAME##Def, #NAME, WARN);
#include "FuzzerExtFunctions.def"
#undef EXT_FUNC
}
} // namespace fuzzer
#endif // LIBFUZZER_WINDOWS

View file

@ -0,0 +1,115 @@
//===- FuzzerFlags.def - Run-time flags -------------------------*- C++ -* ===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
// Flags. FUZZER_FLAG_INT/FUZZER_FLAG_STRING macros should be defined at the
// point of inclusion. We are not using any flag parsing library for better
// portability and independence.
//===----------------------------------------------------------------------===//
FUZZER_FLAG_INT(verbosity, 1, "Verbosity level.")
FUZZER_FLAG_UNSIGNED(seed, 0, "Random seed. If 0, seed is generated.")
FUZZER_FLAG_INT(runs, -1,
"Number of individual test runs (-1 for infinite runs).")
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(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(shuffle, 1, "Shuffle inputs at startup")
FUZZER_FLAG_INT(prefer_small, 1,
"If 1, always prefer smaller inputs during the corpus shuffle.")
FUZZER_FLAG_INT(
timeout, 1200,
"Timeout in seconds (if positive). "
"If one unit runs more than this number of seconds the process will abort.")
FUZZER_FLAG_INT(error_exitcode, 77, "When libFuzzer itself reports a bug "
"this exit code will be used.")
FUZZER_FLAG_INT(timeout_exitcode, 77, "When libFuzzer reports a timeout "
"this exit code will be used.")
FUZZER_FLAG_INT(max_total_time, 0, "If positive, indicates the maximal total "
"time in seconds to run the fuzzer.")
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_INT(minimize_crash, 0, "If 1, minimizes the provided"
" crash input. Use with -runs=N or -max_total_time=N to limit "
"the number attempts")
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_memcmp, 1,
"Use hints from intercepting memcmp, strcmp, etc")
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_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.")
FUZZER_FLAG_UNSIGNED(workers, 0,
"Number of simultaneous worker processes to run the jobs."
" If zero, \"min(jobs,NumberOfCpuCores()/2)\" is used.")
FUZZER_FLAG_INT(reload, 1,
"Reload the main corpus every <N> seconds to get new units"
" discovered by other processes. If 0, disabled")
FUZZER_FLAG_INT(report_slow_units, 10,
"Report slowest units if they run for more than this number of seconds.")
FUZZER_FLAG_INT(only_ascii, 0,
"If 1, generate only ASCII (isprint+isspace) inputs.")
FUZZER_FLAG_STRING(dict, "Experimental. Use the dictionary file.")
FUZZER_FLAG_STRING(artifact_prefix, "Write fuzzing artifacts (crash, "
"timeout, or slow inputs) as "
"$(artifact_prefix)file")
FUZZER_FLAG_STRING(exact_artifact_path,
"Write the single artifact on failure (crash, timeout) "
"as $(exact_artifact_path). This overrides -artifact_prefix "
"and will not use checksum in the file name. Do not "
"use the same path for several parallel processes.")
FUZZER_FLAG_INT(output_csv, 0, "Enable pulse output in CSV format.")
FUZZER_FLAG_INT(print_pcs, 0, "If 1, print out newly covered PCs.")
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 at exit."
" Experimental, only with trace-pc-guard")
FUZZER_FLAG_INT(dump_coverage, 0, "If 1, dump coverage information at exit."
" Experimental, only with trace-pc-guard")
FUZZER_FLAG_INT(handle_segv, 1, "If 1, try to intercept SIGSEGV.")
FUZZER_FLAG_INT(handle_bus, 1, "If 1, try to intercept SIGSEGV.")
FUZZER_FLAG_INT(handle_abrt, 1, "If 1, try to intercept SIGABRT.")
FUZZER_FLAG_INT(handle_ill, 1, "If 1, try to intercept SIGILL.")
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(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.")
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(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_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_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)

View file

@ -0,0 +1,117 @@
//===- FuzzerIO.cpp - IO utils. -------------------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
// IO functions.
//===----------------------------------------------------------------------===//
#include "FuzzerIO.h"
#include "FuzzerDefs.h"
#include "FuzzerExtFunctions.h"
#include <algorithm>
#include <cstdarg>
#include <fstream>
#include <iterator>
#include <sys/stat.h>
#include <sys/types.h>
namespace fuzzer {
static FILE *OutputFile = stderr;
long GetEpoch(const std::string &Path) {
struct stat St;
if (stat(Path.c_str(), &St))
return 0; // Can't stat, be conservative.
return St.st_mtime;
}
Unit FileToVector(const std::string &Path, size_t MaxSize, bool ExitOnError) {
std::ifstream T(Path);
if (ExitOnError && !T) {
Printf("No such directory: %s; exiting\n", Path.c_str());
exit(1);
}
T.seekg(0, T.end);
size_t FileLen = T.tellg();
if (MaxSize)
FileLen = std::min(FileLen, MaxSize);
T.seekg(0, T.beg);
Unit Res(FileLen);
T.read(reinterpret_cast<char *>(Res.data()), FileLen);
return Res;
}
std::string FileToString(const std::string &Path) {
std::ifstream T(Path);
return std::string((std::istreambuf_iterator<char>(T)),
std::istreambuf_iterator<char>());
}
void CopyFileToErr(const std::string &Path) {
Printf("%s", FileToString(Path).c_str());
}
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;
fwrite(U.data(), sizeof(U[0]), U.size(), Out);
fclose(Out);
}
void ReadDirToVectorOfUnits(const char *Path, std::vector<Unit> *V,
long *Epoch, size_t MaxSize, bool ExitOnError) {
long E = Epoch ? *Epoch : 0;
std::vector<std::string> Files;
ListFilesInDirRecursive(Path, Epoch, &Files, /*TopDir*/true);
size_t NumLoaded = 0;
for (size_t i = 0; i < Files.size(); i++) {
auto &X = Files[i];
if (Epoch && GetEpoch(X) < E) continue;
NumLoaded++;
if ((NumLoaded & (NumLoaded - 1)) == 0 && NumLoaded >= 1024)
Printf("Loaded %zd/%zd files from %s\n", NumLoaded, Files.size(), Path);
auto S = FileToVector(X, MaxSize, ExitOnError);
if (!S.empty())
V->push_back(S);
}
}
std::string DirPlusFile(const std::string &DirPath,
const std::string &FileName) {
return DirPath + GetSeparator() + FileName;
}
void DupAndCloseStderr() {
int OutputFd = DuplicateFile(2);
if (OutputFd > 0) {
FILE *NewOutputFile = OpenFile(OutputFd, "w");
if (NewOutputFile) {
OutputFile = NewOutputFile;
if (EF->__sanitizer_set_report_fd)
EF->__sanitizer_set_report_fd(reinterpret_cast<void *>(OutputFd));
CloseFile(2);
}
}
}
void CloseStdout() {
CloseFile(1);
}
void Printf(const char *Fmt, ...) {
va_list ap;
va_start(ap, Fmt);
vfprintf(OutputFile, Fmt, ap);
va_end(ap);
fflush(OutputFile);
}
} // namespace fuzzer

View file

@ -0,0 +1,64 @@
//===- FuzzerIO.h - Internal header for IO utils ----------------*- C++ -* ===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
// IO interface.
//===----------------------------------------------------------------------===//
#ifndef LLVM_FUZZER_IO_H
#define LLVM_FUZZER_IO_H
#include "FuzzerDefs.h"
namespace fuzzer {
long GetEpoch(const std::string &Path);
Unit FileToVector(const std::string &Path, size_t MaxSize = 0,
bool ExitOnError = true);
std::string FileToString(const std::string &Path);
void CopyFileToErr(const std::string &Path);
void WriteToFile(const Unit &U, const std::string &Path);
void ReadDirToVectorOfUnits(const char *Path, std::vector<Unit> *V,
long *Epoch, size_t MaxSize, bool ExitOnError);
// Returns "Dir/FileName" or equivalent for the current OS.
std::string DirPlusFile(const std::string &DirPath,
const std::string &FileName);
// Returns the name of the dir, similar to the 'dirname' utility.
std::string DirName(const std::string &FileName);
void DupAndCloseStderr();
void CloseStdout();
void Printf(const char *Fmt, ...);
// Platform specific functions:
bool IsFile(const std::string &Path);
void ListFilesInDirRecursive(const std::string &Dir, long *Epoch,
std::vector<std::string> *V, bool TopDir);
char GetSeparator();
FILE* OpenFile(int Fd, const char *Mode);
int CloseFile(int Fd);
int DuplicateFile(int Fd);
void RemoveFile(const std::string &Path);
} // namespace fuzzer
#endif // LLVM_FUZZER_IO_H

View file

@ -0,0 +1,88 @@
//===- FuzzerIOPosix.cpp - IO utils for Posix. ----------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
// IO functions implementation using Posix API.
//===----------------------------------------------------------------------===//
#include "FuzzerDefs.h"
#if LIBFUZZER_POSIX
#include "FuzzerExtFunctions.h"
#include "FuzzerIO.h"
#include <cstdarg>
#include <cstdio>
#include <dirent.h>
#include <fstream>
#include <iterator>
#include <libgen.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
namespace fuzzer {
bool IsFile(const std::string &Path) {
struct stat St;
if (stat(Path.c_str(), &St))
return false;
return S_ISREG(St.st_mode);
}
void ListFilesInDirRecursive(const std::string &Dir, long *Epoch,
std::vector<std::string> *V, bool TopDir) {
auto E = GetEpoch(Dir);
if (Epoch)
if (E && *Epoch >= E) return;
DIR *D = opendir(Dir.c_str());
if (!D) {
Printf("No such directory: %s; exiting\n", Dir.c_str());
exit(1);
}
while (auto E = readdir(D)) {
std::string Path = DirPlusFile(Dir, E->d_name);
if (E->d_type == DT_REG || E->d_type == DT_LNK)
V->push_back(Path);
else if (E->d_type == DT_DIR && *E->d_name != '.')
ListFilesInDirRecursive(Path, Epoch, V, false);
}
closedir(D);
if (Epoch && TopDir)
*Epoch = E;
}
char GetSeparator() {
return '/';
}
FILE* OpenFile(int Fd, const char* Mode) {
return fdopen(Fd, Mode);
}
int CloseFile(int fd) {
return close(fd);
}
int DuplicateFile(int Fd) {
return dup(Fd);
}
void RemoveFile(const std::string &Path) {
unlink(Path.c_str());
}
std::string DirName(const std::string &FileName) {
char *Tmp = new char[FileName.size() + 1];
memcpy(Tmp, FileName.c_str(), FileName.size() + 1);
std::string Res = dirname(Tmp);
delete [] Tmp;
return Res;
}
} // namespace fuzzer
#endif // LIBFUZZER_POSIX

View file

@ -0,0 +1,282 @@
//===- FuzzerIOWindows.cpp - IO utils for Windows. ------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
// IO functions implementation for Windows.
//===----------------------------------------------------------------------===//
#include "FuzzerDefs.h"
#if LIBFUZZER_WINDOWS
#include "FuzzerExtFunctions.h"
#include "FuzzerIO.h"
#include <cstdarg>
#include <cstdio>
#include <fstream>
#include <io.h>
#include <iterator>
#include <sys/stat.h>
#include <sys/types.h>
#include <windows.h>
namespace fuzzer {
static bool IsFile(const std::string &Path, const DWORD &FileAttributes) {
if (FileAttributes & FILE_ATTRIBUTE_NORMAL)
return true;
if (FileAttributes & FILE_ATTRIBUTE_DIRECTORY)
return false;
HANDLE FileHandle(
CreateFileA(Path.c_str(), 0, FILE_SHARE_READ, NULL, OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS, 0));
if (FileHandle == INVALID_HANDLE_VALUE) {
Printf("CreateFileA() failed for \"%s\" (Error code: %lu).\n", Path.c_str(),
GetLastError());
return false;
}
DWORD FileType = GetFileType(FileHandle);
if (FileType == FILE_TYPE_UNKNOWN) {
Printf("GetFileType() failed for \"%s\" (Error code: %lu).\n", Path.c_str(),
GetLastError());
CloseHandle(FileHandle);
return false;
}
if (FileType != FILE_TYPE_DISK) {
CloseHandle(FileHandle);
return false;
}
CloseHandle(FileHandle);
return true;
}
bool IsFile(const std::string &Path) {
DWORD Att = GetFileAttributesA(Path.c_str());
if (Att == INVALID_FILE_ATTRIBUTES) {
Printf("GetFileAttributesA() failed for \"%s\" (Error code: %lu).\n",
Path.c_str(), GetLastError());
return false;
}
return IsFile(Path, Att);
}
void ListFilesInDirRecursive(const std::string &Dir, long *Epoch,
std::vector<std::string> *V, bool TopDir) {
auto E = GetEpoch(Dir);
if (Epoch)
if (E && *Epoch >= E) return;
std::string Path(Dir);
assert(!Path.empty());
if (Path.back() != '\\')
Path.push_back('\\');
Path.push_back('*');
// Get the first directory entry.
WIN32_FIND_DATAA FindInfo;
HANDLE FindHandle(FindFirstFileA(Path.c_str(), &FindInfo));
if (FindHandle == INVALID_HANDLE_VALUE)
{
Printf("No file found in: %s.\n", Dir.c_str());
return;
}
do {
std::string FileName = DirPlusFile(Dir, FindInfo.cFileName);
if (FindInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
size_t FilenameLen = strlen(FindInfo.cFileName);
if ((FilenameLen == 1 && FindInfo.cFileName[0] == '.') ||
(FilenameLen == 2 && FindInfo.cFileName[0] == '.' &&
FindInfo.cFileName[1] == '.'))
continue;
ListFilesInDirRecursive(FileName, Epoch, V, false);
}
else if (IsFile(FileName, FindInfo.dwFileAttributes))
V->push_back(FileName);
} while (FindNextFileA(FindHandle, &FindInfo));
DWORD LastError = GetLastError();
if (LastError != ERROR_NO_MORE_FILES)
Printf("FindNextFileA failed (Error code: %lu).\n", LastError);
FindClose(FindHandle);
if (Epoch && TopDir)
*Epoch = E;
}
char GetSeparator() {
return '\\';
}
FILE* OpenFile(int Fd, const char* Mode) {
return _fdopen(Fd, Mode);
}
int CloseFile(int Fd) {
return _close(Fd);
}
int DuplicateFile(int Fd) {
return _dup(Fd);
}
void RemoveFile(const std::string &Path) {
_unlink(Path.c_str());
}
static bool IsSeparator(char C) {
return C == '\\' || C == '/';
}
// Parse disk designators, like "C:\". If Relative == true, also accepts: "C:".
// Returns number of characters considered if successful.
static size_t ParseDrive(const std::string &FileName, const size_t Offset,
bool Relative = true) {
if (Offset + 1 >= FileName.size() || FileName[Offset + 1] != ':')
return 0;
if (Offset + 2 >= FileName.size() || !IsSeparator(FileName[Offset + 2])) {
if (!Relative) // Accept relative path?
return 0;
else
return 2;
}
return 3;
}
// Parse a file name, like: SomeFile.txt
// Returns number of characters considered if successful.
static size_t ParseFileName(const std::string &FileName, const size_t Offset) {
size_t Pos = Offset;
const size_t End = FileName.size();
for(; Pos < End && !IsSeparator(FileName[Pos]); ++Pos)
;
return Pos - Offset;
}
// 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;
const size_t End = FileName.size();
if (Pos >= End || IsSeparator(FileName[Pos]))
return 0;
for(; Pos < End && !IsSeparator(FileName[Pos]); ++Pos)
;
if (Pos >= End)
return 0;
++Pos; // Include separator.
return Pos - Offset;
}
// 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) {
size_t Pos = Offset, Res;
if (!(Res = ParseDir(FileName, Pos)))
return 0;
Pos += Res;
if (!(Res = ParseDir(FileName, Pos)))
return 0;
Pos += Res;
return Pos - Offset;
}
// Parse the given Ref string from the position Offset, to exactly match the given
// string Patt.
// Returns number of characters considered if successful.
static size_t ParseCustomString(const std::string &Ref, size_t Offset,
const char *Patt) {
size_t Len = strlen(Patt);
if (Offset + Len > Ref.size())
return 0;
return Ref.compare(Offset, Len, Patt) == 0 ? Len : 0;
}
// Parse a location, like:
// \\?\UNC\Server\Share\ \\?\C:\ \\Server\Share\ \ C:\ C:
// Returns number of characters considered if successful.
static size_t ParseLocation(const std::string &FileName) {
size_t Pos = 0, Res;
if ((Res = ParseCustomString(FileName, Pos, R"(\\?\)"))) {
Pos += Res;
if ((Res = ParseCustomString(FileName, Pos, R"(UNC\)"))) {
Pos += Res;
if ((Res = ParseServerAndShare(FileName, Pos)))
return Pos + Res;
return 0;
}
if ((Res = ParseDrive(FileName, Pos, false)))
return Pos + Res;
return 0;
}
if (Pos < FileName.size() && IsSeparator(FileName[Pos])) {
++Pos;
if (Pos < FileName.size() && IsSeparator(FileName[Pos])) {
++Pos;
if ((Res = ParseServerAndShare(FileName, Pos)))
return Pos + Res;
return 0;
}
return Pos;
}
if ((Res = ParseDrive(FileName, Pos)))
return Pos + Res;
return Pos;
}
std::string DirName(const std::string &FileName) {
size_t LocationLen = ParseLocation(FileName);
size_t DirLen = 0, Res;
while ((Res = ParseDir(FileName, LocationLen + DirLen)))
DirLen += Res;
size_t FileLen = ParseFileName(FileName, LocationLen + DirLen);
if (LocationLen + DirLen + FileLen != FileName.size()) {
Printf("DirName() failed for \"%s\", invalid path.\n", FileName.c_str());
exit(1);
}
if (DirLen) {
--DirLen; // Remove trailing separator.
if (!FileLen) { // Path ended in separator.
assert(DirLen);
// Remove file name from Dir.
while (DirLen && !IsSeparator(FileName[LocationLen + DirLen - 1]))
--DirLen;
if (DirLen) // Remove trailing separator.
--DirLen;
}
}
if (!LocationLen) { // Relative path.
if (!DirLen)
return ".";
return std::string(".\\").append(FileName, 0, DirLen);
}
return FileName.substr(0, LocationLen + DirLen);
}
} // namespace fuzzer
#endif // LIBFUZZER_WINDOWS

View file

@ -0,0 +1,67 @@
//===- FuzzerInterface.h - Interface header for the Fuzzer ------*- C++ -* ===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
// Define the interface between libFuzzer and the library being tested.
//===----------------------------------------------------------------------===//
// NOTE: the libFuzzer interface is thin and in the majority of cases
// you should not include this file into your target. In 95% of cases
// all you need is to define the following function in your file:
// extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size);
// WARNING: keep the interface in C.
#ifndef LLVM_FUZZER_INTERFACE_H
#define LLVM_FUZZER_INTERFACE_H
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
// Mandatory user-provided target function.
// 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);
// 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);
// 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);
// 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);
// Experimental, may go away in future.
// libFuzzer-provided function to be used inside LLVMFuzzerTestOneInput.
// 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);
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus
#endif // LLVM_FUZZER_INTERFACE_H

View file

@ -0,0 +1,182 @@
//===- FuzzerInternal.h - Internal header for the Fuzzer --------*- C++ -* ===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
// Define the main class fuzzer::Fuzzer and most functions.
//===----------------------------------------------------------------------===//
#ifndef LLVM_FUZZER_INTERNAL_H
#define LLVM_FUZZER_INTERNAL_H
#include "FuzzerDefs.h"
#include "FuzzerExtFunctions.h"
#include "FuzzerInterface.h"
#include "FuzzerOptions.h"
#include "FuzzerSHA1.h"
#include "FuzzerValueBitMap.h"
#include <algorithm>
#include <atomic>
#include <chrono>
#include <climits>
#include <cstdlib>
#include <string.h>
namespace fuzzer {
using namespace std::chrono;
class Fuzzer {
public:
// Aggregates all available coverage measurements.
struct Coverage {
Coverage() { Reset(); }
void Reset() {
BlockCoverage = 0;
CallerCalleeCoverage = 0;
CounterBitmapBits = 0;
CounterBitmap.clear();
VPMap.Reset();
}
size_t BlockCoverage;
size_t CallerCalleeCoverage;
// Precalculated number of bits in CounterBitmap.
size_t CounterBitmapBits;
std::vector<uint8_t> CounterBitmap;
ValueBitMap VPMap;
};
Fuzzer(UserCallback CB, InputCorpus &Corpus, MutationDispatcher &MD,
FuzzingOptions Options);
~Fuzzer();
void Loop();
void MinimizeCrashLoop(const Unit &U);
void ShuffleAndMinimize(UnitVector *V);
void InitializeTraceState();
void RereadOutputCorpus(size_t MaxSize);
size_t secondsSinceProcessStartUp() {
return duration_cast<seconds>(system_clock::now() - ProcessStartTime)
.count();
}
bool TimedOut() {
return Options.MaxTotalTimeSec > 0 &&
secondsSinceProcessStartUp() >
static_cast<size_t>(Options.MaxTotalTimeSec);
}
size_t execPerSec() {
size_t Seconds = secondsSinceProcessStartUp();
return Seconds ? TotalNumberOfRuns / Seconds : 0;
}
size_t getTotalNumberOfRuns() { return TotalNumberOfRuns; }
static void StaticAlarmCallback();
static void StaticCrashSignalCallback();
static void StaticInterruptCallback();
void ExecuteCallback(const uint8_t *Data, size_t Size);
size_t RunOne(const uint8_t *Data, size_t Size);
// 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 CrashResistantMergeInternalStep(const std::string &ControlFilePath);
// Returns a subset of 'Extra' that adds coverage to 'Initial'.
UnitVector FindExtraUnits(const UnitVector &Initial, const UnitVector &Extra);
MutationDispatcher &GetMD() { return MD; }
void PrintFinalStats();
void SetMaxInputLen(size_t MaxInputLen);
void SetMaxMutationLen(size_t MaxMutationLen);
void RssLimitCallback();
// Public for tests.
void ResetCoverage();
bool InFuzzingThread() const { return IsMyThread; }
size_t GetCurrentUnitInFuzzingThead(const uint8_t **Data) const;
void TryDetectingAMemoryLeak(const uint8_t *Data, size_t Size,
bool DuringInitialCorpusExecution);
void HandleMalloc(size_t Size);
private:
void AlarmCallback();
void CrashCallback();
void InterruptCallback();
void MutateAndTestOne();
void ReportNewCoverage(InputInfo *II, const Unit &U);
size_t RunOne(const Unit &U) { return RunOne(U.data(), U.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 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();
void SetDeathCallback();
static void StaticDeathCallback();
void DumpCurrentUnit(const char *Prefix);
void DeathCallback();
void ResetEdgeCoverage();
void ResetCounters();
void PrepareCounters(Fuzzer::Coverage *C);
bool RecordMaxCoverage(Fuzzer::Coverage *C);
void AllocateCurrentUnitData();
uint8_t *CurrentUnitData = nullptr;
std::atomic<size_t> CurrentUnitSize;
uint8_t BaseSha1[kSHA1NumBytes]; // Checksum of the base unit.
bool RunningCB = false;
size_t TotalNumberOfRuns = 0;
size_t NumberOfNewUnitsAdded = 0;
bool HasMoreMallocsThanFrees = false;
size_t NumberOfLeakDetectionAttempts = 0;
UserCallback CB;
InputCorpus &Corpus;
MutationDispatcher &MD;
FuzzingOptions Options;
system_clock::time_point ProcessStartTime = system_clock::now();
system_clock::time_point UnitStartTime, UnitStopTime;
long TimeOfLongestUnitInSeconds = 0;
long EpochOfLastReadOfOutputCorpus = 0;
// Maximum recorded coverage.
Coverage MaxCoverage;
size_t MaxInputLen = 0;
size_t MaxMutationLen = 0;
// Need to know our own thread.
static thread_local bool IsMyThread;
bool InMergeMode = false;
};
}; // namespace fuzzer
#endif // LLVM_FUZZER_INTERNAL_H

View file

@ -0,0 +1,792 @@
//===- FuzzerLoop.cpp - Fuzzer's main loop --------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
// Fuzzer's main loop.
//===----------------------------------------------------------------------===//
#include "FuzzerCorpus.h"
#include "FuzzerInternal.h"
#include "FuzzerIO.h"
#include "FuzzerMutate.h"
#include "FuzzerRandom.h"
#include "FuzzerTracePC.h"
#include <algorithm>
#include <cstring>
#include <memory>
#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
#endif
#define NO_SANITIZE_MEMORY
#if defined(__has_feature)
#if __has_feature(memory_sanitizer)
#undef NO_SANITIZE_MEMORY
#define NO_SANITIZE_MEMORY __attribute__((no_sanitize_memory))
#endif
#endif
namespace fuzzer {
static const size_t kMaxUnitSizeToPrint = 256;
thread_local bool Fuzzer::IsMyThread;
static void MissingExternalApiFunction(const char *FnName) {
Printf("ERROR: %s is not defined. Exiting.\n"
"Did you use -fsanitize-coverage=... to build your code?\n",
FnName);
exit(1);
}
#define CHECK_EXTERNAL_FUNCTION(fn) \
do { \
if (!(EF->fn)) \
MissingExternalApiFunction(#fn); \
} while (false)
// Only one Fuzzer per process.
static Fuzzer *F;
void Fuzzer::ResetEdgeCoverage() {
CHECK_EXTERNAL_FUNCTION(__sanitizer_reset_coverage);
EF->__sanitizer_reset_coverage();
}
void Fuzzer::ResetCounters() {
if (Options.UseCounters)
EF->__sanitizer_update_counter_bitset_and_clear_counters(0);
}
void Fuzzer::PrepareCounters(Fuzzer::Coverage *C) {
if (Options.UseCounters) {
size_t NumCounters = EF->__sanitizer_get_number_of_counters();
C->CounterBitmap.resize(NumCounters);
}
}
// Records data to a maximum coverage tracker. Returns true if additional
// coverage was discovered.
bool Fuzzer::RecordMaxCoverage(Fuzzer::Coverage *C) {
bool Res = false;
uint64_t NewBlockCoverage = EF->__sanitizer_get_total_unique_coverage();
if (NewBlockCoverage > C->BlockCoverage) {
Res = true;
C->BlockCoverage = NewBlockCoverage;
}
if (Options.UseIndirCalls &&
EF->__sanitizer_get_total_unique_caller_callee_pairs) {
uint64_t NewCallerCalleeCoverage =
EF->__sanitizer_get_total_unique_caller_callee_pairs();
if (NewCallerCalleeCoverage > C->CallerCalleeCoverage) {
Res = true;
C->CallerCalleeCoverage = NewCallerCalleeCoverage;
}
}
if (Options.UseCounters) {
uint64_t CounterDelta =
EF->__sanitizer_update_counter_bitset_and_clear_counters(
C->CounterBitmap.data());
if (CounterDelta > 0) {
Res = true;
C->CounterBitmapBits += CounterDelta;
}
}
return Res;
}
// Leak detection is expensive, so we first check if there were more mallocs
// than frees (using the sanitizer malloc hooks) and only then try to call lsan.
struct MallocFreeTracer {
void Start(int TraceLevel) {
this->TraceLevel = TraceLevel;
if (TraceLevel)
Printf("MallocFreeTracer: START\n");
Mallocs = 0;
Frees = 0;
}
// Returns true if there were more mallocs than frees.
bool Stop() {
if (TraceLevel)
Printf("MallocFreeTracer: STOP %zd %zd (%s)\n", Mallocs.load(),
Frees.load(), Mallocs == Frees ? "same" : "DIFFERENT");
bool Result = Mallocs > Frees;
Mallocs = 0;
Frees = 0;
TraceLevel = 0;
return Result;
}
std::atomic<size_t> Mallocs;
std::atomic<size_t> Frees;
int TraceLevel = 0;
};
static MallocFreeTracer AllocTracer;
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) {
Printf("MALLOC[%zd] %p %zd\n", N, ptr, size);
if (TraceLevel >= 2 && EF)
EF->__sanitizer_print_stack_trace();
}
}
ATTRIBUTE_NO_SANITIZE_MEMORY
void FreeHook(const volatile void *ptr) {
size_t N = AllocTracer.Frees++;
if (int TraceLevel = AllocTracer.TraceLevel) {
Printf("FREE[%zd] %p\n", N, ptr);
if (TraceLevel >= 2 && EF)
EF->__sanitizer_print_stack_trace();
}
}
// 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)
return;
Printf("==%d== ERROR: libFuzzer: out-of-memory (malloc(%zd))\n", GetPid(),
Size);
Printf(" To change the out-of-memory limit use -rss_limit_mb=<N>\n\n");
if (EF->__sanitizer_print_stack_trace)
EF->__sanitizer_print_stack_trace();
DumpCurrentUnit("oom-");
Printf("SUMMARY: libFuzzer: out-of-memory\n");
PrintFinalStats();
_Exit(Options.ErrorExitCode); // Stop right now.
}
Fuzzer::Fuzzer(UserCallback CB, InputCorpus &Corpus, MutationDispatcher &MD,
FuzzingOptions Options)
: CB(CB), Corpus(Corpus), MD(MD), Options(Options) {
SetDeathCallback();
InitializeTraceState();
assert(!F);
F = this;
TPC.ResetMaps();
ResetCoverage();
IsMyThread = true;
if (Options.DetectLeaks && EF->__sanitizer_install_malloc_and_free_hooks)
EF->__sanitizer_install_malloc_and_free_hooks(MallocHook, FreeHook);
TPC.SetUseCounters(Options.UseCounters);
TPC.SetUseValueProfile(Options.UseValueProfile);
TPC.SetPrintNewPCs(Options.PrintNewCovPcs);
if (Options.Verbosity)
TPC.PrintModuleInfo();
if (!Options.OutputCorpus.empty() && Options.ReloadIntervalSec)
EpochOfLastReadOfOutputCorpus = GetEpoch(Options.OutputCorpus);
MaxInputLen = MaxMutationLen = Options.MaxLen;
AllocateCurrentUnitData();
CurrentUnitSize = 0;
memset(BaseSha1, 0, sizeof(BaseSha1));
}
Fuzzer::~Fuzzer() { }
void Fuzzer::AllocateCurrentUnitData() {
if (CurrentUnitData || MaxInputLen == 0) return;
CurrentUnitData = new uint8_t[MaxInputLen];
}
void Fuzzer::SetDeathCallback() {
CHECK_EXTERNAL_FUNCTION(__sanitizer_set_death_callback);
EF->__sanitizer_set_death_callback(StaticDeathCallback);
}
void Fuzzer::StaticDeathCallback() {
assert(F);
F->DeathCallback();
}
static void WarnOnUnsuccessfullMerge(bool DoWarn) {
if (!DoWarn) return;
Printf(
"***\n"
"***\n"
"***\n"
"*** NOTE: merge did not succeed due to a failure on one of the inputs.\n"
"*** You will need to filter out crashes from the corpus, e.g. like this:\n"
"*** for f in WITH_CRASHES/*; do ./fuzzer $f && cp $f NO_CRASHES; done\n"
"*** Future versions may have crash-resistant merge, stay tuned.\n"
"***\n"
"***\n"
"***\n");
}
void Fuzzer::DumpCurrentUnit(const char *Prefix) {
WarnOnUnsuccessfullMerge(InMergeMode);
if (!CurrentUnitData) return; // Happens when running individual inputs.
MD.PrintMutationSequence();
Printf("; base unit: %s\n", Sha1ToString(BaseSha1).c_str());
size_t UnitSize = CurrentUnitSize;
if (UnitSize <= kMaxUnitSizeToPrint) {
PrintHexArray(CurrentUnitData, UnitSize, "\n");
PrintASCII(CurrentUnitData, UnitSize, "\n");
}
WriteUnitToFileWithPrefix({CurrentUnitData, CurrentUnitData + UnitSize},
Prefix);
}
NO_SANITIZE_MEMORY
void Fuzzer::DeathCallback() {
DumpCurrentUnit("crash-");
PrintFinalStats();
}
void Fuzzer::StaticAlarmCallback() {
assert(F);
F->AlarmCallback();
}
void Fuzzer::StaticCrashSignalCallback() {
assert(F);
F->CrashCallback();
}
void Fuzzer::StaticInterruptCallback() {
assert(F);
F->InterruptCallback();
}
void Fuzzer::CrashCallback() {
Printf("==%lu== ERROR: libFuzzer: deadly signal\n", GetPid());
if (EF->__sanitizer_print_stack_trace)
EF->__sanitizer_print_stack_trace();
Printf("NOTE: libFuzzer has rudimentary signal handlers.\n"
" Combine libFuzzer with AddressSanitizer or similar for better "
"crash reports.\n");
Printf("SUMMARY: libFuzzer: deadly signal\n");
DumpCurrentUnit("crash-");
PrintFinalStats();
exit(Options.ErrorExitCode);
}
void Fuzzer::InterruptCallback() {
Printf("==%lu== libFuzzer: run interrupted; exiting\n", GetPid());
PrintFinalStats();
_Exit(0); // Stop right now, don't perform any at-exit actions.
}
NO_SANITIZE_MEMORY
void Fuzzer::AlarmCallback() {
assert(Options.UnitTimeoutSec > 0);
if (!InFuzzingThread()) return;
if (!RunningCB)
return; // We have not started running units yet.
size_t Seconds =
duration_cast<seconds>(system_clock::now() - UnitStartTime).count();
if (Seconds == 0)
return;
if (Options.Verbosity >= 2)
Printf("AlarmCallback %zd\n", Seconds);
if (Seconds >= (size_t)Options.UnitTimeoutSec) {
Printf("ALARM: working on the last Unit for %zd seconds\n", Seconds);
Printf(" and the timeout value is %d (use -timeout=N to change)\n",
Options.UnitTimeoutSec);
DumpCurrentUnit("timeout-");
Printf("==%lu== ERROR: libFuzzer: timeout after %d seconds\n", GetPid(),
Seconds);
if (EF->__sanitizer_print_stack_trace)
EF->__sanitizer_print_stack_trace();
Printf("SUMMARY: libFuzzer: timeout\n");
PrintFinalStats();
_Exit(Options.TimeoutExitCode); // Stop right now.
}
}
void Fuzzer::RssLimitCallback() {
Printf(
"==%lu== ERROR: libFuzzer: out-of-memory (used: %zdMb; limit: %zdMb)\n",
GetPid(), GetPeakRSSMb(), Options.RssLimitMb);
Printf(" To change the out-of-memory limit use -rss_limit_mb=<N>\n\n");
if (EF->__sanitizer_print_memory_profile)
EF->__sanitizer_print_memory_profile(95);
DumpCurrentUnit("oom-");
Printf("SUMMARY: libFuzzer: out-of-memory\n");
PrintFinalStats();
_Exit(Options.ErrorExitCode); // Stop right now.
}
void Fuzzer::PrintStats(const char *Where, const char *End, size_t Units) {
size_t ExecPerSec = execPerSec();
if (Options.OutputCSV) {
static bool csvHeaderPrinted = false;
if (!csvHeaderPrinted) {
csvHeaderPrinted = true;
Printf("runs,block_cov,bits,cc_cov,corpus,execs_per_sec,tbms,reason\n");
}
Printf("%zd,%zd,%zd,%zd,%zd,%zd,%s\n", TotalNumberOfRuns,
MaxCoverage.BlockCoverage, MaxCoverage.CounterBitmapBits,
MaxCoverage.CallerCalleeCoverage, Corpus.size(), ExecPerSec, Where);
}
if (!Options.Verbosity)
return;
Printf("#%zd\t%s", TotalNumberOfRuns, Where);
if (MaxCoverage.BlockCoverage)
Printf(" cov: %zd", MaxCoverage.BlockCoverage);
if (size_t N = MaxCoverage.VPMap.GetNumBitsSinceLastMerge())
Printf(" vp: %zd", N);
if (size_t N = TPC.GetTotalPCCoverage())
Printf(" cov: %zd", N);
if (auto TB = MaxCoverage.CounterBitmapBits)
Printf(" bits: %zd", TB);
if (size_t N = Corpus.NumFeatures())
Printf( " ft: %zd", N);
if (MaxCoverage.CallerCalleeCoverage)
Printf(" indir: %zd", MaxCoverage.CallerCalleeCoverage);
if (!Corpus.empty()) {
Printf(" corp: %zd", Corpus.NumActiveUnits());
if (size_t N = Corpus.SizeInBytes()) {
if (N < (1<<14))
Printf("/%zdb", N);
else if (N < (1 << 24))
Printf("/%zdKb", N >> 10);
else
Printf("/%zdMb", N >> 20);
}
}
if (Units)
Printf(" units: %zd", Units);
Printf(" exec/s: %zd", ExecPerSec);
Printf(" rss: %zdMb", GetPeakRSSMb());
Printf("%s", End);
}
void Fuzzer::PrintFinalStats() {
if (Options.PrintCoverage)
TPC.PrintCoverage();
if (Options.DumpCoverage)
TPC.DumpCoverage();
if (Options.PrintCorpusStats)
Corpus.PrintStats();
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);
Printf("stat::new_units_added: %zd\n", NumberOfNewUnitsAdded);
Printf("stat::slowest_unit_time_sec: %zd\n", TimeOfLongestUnitInSeconds);
Printf("stat::peak_rss_mb: %zd\n", GetPeakRSSMb());
}
void Fuzzer::SetMaxInputLen(size_t MaxInputLen) {
assert(this->MaxInputLen == 0); // Can only reset MaxInputLen from 0 to non-0.
assert(MaxInputLen);
this->MaxInputLen = MaxInputLen;
this->MaxMutationLen = MaxInputLen;
AllocateCurrentUnitData();
Printf("INFO: -max_len is not provided, using %zd\n", MaxInputLen);
}
void Fuzzer::SetMaxMutationLen(size_t MaxMutationLen) {
assert(MaxMutationLen && MaxMutationLen <= MaxInputLen);
this->MaxMutationLen = 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);
if (Descr.find(Options.ExitOnSrcPos) != std::string::npos) {
Printf("INFO: found line matching '%s', exiting.\n",
Options.ExitOnSrcPos.c_str());
_Exit(0);
}
}
}
if (!Options.ExitOnItem.empty()) {
if (Corpus.HasUnit(Options.ExitOnItem)) {
Printf("INFO: found item with checksum '%s', exiting.\n",
Options.ExitOnItem.c_str());
_Exit(0);
}
}
}
void Fuzzer::RereadOutputCorpus(size_t MaxSize) {
if (Options.OutputCorpus.empty() || !Options.ReloadIntervalSec) return;
std::vector<Unit> AdditionalCorpus;
ReadDirToVectorOfUnits(Options.OutputCorpus.c_str(), &AdditionalCorpus,
&EpochOfLastReadOfOutputCorpus, MaxSize,
/*ExitOnError*/ false);
if (Options.Verbosity >= 2)
Printf("Reload: read %zd new units.\n", AdditionalCorpus.size());
bool Reloaded = false;
for (auto &U : AdditionalCorpus) {
if (U.size() > MaxSize)
U.resize(MaxSize);
if (!Corpus.HasUnit(U)) {
if (size_t NumFeatures = RunOne(U)) {
CheckExitOnSrcPosOrItem();
Corpus.AddToCorpus(U, NumFeatures);
Reloaded = true;
}
}
}
if (Reloaded)
PrintStats("RELOAD");
}
void Fuzzer::ShuffleCorpus(UnitVector *V) {
std::random_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);
if (Options.Verbosity >= 2)
Printf("NEW0: %zd L %zd\n", MaxCoverage.BlockCoverage, U.size());
}
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 Res = 0;
if (size_t NumFeatures = TPC.CollectFeatures([&](size_t Feature) -> bool {
return Corpus.AddFeature(Feature, Size, Options.Shrink);
}))
Res = NumFeatures;
if (!TPC.UsingTracePcGuard()) {
if (TPC.UpdateValueProfileMap(&MaxCoverage.VPMap))
Res = 1;
if (!Res && RecordMaxCoverage(&MaxCoverage))
Res = 1;
}
auto TimeOfUnit =
duration_cast<seconds>(UnitStopTime - UnitStartTime).count();
if (!(TotalNumberOfRuns & (TotalNumberOfRuns - 1)) &&
secondsSinceProcessStartUp() >= 2)
PrintStats("pulse ");
if (TimeOfUnit > TimeOfLongestUnitInSeconds * 1.1 &&
TimeOfUnit >= Options.ReportSlowUnits) {
TimeOfLongestUnitInSeconds = TimeOfUnit;
Printf("Slowest unit: %zd s:\n", TimeOfLongestUnitInSeconds);
WriteUnitToFileWithPrefix({Data, Data + Size}, "slow-unit-");
}
return Res;
}
size_t Fuzzer::GetCurrentUnitInFuzzingThead(const uint8_t **Data) const {
assert(InFuzzingThread());
*Data = CurrentUnitData;
return CurrentUnitSize;
}
void Fuzzer::ExecuteCallback(const uint8_t *Data, size_t Size) {
assert(InFuzzingThread());
// We copy the contents of Unit into a separate heap buffer
// so that we reliably find buffer overflows in it.
uint8_t *DataCopy = new uint8_t[Size];
memcpy(DataCopy, Data, Size);
if (CurrentUnitData && CurrentUnitData != Data)
memcpy(CurrentUnitData, Data, Size);
CurrentUnitSize = Size;
AllocTracer.Start(Options.TraceMalloc);
UnitStartTime = system_clock::now();
ResetCounters(); // Reset coverage right before the callback.
TPC.ResetMaps();
RunningCB = true;
int Res = CB(DataCopy, Size);
RunningCB = false;
UnitStopTime = system_clock::now();
(void)Res;
assert(Res == 0);
HasMoreMallocsThanFrees = AllocTracer.Stop();
CurrentUnitSize = 0;
delete[] DataCopy;
}
void Fuzzer::WriteToOutputCorpus(const Unit &U) {
if (Options.OnlyASCII)
assert(IsASCII(U));
if (Options.OutputCorpus.empty())
return;
std::string Path = DirPlusFile(Options.OutputCorpus, Hash(U));
WriteToFile(U, Path);
if (Options.Verbosity >= 2)
Printf("Written to %s\n", Path.c_str());
}
void Fuzzer::WriteUnitToFileWithPrefix(const Unit &U, const char *Prefix) {
if (!Options.SaveArtifacts)
return;
std::string Path = Options.ArtifactPrefix + Prefix + Hash(U);
if (!Options.ExactArtifactPath.empty())
Path = Options.ExactArtifactPath; // Overrides ArtifactPrefix.
WriteToFile(U, Path);
Printf("artifact_prefix='%s'; Test unit written to %s\n",
Options.ArtifactPrefix.c_str(), Path.c_str());
if (U.size() <= kMaxUnitSizeToPrint)
Printf("Base64: %s\n", Base64(U).c_str());
}
void Fuzzer::PrintStatusForNewUnit(const Unit &U) {
if (!Options.PrintNEW)
return;
PrintStats("NEW ", "");
if (Options.Verbosity) {
Printf(" L: %zd ", U.size());
MD.PrintMutationSequence();
Printf("\n");
}
}
void Fuzzer::ReportNewCoverage(InputInfo *II, const Unit &U) {
II->NumSuccessfullMutations++;
MD.RecordSuccessfulMutationSequence();
PrintStatusForNewUnit(U);
WriteToOutputCorpus(U);
NumberOfNewUnitsAdded++;
TPC.PrintNewPCs();
}
// Finds minimal number of units in 'Extra' that add coverage to 'Initial'.
// We do it by actually executing the units, sometimes more than once,
// because we may be using different coverage-like signals and the only
// common thing between them is that we can say "this unit found new stuff".
UnitVector Fuzzer::FindExtraUnits(const UnitVector &Initial,
const UnitVector &Extra) {
UnitVector Res = Extra;
UnitVector Tmp;
size_t OldSize = Res.size();
for (int Iter = 0; Iter < 10; Iter++) {
ShuffleCorpus(&Res);
TPC.ResetMaps();
Corpus.ResetFeatureSet();
ResetCoverage();
for (auto &U : Initial) {
TPC.ResetMaps();
RunOne(U);
}
Tmp.clear();
for (auto &U : Res) {
TPC.ResetMaps();
if (RunOne(U))
Tmp.push_back(U);
}
char Stat[7] = "MIN ";
Stat[3] = '0' + Iter;
PrintStats(Stat, "\n", Tmp.size());
size_t NewSize = Tmp.size();
assert(NewSize <= OldSize);
Res.swap(Tmp);
if (NewSize + 5 >= OldSize)
break;
OldSize = NewSize;
}
return Res;
}
void Fuzzer::Merge(const std::vector<std::string> &Corpora) {
if (Corpora.size() <= 1) {
Printf("Merge requires two or more corpus dirs\n");
return;
}
InMergeMode = true;
std::vector<std::string> ExtraCorpora(Corpora.begin() + 1, Corpora.end());
assert(MaxInputLen > 0);
UnitVector Initial, Extra;
ReadDirToVectorOfUnits(Corpora[0].c_str(), &Initial, nullptr, MaxInputLen,
true);
for (auto &C : ExtraCorpora)
ReadDirToVectorOfUnits(C.c_str(), &Extra, nullptr, MaxInputLen, true);
if (!Initial.empty()) {
Printf("=== Minimizing the initial corpus of %zd units\n", Initial.size());
Initial = FindExtraUnits({}, Initial);
}
Printf("=== Merging extra %zd units\n", Extra.size());
auto Res = FindExtraUnits(Initial, Extra);
for (auto &U: Res)
WriteToOutputCorpus(U);
Printf("=== Merge: written %zd units\n", Res.size());
}
// 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 (!&(EF->__lsan_enable) || !&(EF->__lsan_disable) ||
!(EF->__lsan_do_recoverable_leak_check))
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 (NumberOfLeakDetectionAttempts++ > 1000) {
Options.DetectLeaks = false;
Printf("INFO: libFuzzer disabled leak detection after every mutation.\n"
" Most likely the target function accumulates allocated\n"
" memory in a global state w/o actually leaking it.\n"
" You may try running this binary with -trace_malloc=[12]"
" to get a trace of mallocs and frees.\n"
" If LeakSanitizer is enabled in this process it will still\n"
" run on the process shutdown.\n");
return;
}
// Now perform the actual lsan pass. This is expensive and we must ensure
// we don't call it too often.
if (EF->__lsan_do_recoverable_leak_check()) { // Leak is found, report it.
if (DuringInitialCorpusExecution)
Printf("\nINFO: a leak has been found in the initial corpus.\n\n");
Printf("INFO: to ignore leaks on libFuzzer side use -detect_leaks=0.\n\n");
CurrentUnitSize = Size;
DumpCurrentUnit("leak-");
PrintFinalStats();
_Exit(Options.ErrorExitCode); // not exit() to disable lsan further on.
}
}
void Fuzzer::MutateAndTestOne() {
MD.StartMutationSequence();
auto &II = Corpus.ChooseUnitToMutate(MD.GetRand());
const auto &U = II.U;
memcpy(BaseSha1, II.Sha1, sizeof(BaseSha1));
assert(CurrentUnitData);
size_t Size = U.size();
assert(Size <= MaxInputLen && "Oversized Unit");
memcpy(CurrentUnitData, U.data(), Size);
assert(MaxMutationLen > 0);
for (int i = 0; i < Options.MutateDepth; i++) {
if (TotalNumberOfRuns >= Options.MaxNumberOfRuns)
break;
size_t NewSize = 0;
NewSize = MD.Mutate(CurrentUnitData, Size, MaxMutationLen);
assert(NewSize > 0 && "Mutator returned empty unit");
assert(NewSize <= MaxMutationLen && "Mutator return overisized 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();
TryDetectingAMemoryLeak(CurrentUnitData, Size,
/*DuringInitialCorpusExecution*/ false);
}
}
void Fuzzer::ResetCoverage() {
ResetEdgeCoverage();
MaxCoverage.Reset();
PrepareCounters(&MaxCoverage);
}
void Fuzzer::Loop() {
system_clock::time_point LastCorpusReload = system_clock::now();
if (Options.DoCrossOver)
MD.SetCorpus(&Corpus);
while (true) {
auto Now = system_clock::now();
if (duration_cast<seconds>(Now - LastCorpusReload).count() >=
Options.ReloadIntervalSec) {
RereadOutputCorpus(MaxInputLen);
LastCorpusReload = system_clock::now();
}
if (TotalNumberOfRuns >= Options.MaxNumberOfRuns)
break;
if (TimedOut()) break;
// Perform several mutations and runs.
MutateAndTestOne();
}
PrintStats("DONE ", "\n");
MD.PrintRecommendedDictionary();
}
void Fuzzer::MinimizeCrashLoop(const Unit &U) {
if (U.size() <= 2) 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);
TryDetectingAMemoryLeak(CurrentUnitData, NewSize,
/*DuringInitialCorpusExecution*/ false);
}
}
}
} // namespace fuzzer
extern "C" {
size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize) {
assert(fuzzer::F);
return fuzzer::F->GetMD().DefaultMutate(Data, Size, MaxSize);
}
} // extern "C"

View file

@ -0,0 +1,21 @@
//===- FuzzerMain.cpp - main() function and flags -------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
// main() and flags.
//===----------------------------------------------------------------------===//
#include "FuzzerDefs.h"
extern "C" {
// This function should be defined by the user.
int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size);
} // extern "C"
int main(int argc, char **argv) {
return fuzzer::FuzzerDriver(&argc, &argv, LLVMFuzzerTestOneInput);
}

View file

@ -0,0 +1,261 @@
//===- FuzzerMerge.cpp - merging corpora ----------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
// Merging corpora.
//===----------------------------------------------------------------------===//
#include "FuzzerInternal.h"
#include "FuzzerIO.h"
#include "FuzzerMerge.h"
#include "FuzzerTracePC.h"
#include "FuzzerUtil.h"
#include <fstream>
#include <iterator>
#include <sstream>
namespace fuzzer {
bool Merger::Parse(const std::string &Str, bool ParseCoverage) {
std::istringstream SS(Str);
return Parse(SS, ParseCoverage);
}
void Merger::ParseOrExit(std::istream &IS, bool ParseCoverage) {
if (!Parse(IS, ParseCoverage)) {
Printf("MERGE: failed to parse the control file (unexpected error)\n");
exit(1);
}
}
// The control file example:
//
// 3 # The number of inputs
// 1 # The number of inputs in the first corpus, <= the previous number
// file0
// file1
// file2 # One file name per line.
// STARTED 0 123 # FileID, file size
// DONE 0 1 4 6 8 # FileID COV1 COV2 ...
// STARTED 1 456 # If DONE is missing, the input crashed while processing.
// STARTED 2 567
// DONE 2 8 9
bool Merger::Parse(std::istream &IS, bool ParseCoverage) {
LastFailure.clear();
std::string Line;
// Parse NumFiles.
if (!std::getline(IS, Line, '\n')) return false;
std::istringstream L1(Line);
size_t NumFiles = 0;
L1 >> NumFiles;
if (NumFiles == 0 || NumFiles > 10000000) return false;
// Parse NumFilesInFirstCorpus.
if (!std::getline(IS, Line, '\n')) return false;
std::istringstream L2(Line);
NumFilesInFirstCorpus = NumFiles + 1;
L2 >> NumFilesInFirstCorpus;
if (NumFilesInFirstCorpus > NumFiles) return false;
// Parse file names.
Files.resize(NumFiles);
for (size_t i = 0; i < NumFiles; i++)
if (!std::getline(IS, Files[i].Name, '\n'))
return false;
// Parse STARTED and DONE lines.
size_t ExpectedStartMarker = 0;
const size_t kInvalidStartMarker = -1;
size_t LastSeenStartMarker = kInvalidStartMarker;
while (std::getline(IS, Line, '\n')) {
std::istringstream ISS1(Line);
std::string Marker;
size_t N;
ISS1 >> Marker;
ISS1 >> N;
if (Marker == "STARTED") {
// STARTED FILE_ID FILE_SIZE
if (ExpectedStartMarker != N)
return false;
ISS1 >> Files[ExpectedStartMarker].Size;
LastSeenStartMarker = ExpectedStartMarker;
assert(ExpectedStartMarker < Files.size());
ExpectedStartMarker++;
} else if (Marker == "DONE") {
// DONE FILE_SIZE COV1 COV2 COV3 ...
size_t CurrentFileIdx = N;
if (CurrentFileIdx != LastSeenStartMarker)
return false;
LastSeenStartMarker = kInvalidStartMarker;
if (ParseCoverage) {
auto &V = Files[CurrentFileIdx].Features;
V.clear();
while (ISS1 >> std::hex >> N)
V.push_back(N);
std::sort(V.begin(), V.end());
}
} else {
return false;
}
}
if (LastSeenStartMarker != kInvalidStartMarker)
LastFailure = Files[LastSeenStartMarker].Name;
FirstNotProcessedFile = ExpectedStartMarker;
return true;
}
// Decides which files need to be merged (add thost to NewFiles).
// Returns the number of new features added.
size_t Merger::Merge(std::vector<std::string> *NewFiles) {
NewFiles->clear();
assert(NumFilesInFirstCorpus <= Files.size());
std::set<uint32_t> AllFeatures;
// What features are in the initial corpus?
for (size_t i = 0; i < NumFilesInFirstCorpus; i++) {
auto &Cur = Files[i].Features;
AllFeatures.insert(Cur.begin(), Cur.end());
}
size_t InitialNumFeatures = AllFeatures.size();
// 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;
std::set_difference(Cur.begin(), Cur.end(), AllFeatures.begin(),
AllFeatures.end(), std::inserter(Tmp, Tmp.begin()));
Cur.swap(Tmp);
}
// Sort. Give preference to
// * smaller files
// * files with more features.
std::sort(Files.begin() + NumFilesInFirstCorpus, Files.end(),
[&](const MergeFileInfo &a, const MergeFileInfo &b) -> bool {
if (a.Size != b.Size)
return a.Size < b.Size;
return a.Features.size() > b.Features.size();
});
// One greedy pass: add the file's features to AllFeatures.
// If new features were added, add this file to NewFiles.
for (size_t i = NumFilesInFirstCorpus; i < Files.size(); i++) {
auto &Cur = Files[i].Features;
// Printf("%s -> sz %zd ft %zd\n", Files[i].Name.c_str(),
// Files[i].Size, Cur.size());
size_t OldSize = AllFeatures.size();
AllFeatures.insert(Cur.begin(), Cur.end());
if (AllFeatures.size() > OldSize)
NewFiles->push_back(Files[i].Name);
}
return AllFeatures.size() - InitialNumFeatures;
}
// Inner process. May crash if the target crashes.
void Fuzzer::CrashResistantMergeInternalStep(const std::string &CFPath) {
Printf("MERGE-INNER: using the control file '%s'\n", CFPath.c_str());
Merger M;
std::ifstream IF(CFPath);
M.ParseOrExit(IF, false);
IF.close();
if (!M.LastFailure.empty())
Printf("MERGE-INNER: '%s' caused a failure at the previous merge step\n",
M.LastFailure.c_str());
Printf("MERGE-INNER: %zd total files;"
" %zd processed earlier; will process %zd files now\n",
M.Files.size(), M.FirstNotProcessedFile,
M.Files.size() - M.FirstNotProcessedFile);
std::ofstream OF(CFPath, std::ofstream::out | std::ofstream::app);
for (size_t i = M.FirstNotProcessedFile; i < M.Files.size(); i++) {
auto U = FileToVector(M.Files[i].Name);
if (U.size() > MaxInputLen) {
U.resize(MaxInputLen);
U.shrink_to_fit();
}
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.
// 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;
});
// 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)
OF << " " << std::hex << F;
OF << "\n";
}
}
// 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) {
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);
std::string CFPath =
"libFuzzerTemp." + std::to_string(GetPid()) + ".txt";
// Write the control file.
RemoveFile(CFPath);
std::ofstream ControlFile(CFPath);
ControlFile << AllFiles.size() << "\n";
ControlFile << NumFilesInFirstCorpus << "\n";
for (auto &Path: AllFiles)
ControlFile << Path << "\n";
ControlFile.close();
// Execute the inner process untill it passes.
// Every inner process should execute at least one input.
std::string BaseCmd = CloneArgsWithoutX(Args, "keep-all-flags");
for (size_t i = 1; i <= AllFiles.size(); i++) {
Printf("MERGE-OUTER: attempt %zd\n", i);
auto ExitCode =
ExecuteCommand(BaseCmd + " -merge_control_file=" + CFPath);
if (!ExitCode) {
Printf("MERGE-OUTER: succesfull in %zd attempt(s)\n", i);
break;
}
}
// Read the control file and do the merge.
Merger M;
std::ifstream IF(CFPath);
M.ParseOrExit(IF, true);
IF.close();
std::vector<std::string> NewFiles;
size_t NumNewFeatures = M.Merge(&NewFiles);
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);
}
} // namespace fuzzer

View file

@ -0,0 +1,70 @@
//===- FuzzerMerge.h - merging corpa ----------------------------*- C++ -* ===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
// Merging Corpora.
//
// The task:
// Take the existing corpus (possibly empty) and merge new inputs into
// it so that only inputs with new coverage ('features') are added.
// The process should tolerate the crashes, OOMs, leaks, etc.
//
// Algorithm:
// The outter process collects the set of files and writes their names
// into a temporary "control" file, then repeatedly launches the inner
// process until all inputs are processed.
// The outer process does not actually execute the target code.
//
// The inner process reads the control file and sees a) list of all the inputs
// and b) the last processed input. Then it starts processing the inputs one
// by one. Before processing every input it writes one line to control file:
// STARTED INPUT_ID INPUT_SIZE
// After processing an input it write another line:
// DONE INPUT_ID Feature1 Feature2 Feature3 ...
// If a crash happens while processing an input the last line in the control
// file will be "STARTED INPUT_ID" and so the next process will know
// where to resume.
//
// Once all inputs are processed by the innner process(es) the outer process
// reads the control files and does the merge based entirely on the contents
// of control file.
// It uses a single pass greedy algorithm choosing first the smallest inputs
// within the same size the inputs that have more new features.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_FUZZER_MERGE_H
#define LLVM_FUZZER_MERGE_H
#include "FuzzerDefs.h"
#include <istream>
#include <set>
namespace fuzzer {
struct MergeFileInfo {
std::string Name;
size_t Size = 0;
std::vector<uint32_t> Features;
};
struct Merger {
std::vector<MergeFileInfo> Files;
size_t NumFilesInFirstCorpus = 0;
size_t FirstNotProcessedFile = 0;
std::string LastFailure;
bool Parse(std::istream &IS, bool ParseCoverage);
bool Parse(const std::string &Str, bool ParseCoverage);
void ParseOrExit(std::istream &IS, bool ParseCoverage);
size_t Merge(std::vector<std::string> *NewFiles);
};
} // namespace fuzzer
#endif // LLVM_FUZZER_MERGE_H

View file

@ -0,0 +1,527 @@
//===- FuzzerMutate.cpp - Mutate a test input -----------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
// Mutate a test input.
//===----------------------------------------------------------------------===//
#include "FuzzerCorpus.h"
#include "FuzzerDefs.h"
#include "FuzzerExtFunctions.h"
#include "FuzzerIO.h"
#include "FuzzerMutate.h"
#include "FuzzerOptions.h"
namespace fuzzer {
const size_t Dictionary::kMaxDictSize;
static void PrintASCII(const Word &W, const char *PrintAfter) {
PrintASCII(W.data(), W.size(), PrintAfter);
}
MutationDispatcher::MutationDispatcher(Random &Rand,
const FuzzingOptions &Options)
: Rand(Rand), Options(Options) {
DefaultMutators.insert(
DefaultMutators.begin(),
{
{&MutationDispatcher::Mutate_EraseBytes, "EraseBytes"},
{&MutationDispatcher::Mutate_InsertByte, "InsertByte"},
{&MutationDispatcher::Mutate_InsertRepeatedBytes,
"InsertRepeatedBytes"},
{&MutationDispatcher::Mutate_ChangeByte, "ChangeByte"},
{&MutationDispatcher::Mutate_ChangeBit, "ChangeBit"},
{&MutationDispatcher::Mutate_ShuffleBytes, "ShuffleBytes"},
{&MutationDispatcher::Mutate_ChangeASCIIInteger, "ChangeASCIIInt"},
{&MutationDispatcher::Mutate_ChangeBinaryInteger, "ChangeBinInt"},
{&MutationDispatcher::Mutate_CopyPart, "CopyPart"},
{&MutationDispatcher::Mutate_CrossOver, "CrossOver"},
{&MutationDispatcher::Mutate_AddWordFromManualDictionary,
"ManualDict"},
{&MutationDispatcher::Mutate_AddWordFromTemporaryAutoDictionary,
"TempAutoDict"},
{&MutationDispatcher::Mutate_AddWordFromPersistentAutoDictionary,
"PersAutoDict"},
});
if(Options.UseCmp)
DefaultMutators.push_back(
{&MutationDispatcher::Mutate_AddWordFromTORC, "CMP"});
if (EF->LLVMFuzzerCustomMutator)
Mutators.push_back({&MutationDispatcher::Mutate_Custom, "Custom"});
else
Mutators = DefaultMutators;
if (EF->LLVMFuzzerCustomCrossOver)
Mutators.push_back(
{&MutationDispatcher::Mutate_CustomCrossOver, "CustomCrossOver"});
}
static char RandCh(Random &Rand) {
if (Rand.RandBool()) return Rand(256);
const char *Special = "!*'();:@&=+$,/?%#[]012Az-`~.\xff\x00";
return Special[Rand(sizeof(Special) - 1)];
}
size_t MutationDispatcher::Mutate_Custom(uint8_t *Data, size_t Size,
size_t MaxSize) {
return EF->LLVMFuzzerCustomMutator(Data, Size, MaxSize, Rand.Rand());
}
size_t MutationDispatcher::Mutate_CustomCrossOver(uint8_t *Data, size_t Size,
size_t MaxSize) {
if (!Corpus || Corpus->size() < 2 || Size == 0)
return 0;
size_t Idx = Rand(Corpus->size());
const Unit &Other = (*Corpus)[Idx];
if (Other.empty())
return 0;
MutateInPlaceHere.resize(MaxSize);
auto &U = MutateInPlaceHere;
size_t NewSize = EF->LLVMFuzzerCustomCrossOver(
Data, Size, Other.data(), Other.size(), U.data(), U.size(), Rand.Rand());
if (!NewSize)
return 0;
assert(NewSize <= MaxSize && "CustomCrossOver returned overisized unit");
memcpy(Data, U.data(), NewSize);
return NewSize;
}
size_t MutationDispatcher::Mutate_ShuffleBytes(uint8_t *Data, size_t Size,
size_t MaxSize) {
if (Size > MaxSize) return 0;
assert(Size);
size_t ShuffleAmount =
Rand(std::min(Size, (size_t)8)) + 1; // [1,8] and <= Size.
size_t ShuffleStart = Rand(Size - ShuffleAmount);
assert(ShuffleStart + ShuffleAmount <= Size);
std::random_shuffle(Data + ShuffleStart, Data + ShuffleStart + ShuffleAmount,
Rand);
return Size;
}
size_t MutationDispatcher::Mutate_EraseBytes(uint8_t *Data, size_t Size,
size_t MaxSize) {
assert(Size);
if (Size == 1) return 0;
size_t N = Rand(Size / 2) + 1;
assert(N < Size);
size_t Idx = Rand(Size - N + 1);
// Erase Data[Idx:Idx+N].
memmove(Data + Idx, Data + Idx + N, Size - Idx - N);
// Printf("Erase: %zd %zd => %zd; Idx %zd\n", N, Size, Size - N, Idx);
return Size - N;
}
size_t MutationDispatcher::Mutate_InsertByte(uint8_t *Data, size_t Size,
size_t MaxSize) {
if (Size >= MaxSize) return 0;
size_t Idx = Rand(Size + 1);
// Insert new value at Data[Idx].
memmove(Data + Idx + 1, Data + Idx, Size - Idx);
Data[Idx] = RandCh(Rand);
return Size + 1;
}
size_t MutationDispatcher::Mutate_InsertRepeatedBytes(uint8_t *Data,
size_t Size,
size_t MaxSize) {
const size_t kMinBytesToInsert = 3;
if (Size + kMinBytesToInsert >= MaxSize) return 0;
size_t MaxBytesToInsert = std::min(MaxSize - Size, (size_t)128);
size_t N = Rand(MaxBytesToInsert - kMinBytesToInsert + 1) + kMinBytesToInsert;
assert(Size + N <= MaxSize && N);
size_t Idx = Rand(Size + 1);
// Insert new values at Data[Idx].
memmove(Data + Idx + N, Data + Idx, Size - Idx);
// Give preference to 0x00 and 0xff.
uint8_t Byte = Rand.RandBool() ? Rand(256) : (Rand.RandBool() ? 0 : 255);
for (size_t i = 0; i < N; i++)
Data[Idx + i] = Byte;
return Size + N;
}
size_t MutationDispatcher::Mutate_ChangeByte(uint8_t *Data, size_t Size,
size_t MaxSize) {
if (Size > MaxSize) return 0;
size_t Idx = Rand(Size);
Data[Idx] = RandCh(Rand);
return Size;
}
size_t MutationDispatcher::Mutate_ChangeBit(uint8_t *Data, size_t Size,
size_t MaxSize) {
if (Size > MaxSize) return 0;
size_t Idx = Rand(Size);
Data[Idx] ^= 1 << Rand(8);
return Size;
}
size_t MutationDispatcher::Mutate_AddWordFromManualDictionary(uint8_t *Data,
size_t Size,
size_t MaxSize) {
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) {
const Word &W = DE.GetW();
bool UsePositionHint = DE.HasPositionHint() &&
DE.GetPositionHint() + W.size() < Size &&
Rand.RandBool();
if (Rand.RandBool()) { // Insert W.
if (Size + W.size() > MaxSize) return 0;
size_t Idx = UsePositionHint ? DE.GetPositionHint() : Rand(Size + 1);
memmove(Data + Idx + W.size(), Data + Idx, Size - Idx);
memcpy(Data + Idx, W.data(), W.size());
Size += W.size();
} else { // Overwrite some bytes with W.
if (W.size() > Size) return 0;
size_t Idx = UsePositionHint ? DE.GetPositionHint() : Rand(Size - W.size());
memcpy(Data + Idx, W.data(), W.size());
}
return Size;
}
// Somewhere in the past we have observed a comparison instructions
// with arguments Arg1 Arg2. This function tries to guess a dictionary
// entry that will satisfy that comparison.
// It first tries to find one of the arguments (possibly swapped) in the
// input and if it succeeds it creates a DE with a position hint.
// Otherwise it creates a DE with one of the arguments w/o a position hint.
template <class T>
DictionaryEntry MutationDispatcher::MakeDictionaryEntryFromCMP(
T Arg1, T Arg2, const uint8_t *Data, size_t Size) {
ScopedDoingMyOwnMemmem scoped_doing_my_own_memmem;
bool HandleFirst = Rand.RandBool();
T ExistingBytes, DesiredBytes;
Word W;
const uint8_t *End = Data + Size;
for (int Arg = 0; Arg < 2; Arg++) {
ExistingBytes = HandleFirst ? Arg1 : Arg2;
DesiredBytes = HandleFirst ? Arg2 : Arg1;
DesiredBytes += Rand(-1, 1);
if (Rand.RandBool()) ExistingBytes = Bswap(ExistingBytes);
if (Rand.RandBool()) DesiredBytes = Bswap(DesiredBytes);
HandleFirst = !HandleFirst;
W.Set(reinterpret_cast<uint8_t*>(&DesiredBytes), sizeof(T));
const size_t kMaxNumPositions = 8;
size_t Positions[kMaxNumPositions];
size_t NumPositions = 0;
for (const uint8_t *Cur = Data;
Cur < End && NumPositions < kMaxNumPositions; Cur++) {
Cur = (uint8_t *)SearchMemory(Cur, End - Cur, &ExistingBytes, sizeof(T));
if (!Cur) break;
Positions[NumPositions++] = Cur - Data;
}
if (!NumPositions) break;
return DictionaryEntry(W, Positions[Rand(NumPositions)]);
}
DictionaryEntry DE(W);
return DE;
}
size_t MutationDispatcher::Mutate_AddWordFromTORC(
uint8_t *Data, size_t Size, size_t MaxSize) {
Word W;
DictionaryEntry DE;
if (Rand.RandBool()) {
auto X = TPC.TORC8.Get(Rand.Rand());
DE = MakeDictionaryEntryFromCMP(X.A, X.B, Data, Size);
} else {
auto X = TPC.TORC4.Get(Rand.Rand());
if ((X.A >> 16) == 0 && (X.B >> 16) == 0 && Rand.RandBool())
DE = MakeDictionaryEntryFromCMP((uint16_t)X.A, (uint16_t)X.B, Data,
Size);
else
DE = MakeDictionaryEntryFromCMP(X.A, X.B, Data, Size);
}
Size = ApplyDictionaryEntry(Data, Size, MaxSize, DE);
if (!Size) return 0;
DictionaryEntry &DERef =
CmpDictionaryEntriesDeque[CmpDictionaryEntriesDequeIdx++ %
kCmpDictionaryEntriesDequeSize];
DERef = DE;
CurrentDictionaryEntrySequence.push_back(&DERef);
return Size;
}
size_t MutationDispatcher::Mutate_AddWordFromPersistentAutoDictionary(
uint8_t *Data, size_t Size, size_t MaxSize) {
return AddWordFromDictionary(PersistentAutoDictionary, Data, Size, MaxSize);
}
size_t MutationDispatcher::AddWordFromDictionary(Dictionary &D, uint8_t *Data,
size_t Size, size_t MaxSize) {
if (Size > MaxSize) return 0;
if (D.empty()) return 0;
DictionaryEntry &DE = D[Rand(D.size())];
Size = ApplyDictionaryEntry(Data, Size, MaxSize, DE);
if (!Size) return 0;
DE.IncUseCount();
CurrentDictionaryEntrySequence.push_back(&DE);
return Size;
}
// Overwrites part of To[0,ToSize) with a part of From[0,FromSize).
// Returns ToSize.
size_t MutationDispatcher::CopyPartOf(const uint8_t *From, size_t FromSize,
uint8_t *To, size_t ToSize) {
// Copy From[FromBeg, FromBeg + CopySize) into To[ToBeg, ToBeg + CopySize).
size_t ToBeg = Rand(ToSize);
size_t CopySize = Rand(ToSize - ToBeg) + 1;
assert(ToBeg + CopySize <= ToSize);
CopySize = std::min(CopySize, FromSize);
size_t FromBeg = Rand(FromSize - CopySize + 1);
assert(FromBeg + CopySize <= FromSize);
memmove(To + ToBeg, From + FromBeg, CopySize);
return ToSize;
}
// Inserts part of From[0,ToSize) into To.
// Returns new size of To on success or 0 on failure.
size_t MutationDispatcher::InsertPartOf(const uint8_t *From, size_t FromSize,
uint8_t *To, size_t ToSize,
size_t MaxToSize) {
if (ToSize >= MaxToSize) return 0;
size_t AvailableSpace = MaxToSize - ToSize;
size_t MaxCopySize = std::min(AvailableSpace, FromSize);
size_t CopySize = Rand(MaxCopySize) + 1;
size_t FromBeg = Rand(FromSize - CopySize + 1);
assert(FromBeg + CopySize <= FromSize);
size_t ToInsertPos = Rand(ToSize + 1);
assert(ToInsertPos + CopySize <= MaxToSize);
size_t TailSize = ToSize - ToInsertPos;
if (To == From) {
MutateInPlaceHere.resize(MaxToSize);
memcpy(MutateInPlaceHere.data(), From + FromBeg, CopySize);
memmove(To + ToInsertPos + CopySize, To + ToInsertPos, TailSize);
memmove(To + ToInsertPos, MutateInPlaceHere.data(), CopySize);
} else {
memmove(To + ToInsertPos + CopySize, To + ToInsertPos, TailSize);
memmove(To + ToInsertPos, From + FromBeg, CopySize);
}
return ToSize + CopySize;
}
size_t MutationDispatcher::Mutate_CopyPart(uint8_t *Data, size_t Size,
size_t MaxSize) {
if (Size > MaxSize) return 0;
if (Rand.RandBool())
return CopyPartOf(Data, Size, Data, Size);
else
return InsertPartOf(Data, Size, Data, Size, MaxSize);
}
size_t MutationDispatcher::Mutate_ChangeASCIIInteger(uint8_t *Data, size_t Size,
size_t MaxSize) {
if (Size > MaxSize) return 0;
size_t B = Rand(Size);
while (B < Size && !isdigit(Data[B])) B++;
if (B == Size) return 0;
size_t E = B;
while (E < Size && isdigit(Data[E])) E++;
assert(B < E);
// now we have digits in [B, E).
// strtol and friends don't accept non-zero-teminated data, parse it manually.
uint64_t Val = Data[B] - '0';
for (size_t i = B + 1; i < E; i++)
Val = Val * 10 + Data[i] - '0';
// Mutate the integer value.
switch(Rand(5)) {
case 0: Val++; break;
case 1: Val--; break;
case 2: Val /= 2; break;
case 3: Val *= 2; break;
case 4: Val = Rand(Val * Val); break;
default: assert(0);
}
// Just replace the bytes with the new ones, don't bother moving bytes.
for (size_t i = B; i < E; i++) {
size_t Idx = E + B - i - 1;
assert(Idx >= B && Idx < E);
Data[Idx] = (Val % 10) + '0';
Val /= 10;
}
return Size;
}
template<class T>
size_t ChangeBinaryInteger(uint8_t *Data, size_t Size, Random &Rand) {
if (Size < sizeof(T)) return 0;
size_t Off = Rand(Size - sizeof(T) + 1);
assert(Off + sizeof(T) <= Size);
T Val;
if (Off < 64 && !Rand(4)) {
Val = Size;
if (Rand.RandBool())
Val = Bswap(Val);
} else {
memcpy(&Val, Data + Off, sizeof(Val));
T Add = Rand(21);
Add -= 10;
if (Rand.RandBool())
Val = Bswap(T(Bswap(Val) + Add)); // Add assuming different endiannes.
else
Val = Val + Add; // Add assuming current endiannes.
if (Add == 0 || Rand.RandBool()) // Maybe negate.
Val = -Val;
}
memcpy(Data + Off, &Val, sizeof(Val));
return Size;
}
size_t MutationDispatcher::Mutate_ChangeBinaryInteger(uint8_t *Data,
size_t Size,
size_t MaxSize) {
if (Size > MaxSize) return 0;
switch (Rand(4)) {
case 3: return ChangeBinaryInteger<uint64_t>(Data, Size, Rand);
case 2: return ChangeBinaryInteger<uint32_t>(Data, Size, Rand);
case 1: return ChangeBinaryInteger<uint16_t>(Data, Size, Rand);
case 0: return ChangeBinaryInteger<uint8_t>(Data, Size, Rand);
default: assert(0);
}
return 0;
}
size_t MutationDispatcher::Mutate_CrossOver(uint8_t *Data, size_t Size,
size_t MaxSize) {
if (Size > MaxSize) return 0;
if (!Corpus || Corpus->size() < 2 || Size == 0) return 0;
size_t Idx = Rand(Corpus->size());
const Unit &O = (*Corpus)[Idx];
if (O.empty()) return 0;
MutateInPlaceHere.resize(MaxSize);
auto &U = MutateInPlaceHere;
size_t NewSize = 0;
switch(Rand(3)) {
case 0:
NewSize = CrossOver(Data, Size, O.data(), O.size(), U.data(), U.size());
break;
case 1:
NewSize = InsertPartOf(O.data(), O.size(), U.data(), U.size(), MaxSize);
if (NewSize)
break;
// LLVM_FALLTHROUGH;
case 2:
NewSize = CopyPartOf(O.data(), O.size(), U.data(), U.size());
break;
default: assert(0);
}
assert(NewSize > 0 && "CrossOver returned empty unit");
assert(NewSize <= MaxSize && "CrossOver returned overisized unit");
memcpy(Data, U.data(), NewSize);
return NewSize;
}
void MutationDispatcher::StartMutationSequence() {
CurrentMutatorSequence.clear();
CurrentDictionaryEntrySequence.clear();
}
// Copy successful dictionary entries to PersistentAutoDictionary.
void MutationDispatcher::RecordSuccessfulMutationSequence() {
for (auto DE : CurrentDictionaryEntrySequence) {
// PersistentAutoDictionary.AddWithSuccessCountOne(DE);
DE->IncSuccessCount();
// Linear search is fine here as this happens seldom.
if (!PersistentAutoDictionary.ContainsWord(DE->GetW()))
PersistentAutoDictionary.push_back({DE->GetW(), 1});
}
}
void MutationDispatcher::PrintRecommendedDictionary() {
std::vector<DictionaryEntry> V;
for (auto &DE : PersistentAutoDictionary)
if (!ManualDictionary.ContainsWord(DE.GetW()))
V.push_back(DE);
if (V.empty()) return;
Printf("###### Recommended dictionary. ######\n");
for (auto &DE: V) {
Printf("\"");
PrintASCII(DE.GetW(), "\"");
Printf(" # Uses: %zd\n", DE.GetUseCount());
}
Printf("###### End of recommended dictionary. ######\n");
}
void MutationDispatcher::PrintMutationSequence() {
Printf("MS: %zd ", CurrentMutatorSequence.size());
for (auto M : CurrentMutatorSequence)
Printf("%s-", M.Name);
if (!CurrentDictionaryEntrySequence.empty()) {
Printf(" DE: ");
for (auto DE : CurrentDictionaryEntrySequence) {
Printf("\"");
PrintASCII(DE->GetW(), "\"-");
}
}
}
size_t MutationDispatcher::Mutate(uint8_t *Data, size_t Size, size_t MaxSize) {
return MutateImpl(Data, Size, MaxSize, Mutators);
}
size_t MutationDispatcher::DefaultMutate(uint8_t *Data, size_t Size,
size_t MaxSize) {
return MutateImpl(Data, Size, MaxSize, DefaultMutators);
}
// 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) {
assert(MaxSize > 0);
if (Size == 0) {
for (size_t i = 0; i < MaxSize; i++)
Data[i] = RandCh(Rand);
if (Options.OnlyASCII)
ToASCII(Data, MaxSize);
return MaxSize;
}
assert(Size > 0);
// Some mutations may fail (e.g. can't insert more bytes if Size == MaxSize),
// in which case they will return 0.
// Try several times before returning un-mutated data.
for (int Iter = 0; Iter < 100; Iter++) {
auto M = Mutators[Rand(Mutators.size())];
size_t NewSize = (this->*(M.Fn))(Data, Size, MaxSize);
if (NewSize && NewSize <= MaxSize) {
if (Options.OnlyASCII)
ToASCII(Data, NewSize);
CurrentMutatorSequence.push_back(M);
return NewSize;
}
}
return std::min(Size, MaxSize);
}
void MutationDispatcher::AddWordToManualDictionary(const Word &W) {
ManualDictionary.push_back(
{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

View file

@ -0,0 +1,145 @@
//===- FuzzerMutate.h - Internal header for the Fuzzer ----------*- C++ -* ===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
// fuzzer::MutationDispatcher
//===----------------------------------------------------------------------===//
#ifndef LLVM_FUZZER_MUTATE_H
#define LLVM_FUZZER_MUTATE_H
#include "FuzzerDefs.h"
#include "FuzzerDictionary.h"
#include "FuzzerRandom.h"
namespace fuzzer {
class MutationDispatcher {
public:
MutationDispatcher(Random &Rand, const FuzzingOptions &Options);
~MutationDispatcher() {}
/// Indicate that we are about to start a new sequence of mutations.
void StartMutationSequence();
/// Print the current sequence of mutations.
void PrintMutationSequence();
/// Indicate that the current sequence of mutations was successfull.
void RecordSuccessfulMutationSequence();
/// Mutates data by invoking user-provided mutator.
size_t Mutate_Custom(uint8_t *Data, size_t Size, size_t MaxSize);
/// Mutates data by invoking user-provided crossover.
size_t Mutate_CustomCrossOver(uint8_t *Data, size_t Size, size_t MaxSize);
/// Mutates data by shuffling bytes.
size_t Mutate_ShuffleBytes(uint8_t *Data, size_t Size, size_t MaxSize);
/// Mutates data by erasing bytes.
size_t Mutate_EraseBytes(uint8_t *Data, size_t Size, size_t MaxSize);
/// Mutates data by inserting a byte.
size_t Mutate_InsertByte(uint8_t *Data, size_t Size, size_t MaxSize);
/// Mutates data by inserting several repeated bytes.
size_t Mutate_InsertRepeatedBytes(uint8_t *Data, size_t Size, size_t MaxSize);
/// Mutates data by chanding one byte.
size_t Mutate_ChangeByte(uint8_t *Data, size_t Size, size_t MaxSize);
/// Mutates data by chanding one bit.
size_t Mutate_ChangeBit(uint8_t *Data, size_t Size, size_t MaxSize);
/// Mutates data by copying/inserting a part of data into a different place.
size_t Mutate_CopyPart(uint8_t *Data, size_t Size, size_t MaxSize);
/// Mutates data by adding a word from the manual dictionary.
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);
/// Mutates data by adding a word from the persistent automatic dictionary.
size_t Mutate_AddWordFromPersistentAutoDictionary(uint8_t *Data, size_t Size,
size_t MaxSize);
/// Tries to find an ASCII integer in Data, changes it to another ASCII int.
size_t Mutate_ChangeASCIIInteger(uint8_t *Data, size_t Size, size_t MaxSize);
/// Change a 1-, 2-, 4-, or 8-byte integer in interesting ways.
size_t Mutate_ChangeBinaryInteger(uint8_t *Data, size_t Size, size_t MaxSize);
/// CrossOver Data with some other element of the corpus.
size_t Mutate_CrossOver(uint8_t *Data, size_t Size, size_t MaxSize);
/// Applies one of the configured mutations.
/// Returns the new size of data which could be up to MaxSize.
size_t Mutate(uint8_t *Data, size_t Size, size_t MaxSize);
/// Applies one of the default mutations. Provided as a service
/// to mutation authors.
size_t DefaultMutate(uint8_t *Data, size_t Size, size_t MaxSize);
/// Creates a cross-over of two pieces of Data, returns its size.
size_t CrossOver(const uint8_t *Data1, size_t Size1, const uint8_t *Data2,
size_t Size2, uint8_t *Out, size_t MaxOutSize);
void AddWordToManualDictionary(const Word &W);
void AddWordToAutoDictionary(DictionaryEntry DE);
void ClearAutoDictionary();
void PrintRecommendedDictionary();
void SetCorpus(const InputCorpus *Corpus) { this->Corpus = Corpus; }
Random &GetRand() { return Rand; }
private:
struct Mutator {
size_t (MutationDispatcher::*Fn)(uint8_t *Data, size_t Size, size_t Max);
const char *Name;
};
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);
size_t InsertPartOf(const uint8_t *From, size_t FromSize, uint8_t *To,
size_t ToSize, size_t MaxToSize);
size_t CopyPartOf(const uint8_t *From, size_t FromSize, uint8_t *To,
size_t ToSize);
size_t ApplyDictionaryEntry(uint8_t *Data, size_t Size, size_t MaxSize,
DictionaryEntry &DE);
template <class T>
DictionaryEntry MakeDictionaryEntryFromCMP(T Arg1, T Arg2,
const uint8_t *Data, size_t Size);
Random &Rand;
const FuzzingOptions &Options;
// Dictionary provided by the user via -dict=DICT_FILE.
Dictionary ManualDictionary;
// Temporary dictionary modified by the fuzzer itself,
// recreated periodically.
Dictionary TempAutoDictionary;
// Persistent dictionary modified by the fuzzer, consists of
// entries that led to successfull discoveries in the past mutations.
Dictionary PersistentAutoDictionary;
std::vector<Mutator> CurrentMutatorSequence;
std::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;
std::vector<Mutator> Mutators;
std::vector<Mutator> DefaultMutators;
};
} // namespace fuzzer
#endif // LLVM_FUZZER_MUTATE_H

View file

@ -0,0 +1,68 @@
//===- FuzzerOptions.h - Internal header for the Fuzzer ---------*- C++ -* ===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
// fuzzer::FuzzingOptions
//===----------------------------------------------------------------------===//
#ifndef LLVM_FUZZER_OPTIONS_H
#define LLVM_FUZZER_OPTIONS_H
#include "FuzzerDefs.h"
namespace fuzzer {
struct FuzzingOptions {
int Verbosity = 1;
size_t MaxLen = 0;
int UnitTimeoutSec = 300;
int TimeoutExitCode = 77;
int ErrorExitCode = 77;
int MaxTotalTimeSec = 0;
int RssLimitMb = 0;
bool DoCrossOver = true;
int MutateDepth = 5;
bool UseCounters = false;
bool UseIndirCalls = true;
bool UseMemcmp = true;
bool UseMemmem = true;
bool UseCmp = false;
bool UseValueProfile = false;
bool Shrink = false;
int ReloadIntervalSec = 1;
bool ShuffleAtStartUp = true;
bool PreferSmall = true;
size_t MaxNumberOfRuns = -1L;
int ReportSlowUnits = 10;
bool OnlyASCII = false;
std::string OutputCorpus;
std::string ArtifactPrefix = "./";
std::string ExactArtifactPath;
std::string ExitOnSrcPos;
std::string ExitOnItem;
bool SaveArtifacts = true;
bool PrintNEW = true; // Print a status line when new units are found;
bool OutputCSV = false;
bool PrintNewCovPcs = false;
bool PrintFinalStats = false;
bool PrintCorpusStats = false;
bool PrintCoverage = false;
bool DumpCoverage = false;
bool DetectLeaks = true;
int TraceMalloc = 0;
bool HandleAbrt = false;
bool HandleBus = false;
bool HandleFpe = false;
bool HandleIll = false;
bool HandleInt = false;
bool HandleSegv = false;
bool HandleTerm = false;
};
} // namespace fuzzer
#endif // LLVM_FUZZER_OPTIONS_H

View file

@ -0,0 +1,36 @@
//===- FuzzerRandom.h - Internal header for the Fuzzer ----------*- C++ -* ===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
// fuzzer::Random
//===----------------------------------------------------------------------===//
#ifndef LLVM_FUZZER_RANDOM_H
#define LLVM_FUZZER_RANDOM_H
#include <random>
namespace fuzzer {
class Random {
public:
Random(unsigned int seed) : R(seed) {}
size_t Rand() { return R(); }
size_t RandBool() { return Rand() % 2; }
size_t operator()(size_t n) { return n ? Rand() % n : 0; }
intptr_t operator()(intptr_t From, intptr_t To) {
assert(From < To);
intptr_t RangeSize = To - From + 1;
return operator()(RangeSize) + From;
}
std::mt19937 &Get_mt19937() { return R; }
private:
std::mt19937 R;
};
} // namespace fuzzer
#endif // LLVM_FUZZER_RANDOM_H

View file

@ -0,0 +1,222 @@
//===- FuzzerSHA1.h - Private copy of the SHA1 implementation ---*- C++ -* ===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
// This code is taken from public domain
// (http://oauth.googlecode.com/svn/code/c/liboauth/src/sha1.c)
// and modified by adding anonymous namespace, adding an interface
// function fuzzer::ComputeSHA1() and removing unnecessary code.
//
// lib/Fuzzer can not use SHA1 implementation from openssl because
// openssl may not be available and because we may be fuzzing openssl itself.
// For the same reason we do not want to depend on SHA1 from LLVM tree.
//===----------------------------------------------------------------------===//
#include "FuzzerSHA1.h"
#include "FuzzerDefs.h"
/* This code is public-domain - it is based on libcrypt
* placed in the public domain by Wei Dai and other contributors.
*/
#include <iomanip>
#include <sstream>
#include <stdint.h>
#include <string.h>
namespace { // Added for LibFuzzer
#ifdef __BIG_ENDIAN__
# define SHA_BIG_ENDIAN
#elif defined __LITTLE_ENDIAN__
/* override */
#elif defined __BYTE_ORDER
# if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
# define SHA_BIG_ENDIAN
# endif
#else // ! defined __LITTLE_ENDIAN__
# include <endian.h> // machine/endian.h
# if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
# define SHA_BIG_ENDIAN
# endif
#endif
/* header */
#define HASH_LENGTH 20
#define BLOCK_LENGTH 64
typedef struct sha1nfo {
uint32_t buffer[BLOCK_LENGTH/4];
uint32_t state[HASH_LENGTH/4];
uint32_t byteCount;
uint8_t bufferOffset;
uint8_t keyBuffer[BLOCK_LENGTH];
uint8_t innerHash[HASH_LENGTH];
} sha1nfo;
/* public API - prototypes - TODO: doxygen*/
/**
*/
void sha1_init(sha1nfo *s);
/**
*/
void sha1_writebyte(sha1nfo *s, uint8_t data);
/**
*/
void sha1_write(sha1nfo *s, const char *data, size_t len);
/**
*/
uint8_t* sha1_result(sha1nfo *s);
/* code */
#define SHA1_K0 0x5a827999
#define SHA1_K20 0x6ed9eba1
#define SHA1_K40 0x8f1bbcdc
#define SHA1_K60 0xca62c1d6
void sha1_init(sha1nfo *s) {
s->state[0] = 0x67452301;
s->state[1] = 0xefcdab89;
s->state[2] = 0x98badcfe;
s->state[3] = 0x10325476;
s->state[4] = 0xc3d2e1f0;
s->byteCount = 0;
s->bufferOffset = 0;
}
uint32_t sha1_rol32(uint32_t number, uint8_t bits) {
return ((number << bits) | (number >> (32-bits)));
}
void sha1_hashBlock(sha1nfo *s) {
uint8_t i;
uint32_t a,b,c,d,e,t;
a=s->state[0];
b=s->state[1];
c=s->state[2];
d=s->state[3];
e=s->state[4];
for (i=0; i<80; i++) {
if (i>=16) {
t = s->buffer[(i+13)&15] ^ s->buffer[(i+8)&15] ^ s->buffer[(i+2)&15] ^ s->buffer[i&15];
s->buffer[i&15] = sha1_rol32(t,1);
}
if (i<20) {
t = (d ^ (b & (c ^ d))) + SHA1_K0;
} else if (i<40) {
t = (b ^ c ^ d) + SHA1_K20;
} else if (i<60) {
t = ((b & c) | (d & (b | c))) + SHA1_K40;
} else {
t = (b ^ c ^ d) + SHA1_K60;
}
t+=sha1_rol32(a,5) + e + s->buffer[i&15];
e=d;
d=c;
c=sha1_rol32(b,30);
b=a;
a=t;
}
s->state[0] += a;
s->state[1] += b;
s->state[2] += c;
s->state[3] += d;
s->state[4] += e;
}
void sha1_addUncounted(sha1nfo *s, uint8_t data) {
uint8_t * const b = (uint8_t*) s->buffer;
#ifdef SHA_BIG_ENDIAN
b[s->bufferOffset] = data;
#else
b[s->bufferOffset ^ 3] = data;
#endif
s->bufferOffset++;
if (s->bufferOffset == BLOCK_LENGTH) {
sha1_hashBlock(s);
s->bufferOffset = 0;
}
}
void sha1_writebyte(sha1nfo *s, uint8_t data) {
++s->byteCount;
sha1_addUncounted(s, data);
}
void sha1_write(sha1nfo *s, const char *data, size_t len) {
for (;len--;) sha1_writebyte(s, (uint8_t) *data++);
}
void sha1_pad(sha1nfo *s) {
// Implement SHA-1 padding (fips180-2 §5.1.1)
// Pad with 0x80 followed by 0x00 until the end of the block
sha1_addUncounted(s, 0x80);
while (s->bufferOffset != 56) sha1_addUncounted(s, 0x00);
// Append length in the last 8 bytes
sha1_addUncounted(s, 0); // We're only using 32 bit lengths
sha1_addUncounted(s, 0); // But SHA-1 supports 64 bit lengths
sha1_addUncounted(s, 0); // So zero pad the top bits
sha1_addUncounted(s, s->byteCount >> 29); // Shifting to multiply by 8
sha1_addUncounted(s, s->byteCount >> 21); // as SHA-1 supports bitstreams as well as
sha1_addUncounted(s, s->byteCount >> 13); // byte.
sha1_addUncounted(s, s->byteCount >> 5);
sha1_addUncounted(s, s->byteCount << 3);
}
uint8_t* sha1_result(sha1nfo *s) {
// Pad to complete the last block
sha1_pad(s);
#ifndef SHA_BIG_ENDIAN
// Swap byte order back
int i;
for (i=0; i<5; i++) {
s->state[i]=
(((s->state[i])<<24)& 0xff000000)
| (((s->state[i])<<8) & 0x00ff0000)
| (((s->state[i])>>8) & 0x0000ff00)
| (((s->state[i])>>24)& 0x000000ff);
}
#endif
// Return pointer to hash (20 characters)
return (uint8_t*) s->state;
}
} // namespace; Added for LibFuzzer
namespace fuzzer {
// The rest is added for LibFuzzer
void ComputeSHA1(const uint8_t *Data, size_t Len, uint8_t *Out) {
sha1nfo s;
sha1_init(&s);
sha1_write(&s, (const char*)Data, Len);
memcpy(Out, sha1_result(&s), HASH_LENGTH);
}
std::string Sha1ToString(const uint8_t Sha1[kSHA1NumBytes]) {
std::stringstream SS;
for (int i = 0; i < kSHA1NumBytes; i++)
SS << std::hex << std::setfill('0') << std::setw(2) << (unsigned)Sha1[i];
return SS.str();
}
std::string Hash(const Unit &U) {
uint8_t Hash[kSHA1NumBytes];
ComputeSHA1(U.data(), U.size(), Hash);
return Sha1ToString(Hash);
}
}

View file

@ -0,0 +1,33 @@
//===- FuzzerSHA1.h - Internal header for the SHA1 utils --------*- C++ -* ===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
// SHA1 utils.
//===----------------------------------------------------------------------===//
#ifndef LLVM_FUZZER_SHA1_H
#define LLVM_FUZZER_SHA1_H
#include "FuzzerDefs.h"
#include <cstddef>
#include <stdint.h>
namespace fuzzer {
// Private copy of SHA1 implementation.
static const int kSHA1NumBytes = 20;
// Computes SHA1 hash of 'Len' bytes in 'Data', writes kSHA1NumBytes to 'Out'.
void ComputeSHA1(const uint8_t *Data, size_t Len, uint8_t *Out);
std::string Sha1ToString(const uint8_t Sha1[kSHA1NumBytes]);
std::string Hash(const Unit &U);
} // namespace fuzzer
#endif // LLVM_FUZZER_SHA1_H

View file

@ -0,0 +1,339 @@
//===- FuzzerTracePC.cpp - PC tracing--------------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
// Trace PCs.
// This module implements __sanitizer_cov_trace_pc_guard[_init],
// the callback required for -fsanitize-coverage=trace-pc-guard instrumentation.
//
//===----------------------------------------------------------------------===//
#include "FuzzerCorpus.h"
#include "FuzzerDefs.h"
#include "FuzzerDictionary.h"
#include "FuzzerExtFunctions.h"
#include "FuzzerIO.h"
#include "FuzzerTracePC.h"
#include "FuzzerValueBitMap.h"
#include <map>
#include <sanitizer/coverage_interface.h>
#include <set>
#include <sstream>
namespace fuzzer {
TracePC TPC;
void TracePC::HandleTrace(uint32_t *Guard, uintptr_t PC) {
uint32_t Idx = *Guard;
if (!Idx) return;
PCs[Idx % kNumPCs] = PC;
Counters[Idx % kNumCounters]++;
}
size_t TracePC::GetTotalPCCoverage() {
size_t Res = 0;
for (size_t i = 1; i < GetNumPCs(); i++)
if (PCs[i])
Res++;
return Res;
}
void TracePC::HandleInit(uint32_t *Start, uint32_t *Stop) {
if (Start == Stop || *Start) return;
assert(NumModules < sizeof(Modules) / sizeof(Modules[0]));
for (uint32_t *P = Start; P < Stop; P++)
*P = ++NumGuards;
Modules[NumModules].Start = Start;
Modules[NumModules].Stop = Stop;
NumModules++;
}
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");
}
void TracePC::HandleCallerCallee(uintptr_t Caller, uintptr_t Callee) {
const uintptr_t kBits = 12;
const uintptr_t kMask = (1 << kBits) - 1;
uintptr_t Idx = (Caller & kMask) | ((Callee & kMask) << kBits);
HandleValueProfile(Idx);
}
static bool IsInterestingCoverageFile(std::string &File) {
if (File.find("compiler-rt/lib/") != std::string::npos)
return false; // sanitizer internal.
if (File.find("/usr/lib/") != std::string::npos)
return false;
if (File.find("/usr/include/") != std::string::npos)
return false;
if (File == "<null>")
return false;
return true;
}
void TracePC::PrintNewPCs() {
if (DoPrintNewPCs) {
if (!PrintedPCs)
PrintedPCs = new std::set<uintptr_t>;
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]);
}
}
void TracePC::PrintCoverage() {
if (!EF->__sanitizer_symbolize_pc ||
!EF->__sanitizer_get_module_and_offset_for_pc) {
Printf("INFO: __sanitizer_symbolize_pc or "
"__sanitizer_get_module_and_offset_for_pc is not available,"
" 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++) {
if (!PCs[i]) continue;
std::string FileStr = DescribePC("%s", PCs[i]);
if (!IsInterestingCoverageFile(FileStr)) continue;
std::string FixedPCStr = DescribePC("%p", PCs[i]);
std::string FunctionStr = DescribePC("%F", PCs[i]);
std::string LineStr = DescribePC("%l", PCs[i]);
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 *>(PCs[i]), ModulePathRaw,
sizeof(ModulePathRaw), &OffsetRaw))
continue;
std::string Module = ModulePathRaw;
uintptr_t FixedPC = std::stol(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 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 = "objdump -d " + ModuleName +
" | grep 'call.*__sanitizer_cov_trace_pc_guard' | awk -F: '{print $1}'";
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')) {
uintptr_t PcOffset = std::stol(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 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());
}
}
void TracePC::DumpCoverage() {
__sanitizer_dump_coverage(PCs, GetNumPCs());
}
// Value profile.
// We keep track of various values that affect control flow.
// These values are inserted into a bit-set-based hash map.
// Every new bit in the map is treated as a new coverage.
//
// For memcmp/strcmp/etc the interesting value is the length of the common
// prefix of the parameters.
// For cmp instructions the interesting value is a XOR of the parameters.
// The interesting value is mixed up with the PC and is then added to the map.
ATTRIBUTE_NO_SANITIZE_MEMORY
void TracePC::AddValueForMemcmp(void *caller_pc, const void *s1, const void *s2,
size_t n) {
if (!n) return;
size_t Len = std::min(n, (size_t)32);
const uint8_t *A1 = reinterpret_cast<const uint8_t *>(s1);
const uint8_t *A2 = reinterpret_cast<const uint8_t *>(s2);
size_t I = 0;
for (; I < Len; I++)
if (A1[I] != A2[I])
break;
size_t PC = reinterpret_cast<size_t>(caller_pc);
size_t Idx = I;
// if (I < Len)
// Idx += __builtin_popcountl((A1[I] ^ A2[I])) - 1;
TPC.HandleValueProfile((PC & 4095) | (Idx << 12));
}
ATTRIBUTE_NO_SANITIZE_MEMORY
void TracePC::AddValueForStrcmp(void *caller_pc, const char *s1, const char *s2,
size_t n) {
if (!n) return;
size_t Len = std::min(n, (size_t)32);
const uint8_t *A1 = reinterpret_cast<const uint8_t *>(s1);
const uint8_t *A2 = reinterpret_cast<const uint8_t *>(s2);
size_t I = 0;
for (; I < Len; I++)
if (A1[I] != A2[I] || A1[I] == 0)
break;
size_t PC = reinterpret_cast<size_t>(caller_pc);
size_t Idx = I;
// if (I < Len && A1[I])
// Idx += __builtin_popcountl((A1[I] ^ A2[I])) - 1;
TPC.HandleValueProfile((PC & 4095) | (Idx << 12));
}
template <class T>
ATTRIBUTE_TARGET_POPCNT
#ifdef __clang__ // g++ can't handle this __attribute__ here :(
__attribute__((always_inline))
#endif // __clang__
void TracePC::HandleCmp(void *PC, T Arg1, T Arg2) {
uintptr_t PCuint = reinterpret_cast<uintptr_t>(PC);
uint64_t ArgXor = Arg1 ^ Arg2;
uint64_t ArgDistance = __builtin_popcountl(ArgXor) + 1; // [1,65]
uintptr_t Idx = ((PCuint & 4095) + 1) * ArgDistance;
if (sizeof(T) == 4)
TORC4.Insert(ArgXor, Arg1, Arg2);
else if (sizeof(T) == 8)
TORC8.Insert(ArgXor, Arg1, Arg2);
HandleValueProfile(Idx);
}
} // namespace fuzzer
extern "C" {
__attribute__((visibility("default")))
void __sanitizer_cov_trace_pc_guard(uint32_t *Guard) {
uintptr_t PC = (uintptr_t)__builtin_return_address(0);
fuzzer::TPC.HandleTrace(Guard, PC);
}
__attribute__((visibility("default")))
void __sanitizer_cov_trace_pc_guard_init(uint32_t *Start, uint32_t *Stop) {
fuzzer::TPC.HandleInit(Start, Stop);
}
__attribute__((visibility("default")))
void __sanitizer_cov_trace_pc_indir(uintptr_t Callee) {
uintptr_t PC = (uintptr_t)__builtin_return_address(0);
fuzzer::TPC.HandleCallerCallee(PC, Callee);
}
__attribute__((visibility("default")))
void __sanitizer_cov_trace_cmp8(uint64_t Arg1, uint64_t Arg2) {
fuzzer::TPC.HandleCmp(__builtin_return_address(0), Arg1, Arg2);
}
__attribute__((visibility("default")))
void __sanitizer_cov_trace_cmp4(uint32_t Arg1, uint32_t Arg2) {
fuzzer::TPC.HandleCmp(__builtin_return_address(0), Arg1, Arg2);
}
__attribute__((visibility("default")))
void __sanitizer_cov_trace_cmp2(uint16_t Arg1, uint16_t Arg2) {
fuzzer::TPC.HandleCmp(__builtin_return_address(0), Arg1, Arg2);
}
__attribute__((visibility("default")))
void __sanitizer_cov_trace_cmp1(uint8_t Arg1, uint8_t Arg2) {
fuzzer::TPC.HandleCmp(__builtin_return_address(0), Arg1, Arg2);
}
__attribute__((visibility("default")))
void __sanitizer_cov_trace_switch(uint64_t Val, uint64_t *Cases) {
// Updates the value profile based on the relative position of Val and Cases.
// We want to handle one random case at every call (handling all is slow).
// Since none of the arguments contain any random bits we use a thread-local
// counter to choose the random case to handle.
static thread_local size_t Counter;
Counter++;
uint64_t N = Cases[0];
uint64_t *Vals = Cases + 2;
char *PC = (char*)__builtin_return_address(0);
// We need a random number < N using Counter as a seed. But w/o DIV.
// * find a power of two >= N
// * mask Counter with this power of two.
// * maybe subtract N.
size_t Nlog = sizeof(long) * 8 - __builtin_clzl((long)N);
size_t PowerOfTwoGeN = 1U << Nlog;
assert(PowerOfTwoGeN >= N);
size_t Idx = Counter & (PowerOfTwoGeN - 1);
if (Idx >= N)
Idx -= N;
assert(Idx < N);
uint64_t TwoIn32 = 1ULL << 32;
if ((Val | Vals[Idx]) < TwoIn32)
fuzzer::TPC.HandleCmp(PC + Idx, static_cast<uint32_t>(Val),
static_cast<uint32_t>(Vals[Idx]));
else
fuzzer::TPC.HandleCmp(PC + Idx, Val, Vals[Idx]);
}
__attribute__((visibility("default")))
void __sanitizer_cov_trace_div4(uint32_t Val) {
fuzzer::TPC.HandleCmp(__builtin_return_address(0), Val, (uint32_t)0);
}
__attribute__((visibility("default")))
void __sanitizer_cov_trace_div8(uint64_t Val) {
fuzzer::TPC.HandleCmp(__builtin_return_address(0), Val, (uint64_t)0);
}
__attribute__((visibility("default")))
void __sanitizer_cov_trace_gep(uintptr_t Idx) {
fuzzer::TPC.HandleCmp(__builtin_return_address(0), Idx, (uintptr_t)0);
}
} // extern "C"

View file

@ -0,0 +1,158 @@
//===- FuzzerTracePC.h - Internal header for the Fuzzer ---------*- C++ -* ===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
// fuzzer::TracePC
//===----------------------------------------------------------------------===//
#ifndef LLVM_FUZZER_TRACE_PC
#define LLVM_FUZZER_TRACE_PC
#include "FuzzerDefs.h"
#include "FuzzerValueBitMap.h"
#include <set>
namespace fuzzer {
// TableOfRecentCompares (TORC) remembers the most recently performed
// comparisons of type T.
// We record the arguments of CMP instructions in this table unconditionally
// because it seems cheaper this way than to compute some expensive
// conditions inside __sanitizer_cov_trace_cmp*.
// After the unit has been executed we may decide to use the contents of
// this table to populate a Dictionary.
template<class T, size_t kSizeT>
struct TableOfRecentCompares {
static const size_t kSize = kSizeT;
struct Pair {
T A, B;
};
void Insert(size_t Idx, T Arg1, T Arg2) {
Idx = Idx % kSize;
Table[Idx].A = Arg1;
Table[Idx].B = Arg2;
}
Pair Get(size_t I) { return Table[I % kSize]; }
Pair Table[kSize];
};
class TracePC {
public:
static const size_t kFeatureSetSize = ValueBitMap::kNumberOfItems;
void HandleTrace(uint32_t *guard, uintptr_t PC);
void HandleInit(uint32_t *start, uint32_t *stop);
void HandleCallerCallee(uintptr_t Caller, uintptr_t Callee);
void HandleValueProfile(size_t Value) { ValueProfileMap.AddValue(Value); }
template <class T> void HandleCmp(void *PC, T Arg1, T Arg2);
size_t GetTotalPCCoverage();
void SetUseCounters(bool UC) { UseCounters = UC; }
void SetUseValueProfile(bool VP) { UseValueProfile = VP; }
void SetPrintNewPCs(bool P) { DoPrintNewPCs = P; }
template <class Callback> size_t CollectFeatures(Callback CB);
bool UpdateValueProfileMap(ValueBitMap *MaxValueProfileMap) {
return UseValueProfile && MaxValueProfileMap->MergeFrom(ValueProfileMap);
}
void ResetMaps() {
ValueProfileMap.Reset();
memset(Counters, 0, sizeof(Counters));
}
void UpdateFeatureSet(size_t CurrentElementIdx, size_t CurrentElementSize);
void PrintFeatureSet();
void PrintModuleInfo();
void PrintCoverage();
void DumpCoverage();
void AddValueForMemcmp(void *caller_pc, const void *s1, const void *s2,
size_t n);
void AddValueForStrcmp(void *caller_pc, const char *s1, const char *s2,
size_t n);
bool UsingTracePcGuard() const {return NumModules; }
static const size_t kTORCSize = 1 << 5;
TableOfRecentCompares<uint32_t, kTORCSize> TORC4;
TableOfRecentCompares<uint64_t, kTORCSize> TORC8;
void PrintNewPCs();
size_t GetNumPCs() const { return Min(kNumPCs, NumGuards + 1); }
uintptr_t GetPC(size_t Idx) {
assert(Idx < GetNumPCs());
return PCs[Idx];
}
private:
bool UseCounters = false;
bool UseValueProfile = false;
bool DoPrintNewPCs = false;
struct Module {
uint32_t *Start, *Stop;
};
Module Modules[4096];
size_t NumModules; // linker-initialized.
size_t NumGuards; // linker-initialized.
static const size_t kNumCounters = 1 << 14;
alignas(8) uint8_t Counters[kNumCounters];
static const size_t kNumPCs = 1 << 24;
uintptr_t PCs[kNumPCs];
std::set<uintptr_t> *PrintedPCs;
ValueBitMap ValueProfileMap;
};
template <class Callback>
size_t TracePC::CollectFeatures(Callback CB) {
if (!UsingTracePcGuard()) return 0;
size_t Res = 0;
const size_t Step = 8;
assert(reinterpret_cast<uintptr_t>(Counters) % Step == 0);
size_t N = Min(kNumCounters, NumGuards + 1);
N = (N + Step - 1) & ~(Step - 1); // Round up.
for (size_t Idx = 0; Idx < N; Idx += Step) {
uint64_t Bundle = *reinterpret_cast<uint64_t*>(&Counters[Idx]);
if (!Bundle) continue;
for (size_t i = Idx; i < Idx + Step; i++) {
uint8_t Counter = (Bundle >> ((i - Idx) * 8)) & 0xff;
if (!Counter) continue;
Counters[i] = 0;
unsigned Bit = 0;
/**/ if (Counter >= 128) Bit = 7;
else if (Counter >= 32) Bit = 6;
else if (Counter >= 16) Bit = 5;
else if (Counter >= 8) Bit = 4;
else if (Counter >= 4) Bit = 3;
else if (Counter >= 3) Bit = 2;
else if (Counter >= 2) Bit = 1;
size_t Feature = (i * 8 + Bit);
if (CB(Feature))
Res++;
}
}
if (UseValueProfile)
ValueProfileMap.ForEach([&](size_t Idx) {
if (CB(NumGuards * 8 + Idx))
Res++;
});
return Res;
}
extern TracePC TPC;
} // namespace fuzzer
#endif // LLVM_FUZZER_TRACE_PC

View file

@ -0,0 +1,325 @@
//===- 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 "FuzzerRandom.h"
#include "FuzzerTracePC.h"
#include <algorithm>
#include <cstring>
#include <map>
#include <set>
#include <thread>
namespace fuzzer {
// For now, very simple: put Size bytes of Data at position Pos.
struct TraceBasedMutation {
uint32_t Pos;
Word W;
};
// Declared as static globals for faster checks inside the hooks.
static bool RecordingMemcmp = false;
static bool RecordingMemmem = false;
static bool DoingMyOwnMemmem = false;
ScopedDoingMyOwnMemmem::ScopedDoingMyOwnMemmem() { DoingMyOwnMemmem = true; }
ScopedDoingMyOwnMemmem::~ScopedDoingMyOwnMemmem() { DoingMyOwnMemmem = false; }
class TraceState {
public:
TraceState(MutationDispatcher &MD, const FuzzingOptions &Options,
const Fuzzer *F)
: MD(MD), Options(Options), F(F) {}
void TraceMemcmpCallback(size_t CmpSize, const uint8_t *Data1,
const uint8_t *Data2);
void TraceSwitchCallback(uintptr_t PC, size_t ValSizeInBits, uint64_t Val,
size_t NumCases, uint64_t *Cases);
int TryToAddDesiredData(uint64_t PresentData, uint64_t DesiredData,
size_t DataSize);
int TryToAddDesiredData(const uint8_t *PresentData,
const uint8_t *DesiredData, size_t DataSize);
void StartTraceRecording() {
if (!Options.UseMemcmp)
return;
RecordingMemcmp = Options.UseMemcmp;
RecordingMemmem = Options.UseMemmem;
NumMutations = 0;
InterestingWords.clear();
MD.ClearAutoDictionary();
}
void StopTraceRecording() {
if (!RecordingMemcmp)
return;
RecordingMemcmp = false;
for (size_t i = 0; i < NumMutations; i++) {
auto &M = Mutations[i];
if (Options.Verbosity >= 2) {
AutoDictUnitCounts[M.W]++;
AutoDictAdds++;
if ((AutoDictAdds & (AutoDictAdds - 1)) == 0) {
typedef std::pair<size_t, Word> CU;
std::vector<CU> CountedUnits;
for (auto &I : AutoDictUnitCounts)
CountedUnits.push_back(std::make_pair(I.second, I.first));
std::sort(CountedUnits.begin(), CountedUnits.end(),
[](const CU &a, const CU &b) { return a.first > b.first; });
Printf("AutoDict:\n");
for (auto &I : CountedUnits) {
Printf(" %zd ", I.first);
PrintASCII(I.second.data(), I.second.size());
Printf("\n");
}
}
}
MD.AddWordToAutoDictionary({M.W, M.Pos});
}
for (auto &W : InterestingWords)
MD.AddWordToAutoDictionary({W});
}
void AddMutation(uint32_t Pos, uint32_t Size, const uint8_t *Data) {
if (NumMutations >= kMaxMutations) return;
auto &M = Mutations[NumMutations++];
M.Pos = Pos;
M.W.Set(Data, Size);
}
void AddMutation(uint32_t Pos, uint32_t Size, uint64_t Data) {
assert(Size <= sizeof(Data));
AddMutation(Pos, Size, reinterpret_cast<uint8_t*>(&Data));
}
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:
bool IsTwoByteData(uint64_t Data) {
int64_t Signed = static_cast<int64_t>(Data);
Signed >>= 16;
return Signed == 0 || Signed == -1L;
}
// We don't want to create too many trace-based mutations as it is both
// expensive and useless. So after some number of mutations is collected,
// start rejecting some of them. The more there are mutations the more we
// reject.
bool WantToHandleOneMoreMutation() {
const size_t FirstN = 64;
// Gladly handle first N mutations.
if (NumMutations <= FirstN) return true;
size_t Diff = NumMutations - FirstN;
size_t DiffLog = sizeof(long) * 8 - __builtin_clzl((long)Diff);
assert(DiffLog > 0 && DiffLog < 64);
bool WantThisOne = MD.GetRand()(1 << DiffLog) == 0; // 1 out of DiffLog.
return WantThisOne;
}
static const size_t kMaxMutations = 1 << 16;
size_t NumMutations;
TraceBasedMutation Mutations[kMaxMutations];
// 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;
std::map<Word, size_t> AutoDictUnitCounts;
size_t AutoDictAdds = 0;
};
int TraceState::TryToAddDesiredData(uint64_t PresentData, uint64_t DesiredData,
size_t DataSize) {
if (NumMutations >= kMaxMutations || !WantToHandleOneMoreMutation()) return 0;
ScopedDoingMyOwnMemmem scoped_doing_my_own_memmem;
const uint8_t *UnitData;
auto UnitSize = F->GetCurrentUnitInFuzzingThead(&UnitData);
int Res = 0;
const uint8_t *Beg = UnitData;
const uint8_t *End = Beg + UnitSize;
for (const uint8_t *Cur = Beg; Cur < End; Cur++) {
Cur = (uint8_t *)SearchMemory(Cur, End - Cur, &PresentData, DataSize);
if (!Cur)
break;
size_t Pos = Cur - Beg;
assert(Pos < UnitSize);
AddMutation(Pos, DataSize, DesiredData);
AddMutation(Pos, DataSize, DesiredData + 1);
AddMutation(Pos, DataSize, DesiredData - 1);
Res++;
}
return Res;
}
int TraceState::TryToAddDesiredData(const uint8_t *PresentData,
const uint8_t *DesiredData,
size_t DataSize) {
if (NumMutations >= kMaxMutations || !WantToHandleOneMoreMutation()) return 0;
ScopedDoingMyOwnMemmem scoped_doing_my_own_memmem;
const uint8_t *UnitData;
auto UnitSize = F->GetCurrentUnitInFuzzingThead(&UnitData);
int Res = 0;
const uint8_t *Beg = UnitData;
const uint8_t *End = Beg + UnitSize;
for (const uint8_t *Cur = Beg; Cur < End; Cur++) {
Cur = (uint8_t *)SearchMemory(Cur, End - Cur, PresentData, DataSize);
if (!Cur)
break;
size_t Pos = Cur - Beg;
assert(Pos < UnitSize);
AddMutation(Pos, DataSize, DesiredData);
Res++;
}
return Res;
}
void TraceState::TraceMemcmpCallback(size_t CmpSize, const uint8_t *Data1,
const uint8_t *Data2) {
if (!RecordingMemcmp || !F->InFuzzingThread()) return;
CmpSize = std::min(CmpSize, Word::GetMaxSize());
int Added2 = TryToAddDesiredData(Data1, Data2, CmpSize);
int Added1 = TryToAddDesiredData(Data2, Data1, CmpSize);
if ((Added1 || Added2) && Options.Verbosity >= 3) {
Printf("MemCmp Added %d%d: ", Added1, Added2);
if (Added1) PrintASCII(Data1, CmpSize);
if (Added2) PrintASCII(Data2, CmpSize);
Printf("\n");
}
}
void TraceState::TraceSwitchCallback(uintptr_t PC, size_t ValSizeInBits,
uint64_t Val, size_t NumCases,
uint64_t *Cases) {
if (F->InFuzzingThread()) return;
size_t ValSize = ValSizeInBits / 8;
bool TryShort = IsTwoByteData(Val);
for (size_t i = 0; i < NumCases; i++)
TryShort &= IsTwoByteData(Cases[i]);
if (Options.Verbosity >= 3)
Printf("TraceSwitch: %p %zd # %zd; TryShort %d\n", PC, Val, NumCases,
TryShort);
for (size_t i = 0; i < NumCases; i++) {
TryToAddDesiredData(Val, Cases[i], ValSize);
if (TryShort)
TryToAddDesiredData(Val, Cases[i], 2);
}
}
static TraceState *TS;
void Fuzzer::StartTraceRecording() {
if (!TS) return;
TS->StartTraceRecording();
}
void Fuzzer::StopTraceRecording() {
if (!TS) return;
TS->StopTraceRecording();
}
void Fuzzer::InitializeTraceState() {
if (!Options.UseMemcmp) 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;
}
} // namespace fuzzer
using fuzzer::TS;
using fuzzer::RecordingMemcmp;
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
void __sanitizer_weak_hook_memcmp(void *caller_pc, const void *s1,
const void *s2, size_t n, int result) {
fuzzer::TPC.AddValueForMemcmp(caller_pc, s1, s2, n);
if (!RecordingMemcmp) return;
if (result == 0) return; // No reason to mutate.
if (n <= 1) return; // Not interesting.
TS->TraceMemcmpCallback(n, reinterpret_cast<const uint8_t *>(s1),
reinterpret_cast<const uint8_t *>(s2));
}
void __sanitizer_weak_hook_strncmp(void *caller_pc, const char *s1,
const char *s2, size_t n, int result) {
fuzzer::TPC.AddValueForStrcmp(caller_pc, s1, s2, n);
if (!RecordingMemcmp) 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.
TS->TraceMemcmpCallback(n, reinterpret_cast<const uint8_t *>(s1),
reinterpret_cast<const uint8_t *>(s2));
}
void __sanitizer_weak_hook_strcmp(void *caller_pc, const char *s1,
const char *s2, int result) {
fuzzer::TPC.AddValueForStrcmp(caller_pc, s1, s2, 64);
if (!RecordingMemcmp) return;
if (result == 0) return; // No reason to mutate.
size_t Len1 = strlen(s1);
size_t Len2 = strlen(s2);
size_t N = std::min(Len1, Len2);
if (N <= 1) return; // Not interesting.
TS->TraceMemcmpCallback(N, reinterpret_cast<const uint8_t *>(s1),
reinterpret_cast<const uint8_t *>(s2));
}
void __sanitizer_weak_hook_strncasecmp(void *called_pc, const char *s1,
const char *s2, size_t n, int result) {
return __sanitizer_weak_hook_strncmp(called_pc, s1, s2, n, result);
}
void __sanitizer_weak_hook_strcasecmp(void *called_pc, const char *s1,
const char *s2, int result) {
return __sanitizer_weak_hook_strcmp(called_pc, s1, s2, result);
}
void __sanitizer_weak_hook_strstr(void *called_pc, const char *s1,
const char *s2, char *result) {
TS->AddInterestingWord(reinterpret_cast<const uint8_t *>(s2), strlen(s2));
}
void __sanitizer_weak_hook_strcasestr(void *called_pc, const char *s1,
const char *s2, char *result) {
TS->AddInterestingWord(reinterpret_cast<const uint8_t *>(s2), strlen(s2));
}
void __sanitizer_weak_hook_memmem(void *called_pc, const void *s1, size_t len1,
const void *s2, size_t len2, void *result) {
if (fuzzer::DoingMyOwnMemmem) return;
TS->AddInterestingWord(reinterpret_cast<const uint8_t *>(s2), len2);
}
#endif // LLVM_FUZZER_DEFINES_SANITIZER_WEAK_HOOOKS
} // extern "C"

View file

@ -0,0 +1,218 @@
//===- FuzzerUtil.cpp - Misc utils ----------------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
// Misc utils.
//===----------------------------------------------------------------------===//
#include "FuzzerUtil.h"
#include "FuzzerIO.h"
#include "FuzzerInternal.h"
#include <cassert>
#include <chrono>
#include <cstring>
#include <errno.h>
#include <signal.h>
#include <sstream>
#include <stdio.h>
#include <sys/types.h>
#include <thread>
namespace fuzzer {
void PrintHexArray(const uint8_t *Data, size_t Size,
const char *PrintAfter) {
for (size_t i = 0; i < Size; i++)
Printf("0x%x,", (unsigned)Data[i]);
Printf("%s", PrintAfter);
}
void Print(const Unit &v, const char *PrintAfter) {
PrintHexArray(v.data(), v.size(), PrintAfter);
}
void PrintASCIIByte(uint8_t Byte) {
if (Byte == '\\')
Printf("\\\\");
else if (Byte == '"')
Printf("\\\"");
else if (Byte >= 32 && Byte < 127)
Printf("%c", Byte);
else
Printf("\\x%02x", Byte);
}
void PrintASCII(const uint8_t *Data, size_t Size, const char *PrintAfter) {
for (size_t i = 0; i < Size; i++)
PrintASCIIByte(Data[i]);
Printf("%s", PrintAfter);
}
void PrintASCII(const Unit &U, const char *PrintAfter) {
PrintASCII(U.data(), U.size(), PrintAfter);
}
bool ToASCII(uint8_t *Data, size_t Size) {
bool Changed = false;
for (size_t i = 0; i < Size; i++) {
uint8_t &X = Data[i];
auto NewX = X;
NewX &= 127;
if (!isspace(NewX) && !isprint(NewX))
NewX = ' ';
Changed |= NewX != X;
X = NewX;
}
return Changed;
}
bool IsASCII(const Unit &U) { return IsASCII(U.data(), U.size()); }
bool IsASCII(const uint8_t *Data, size_t Size) {
for (size_t i = 0; i < Size; i++)
if (!(isprint(Data[i]) || isspace(Data[i]))) return false;
return true;
}
bool ParseOneDictionaryEntry(const std::string &Str, Unit *U) {
U->clear();
if (Str.empty()) return false;
size_t L = 0, R = Str.size() - 1; // We are parsing the range [L,R].
// Skip spaces from both sides.
while (L < R && isspace(Str[L])) L++;
while (R > L && isspace(Str[R])) R--;
if (R - L < 2) return false;
// Check the closing "
if (Str[R] != '"') return false;
R--;
// Find the opening "
while (L < R && Str[L] != '"') L++;
if (L >= R) return false;
assert(Str[L] == '\"');
L++;
assert(L <= R);
for (size_t Pos = L; Pos <= R; Pos++) {
uint8_t V = (uint8_t)Str[Pos];
if (!isprint(V) && !isspace(V)) return false;
if (V =='\\') {
// Handle '\\'
if (Pos + 1 <= R && (Str[Pos + 1] == '\\' || Str[Pos + 1] == '"')) {
U->push_back(Str[Pos + 1]);
Pos++;
continue;
}
// Handle '\xAB'
if (Pos + 3 <= R && Str[Pos + 1] == 'x'
&& isxdigit(Str[Pos + 2]) && isxdigit(Str[Pos + 3])) {
char Hex[] = "0xAA";
Hex[2] = Str[Pos + 2];
Hex[3] = Str[Pos + 3];
U->push_back(strtol(Hex, nullptr, 16));
Pos += 3;
continue;
}
return false; // Invalid escape.
} else {
// Any other character.
U->push_back(V);
}
}
return true;
}
bool ParseDictionaryFile(const std::string &Text, std::vector<Unit> *Units) {
if (Text.empty()) {
Printf("ParseDictionaryFile: file does not exist or is empty\n");
return false;
}
std::istringstream ISS(Text);
Units->clear();
Unit U;
int LineNo = 0;
std::string S;
while (std::getline(ISS, S, '\n')) {
LineNo++;
size_t Pos = 0;
while (Pos < S.size() && isspace(S[Pos])) Pos++; // Skip spaces.
if (Pos == S.size()) continue; // Empty line.
if (S[Pos] == '#') continue; // Comment line.
if (ParseOneDictionaryEntry(S, &U)) {
Units->push_back(U);
} else {
Printf("ParseDictionaryFile: error in line %d\n\t\t%s\n", LineNo,
S.c_str());
return false;
}
}
return true;
}
std::string Base64(const Unit &U) {
static const char Table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";
std::string Res;
size_t i;
for (i = 0; i + 2 < U.size(); i += 3) {
uint32_t x = (U[i] << 16) + (U[i + 1] << 8) + U[i + 2];
Res += Table[(x >> 18) & 63];
Res += Table[(x >> 12) & 63];
Res += Table[(x >> 6) & 63];
Res += Table[x & 63];
}
if (i + 1 == U.size()) {
uint32_t x = (U[i] << 16);
Res += Table[(x >> 18) & 63];
Res += Table[(x >> 12) & 63];
Res += "==";
} else if (i + 2 == U.size()) {
uint32_t x = (U[i] << 16) + (U[i + 1] << 8);
Res += Table[(x >> 18) & 63];
Res += Table[(x >> 12) & 63];
Res += Table[(x >> 6) & 63];
Res += "=";
}
return Res;
}
std::string DescribePC(const char *SymbolizedFMT, uintptr_t PC) {
if (!EF->__sanitizer_symbolize_pc) return "<can not symbolize>";
char PcDescr[1024];
EF->__sanitizer_symbolize_pc(reinterpret_cast<void*>(PC),
SymbolizedFMT, PcDescr, sizeof(PcDescr));
PcDescr[sizeof(PcDescr) - 1] = 0; // Just in case.
return PcDescr;
}
void PrintPC(const char *SymbolizedFMT, const char *FallbackFMT, uintptr_t PC) {
if (EF->__sanitizer_symbolize_pc)
Printf("%s", DescribePC(SymbolizedFMT, PC).c_str());
else
Printf(FallbackFMT, PC);
}
unsigned NumberOfCpuCores() {
unsigned N = std::thread::hardware_concurrency();
if (!N) {
Printf("WARNING: std::thread::hardware_concurrency not well defined for "
"your platform. Assuming CPU count of 1.\n");
N = 1;
}
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;
}
} // namespace fuzzer

View file

@ -0,0 +1,72 @@
//===- FuzzerUtil.h - Internal header for the Fuzzer Utils ------*- C++ -* ===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
// Util functions.
//===----------------------------------------------------------------------===//
#ifndef LLVM_FUZZER_UTIL_H
#define LLVM_FUZZER_UTIL_H
#include "FuzzerDefs.h"
namespace fuzzer {
void PrintHexArray(const Unit &U, const char *PrintAfter = "");
void PrintHexArray(const uint8_t *Data, size_t Size,
const char *PrintAfter = "");
void PrintASCII(const uint8_t *Data, size_t Size, const char *PrintAfter = "");
void PrintASCII(const Unit &U, const char *PrintAfter = "");
// Changes U to contain only ASCII (isprint+isspace) characters.
// Returns true iff U has been changed.
bool ToASCII(uint8_t *Data, size_t Size);
bool IsASCII(const Unit &U);
bool IsASCII(const uint8_t *Data, size_t Size);
std::string Base64(const Unit &U);
void PrintPC(const char *SymbolizedFMT, const char *FallbackFMT, uintptr_t PC);
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);
void SleepSeconds(int Seconds);
unsigned long GetPid();
size_t GetPeakRSSMb();
int ExecuteCommand(const std::string &Command);
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,
const char *X1, const char *X2);
inline std::string CloneArgsWithoutX(const std::vector<std::string> &Args,
const char *X) {
return CloneArgsWithoutX(Args, X, X);
}
} // namespace fuzzer
#endif // LLVM_FUZZER_UTIL_H

View file

@ -0,0 +1,152 @@
//===- FuzzerUtilDarwin.cpp - Misc utils ----------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
// Misc utils for Darwin.
//===----------------------------------------------------------------------===//
#include "FuzzerDefs.h"
#if LIBFUZZER_APPLE
#include "FuzzerIO.h"
#include <mutex>
#include <signal.h>
#include <spawn.h>
#include <sys/wait.h>
// There is no header for this on macOS so declare here
extern "C" char **environ;
namespace fuzzer {
static std::mutex SignalMutex;
// Global variables used to keep track of how signal handling should be
// restored. They should **not** be accessed without holding `SignalMutex`.
static int ActiveThreadCount = 0;
static struct sigaction OldSigIntAction;
static struct sigaction OldSigQuitAction;
static sigset_t OldBlockedSignalsSet;
// This is a reimplementation of Libc's `system()`. On Darwin the Libc
// implementation contains a mutex which prevents it from being used
// concurrently. This implementation **can** be used concurrently. It sets the
// 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) {
posix_spawnattr_t SpawnAttributes;
if (posix_spawnattr_init(&SpawnAttributes))
return -1;
// Block and ignore signals of the current process when the first thread
// enters.
{
std::lock_guard<std::mutex> Lock(SignalMutex);
if (ActiveThreadCount == 0) {
static struct sigaction IgnoreSignalAction;
sigset_t BlockedSignalsSet;
memset(&IgnoreSignalAction, 0, sizeof(IgnoreSignalAction));
IgnoreSignalAction.sa_handler = SIG_IGN;
if (sigaction(SIGINT, &IgnoreSignalAction, &OldSigIntAction) == -1) {
Printf("Failed to ignore SIGINT\n");
(void)posix_spawnattr_destroy(&SpawnAttributes);
return -1;
}
if (sigaction(SIGQUIT, &IgnoreSignalAction, &OldSigQuitAction) == -1) {
Printf("Failed to ignore SIGQUIT\n");
// Try our best to restore the signal handlers.
(void)sigaction(SIGINT, &OldSigIntAction, NULL);
(void)posix_spawnattr_destroy(&SpawnAttributes);
return -1;
}
(void)sigemptyset(&BlockedSignalsSet);
(void)sigaddset(&BlockedSignalsSet, SIGCHLD);
if (sigprocmask(SIG_BLOCK, &BlockedSignalsSet, &OldBlockedSignalsSet) ==
-1) {
Printf("Failed to block SIGCHLD\n");
// Try our best to restore the signal handlers.
(void)sigaction(SIGQUIT, &OldSigQuitAction, NULL);
(void)sigaction(SIGINT, &OldSigIntAction, NULL);
(void)posix_spawnattr_destroy(&SpawnAttributes);
return -1;
}
}
++ActiveThreadCount;
}
// NOTE: Do not introduce any new `return` statements past this
// point. It is important that `ActiveThreadCount` always be decremented
// when leaving this function.
// Make sure the child process uses the default handlers for the
// following signals rather than inheriting what the parent has.
sigset_t DefaultSigSet;
(void)sigemptyset(&DefaultSigSet);
(void)sigaddset(&DefaultSigSet, SIGQUIT);
(void)sigaddset(&DefaultSigSet, SIGINT);
(void)posix_spawnattr_setsigdefault(&SpawnAttributes, &DefaultSigSet);
// Make sure the child process doesn't block SIGCHLD
(void)posix_spawnattr_setsigmask(&SpawnAttributes, &OldBlockedSignalsSet);
short SpawnFlags = POSIX_SPAWN_SETSIGDEF | POSIX_SPAWN_SETSIGMASK;
(void)posix_spawnattr_setflags(&SpawnAttributes, SpawnFlags);
pid_t Pid;
char **Environ = environ; // Read from global
const char *CommandCStr = Command.c_str();
const char *Argv[] = {"sh", "-c", 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);
(void)posix_spawnattr_destroy(&SpawnAttributes);
if (!ErrorCode) {
pid_t SavedPid = Pid;
do {
// Repeat until call completes uninterrupted.
Pid = waitpid(SavedPid, &ProcessStatus, /*options=*/0);
} while (Pid == -1 && errno == EINTR);
if (Pid == -1) {
// Fail for some other reason.
ProcessStatus = -1;
}
} else if (ErrorCode == ENOMEM || ErrorCode == EAGAIN) {
// Fork failure.
ProcessStatus = -1;
} else {
// Shell execution failure.
ProcessStatus = W_EXITCODE(127, 0);
}
// Restore the signal handlers of the current process when the last thread
// using this function finishes.
{
std::lock_guard<std::mutex> Lock(SignalMutex);
--ActiveThreadCount;
if (ActiveThreadCount == 0) {
bool FailedRestore = false;
if (sigaction(SIGINT, &OldSigIntAction, NULL) == -1) {
Printf("Failed to restore SIGINT handling\n");
FailedRestore = true;
}
if (sigaction(SIGQUIT, &OldSigQuitAction, NULL) == -1) {
Printf("Failed to restore SIGQUIT handling\n");
FailedRestore = true;
}
if (sigprocmask(SIG_BLOCK, &OldBlockedSignalsSet, NULL) == -1) {
Printf("Failed to unblock SIGCHLD\n");
FailedRestore = true;
}
if (FailedRestore)
ProcessStatus = -1;
}
}
return ProcessStatus;
}
} // namespace fuzzer
#endif // LIBFUZZER_APPLE

View file

@ -0,0 +1,24 @@
//===- FuzzerUtilLinux.cpp - Misc utils for Linux. ------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
// Misc utils for Linux.
//===----------------------------------------------------------------------===//
#include "FuzzerDefs.h"
#if LIBFUZZER_LINUX
#include <stdlib.h>
namespace fuzzer {
int ExecuteCommand(const std::string &Command) {
return system(Command.c_str());
}
} // namespace fuzzer
#endif // LIBFUZZER_LINUX

View file

@ -0,0 +1,117 @@
//===- FuzzerUtilPosix.cpp - Misc utils for Posix. ------------------------===//
//
// 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 Posix API.
//===----------------------------------------------------------------------===//
#include "FuzzerDefs.h"
#if LIBFUZZER_POSIX
#include "FuzzerIO.h"
#include "FuzzerInternal.h"
#include <cassert>
#include <chrono>
#include <cstring>
#include <errno.h>
#include <iomanip>
#include <signal.h>
#include <sstream>
#include <stdio.h>
#include <sys/resource.h>
#include <sys/syscall.h>
#include <sys/time.h>
#include <sys/types.h>
#include <thread>
#include <unistd.h>
namespace fuzzer {
static void AlarmHandler(int, siginfo_t *, void *) {
Fuzzer::StaticAlarmCallback();
}
static void CrashHandler(int, siginfo_t *, void *) {
Fuzzer::StaticCrashSignalCallback();
}
static void InterruptHandler(int, siginfo_t *, void *) {
Fuzzer::StaticInterruptCallback();
}
static void SetSigaction(int signum,
void (*callback)(int, siginfo_t *, void *)) {
struct sigaction sigact;
memset(&sigact, 0, sizeof(sigact));
sigact.sa_sigaction = callback;
if (sigaction(signum, &sigact, 0)) {
Printf("libFuzzer: sigaction failed with %d\n", errno);
exit(1);
}
}
void SetTimer(int Seconds) {
struct itimerval T {
{Seconds, 0}, { Seconds, 0 }
};
if (setitimer(ITIMER_REAL, &T, nullptr)) {
Printf("libFuzzer: setitimer failed with %d\n", errno);
exit(1);
}
SetSigaction(SIGALRM, AlarmHandler);
}
void SetSignalHandler(const FuzzingOptions& Options) {
if (Options.UnitTimeoutSec > 0)
SetTimer(Options.UnitTimeoutSec / 2 + 1);
if (Options.HandleInt)
SetSigaction(SIGINT, InterruptHandler);
if (Options.HandleTerm)
SetSigaction(SIGTERM, InterruptHandler);
if (Options.HandleSegv)
SetSigaction(SIGSEGV, CrashHandler);
if (Options.HandleBus)
SetSigaction(SIGBUS, CrashHandler);
if (Options.HandleAbrt)
SetSigaction(SIGABRT, CrashHandler);
if (Options.HandleIll)
SetSigaction(SIGILL, CrashHandler);
if (Options.HandleFpe)
SetSigaction(SIGFPE, CrashHandler);
}
void SleepSeconds(int Seconds) {
sleep(Seconds); // Use C API to avoid coverage from instrumented libc++.
}
unsigned long GetPid() { return (unsigned long)getpid(); }
size_t GetPeakRSSMb() {
struct rusage usage;
if (getrusage(RUSAGE_SELF, &usage))
return 0;
if (LIBFUZZER_LINUX) {
// ru_maxrss is in KiB
return usage.ru_maxrss >> 10;
} else if (LIBFUZZER_APPLE) {
// ru_maxrss is in bytes
return usage.ru_maxrss >> 20;
}
assert(0 && "GetPeakRSSMb() is not implemented for your platform");
return 0;
}
FILE *OpenProcessPipe(const char *Command, const char *Mode) {
return popen(Command, Mode);
}
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_POSIX

View file

@ -0,0 +1,182 @@
//===- FuzzerUtilWindows.cpp - Misc utils for Windows. --------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
// Misc utils implementation for Windows.
//===----------------------------------------------------------------------===//
#include "FuzzerDefs.h"
#if LIBFUZZER_WINDOWS
#include "FuzzerIO.h"
#include "FuzzerInternal.h"
#include <cassert>
#include <chrono>
#include <cstring>
#include <errno.h>
#include <iomanip>
#include <signal.h>
#include <sstream>
#include <stdio.h>
#include <sys/types.h>
#include <windows.h>
#include <Psapi.h>
namespace fuzzer {
static const FuzzingOptions* HandlerOpt = nullptr;
LONG CALLBACK ExceptionHandler(PEXCEPTION_POINTERS ExceptionInfo) {
switch (ExceptionInfo->ExceptionRecord->ExceptionCode) {
case EXCEPTION_ACCESS_VIOLATION:
case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
case EXCEPTION_STACK_OVERFLOW:
if (HandlerOpt->HandleSegv)
Fuzzer::StaticCrashSignalCallback();
break;
case EXCEPTION_DATATYPE_MISALIGNMENT:
case EXCEPTION_IN_PAGE_ERROR:
if (HandlerOpt->HandleBus)
Fuzzer::StaticCrashSignalCallback();
break;
case EXCEPTION_ILLEGAL_INSTRUCTION:
case EXCEPTION_PRIV_INSTRUCTION:
if (HandlerOpt->HandleIll)
Fuzzer::StaticCrashSignalCallback();
break;
case EXCEPTION_FLT_DENORMAL_OPERAND:
case EXCEPTION_FLT_DIVIDE_BY_ZERO:
case EXCEPTION_FLT_INEXACT_RESULT:
case EXCEPTION_FLT_INVALID_OPERATION:
case EXCEPTION_FLT_OVERFLOW:
case EXCEPTION_FLT_STACK_CHECK:
case EXCEPTION_FLT_UNDERFLOW:
case EXCEPTION_INT_DIVIDE_BY_ZERO:
case EXCEPTION_INT_OVERFLOW:
if (HandlerOpt->HandleFpe)
Fuzzer::StaticCrashSignalCallback();
break;
}
return EXCEPTION_CONTINUE_SEARCH;
}
BOOL WINAPI CtrlHandler(DWORD dwCtrlType) {
switch (dwCtrlType) {
case CTRL_C_EVENT:
if (HandlerOpt->HandleInt)
Fuzzer::StaticInterruptCallback();
return TRUE;
case CTRL_BREAK_EVENT:
if (HandlerOpt->HandleTerm)
Fuzzer::StaticInterruptCallback();
return TRUE;
}
return FALSE;
}
void CALLBACK AlarmHandler(PVOID, BOOLEAN) {
Fuzzer::StaticAlarmCallback();
}
class TimerQ {
HANDLE TimerQueue;
public:
TimerQ() : TimerQueue(NULL) {};
~TimerQ() {
if (TimerQueue)
DeleteTimerQueueEx(TimerQueue, NULL);
};
void SetTimer(int Seconds) {
if (!TimerQueue) {
TimerQueue = CreateTimerQueue();
if (!TimerQueue) {
Printf("libFuzzer: CreateTimerQueue failed.\n");
exit(1);
}
}
HANDLE Timer;
if (!CreateTimerQueueTimer(&Timer, TimerQueue, AlarmHandler, NULL,
Seconds*1000, Seconds*1000, 0)) {
Printf("libFuzzer: CreateTimerQueueTimer failed.\n");
exit(1);
}
};
};
static TimerQ Timer;
static void CrashHandler(int) { Fuzzer::StaticCrashSignalCallback(); }
void SetSignalHandler(const FuzzingOptions& Options) {
HandlerOpt = &Options;
if (Options.UnitTimeoutSec > 0)
Timer.SetTimer(Options.UnitTimeoutSec / 2 + 1);
if (Options.HandleInt || Options.HandleTerm)
if (!SetConsoleCtrlHandler(CtrlHandler, TRUE)) {
DWORD LastError = GetLastError();
Printf("libFuzzer: SetConsoleCtrlHandler failed (Error code: %lu).\n",
LastError);
exit(1);
}
if (Options.HandleSegv || Options.HandleBus || Options.HandleIll ||
Options.HandleFpe)
if (!AddVectoredExceptionHandler(1, ExceptionHandler)) {
Printf("libFuzzer: AddVectoredExceptionHandler failed.\n");
exit(1);
}
if (Options.HandleAbrt)
if (SIG_ERR == signal(SIGABRT, CrashHandler)) {
Printf("libFuzzer: signal failed with %d\n", errno);
exit(1);
}
}
void SleepSeconds(int Seconds) { Sleep(Seconds * 1000); }
unsigned long GetPid() { return GetCurrentProcessId(); }
size_t GetPeakRSSMb() {
PROCESS_MEMORY_COUNTERS info;
if (!GetProcessMemoryInfo(GetCurrentProcess(), &info, sizeof(info)))
return 0;
return info.PeakWorkingSetSize >> 20;
}
FILE *OpenProcessPipe(const char *Command, const char *Mode) {
return _popen(Command, Mode);
}
int ExecuteCommand(const std::string &Command) {
return system(Command.c_str());
}
const void *SearchMemory(const void *Data, size_t DataLen, const void *Patt,
size_t PattLen) {
// TODO: make this implementation more efficient.
const char *Cdata = (const char *)Data;
const char *Cpatt = (const char *)Patt;
if (!Data || !Patt || DataLen == 0 || PattLen == 0 || DataLen < PattLen)
return NULL;
if (PattLen == 1)
return memchr(Data, *Cpatt, DataLen);
const char *End = Cdata + DataLen - PattLen + 1;
for (const char *It = Cdata; It < End; ++It)
if (It[0] == Cpatt[0] && memcmp(It, Cpatt, PattLen) == 0)
return It;
return NULL;
}
} // namespace fuzzer
#endif // LIBFUZZER_WINDOWS

View file

@ -0,0 +1,87 @@
//===- FuzzerValueBitMap.h - INTERNAL - Bit map -----------------*- C++ -* ===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
// ValueBitMap.
//===----------------------------------------------------------------------===//
#ifndef LLVM_FUZZER_VALUE_BIT_MAP_H
#define LLVM_FUZZER_VALUE_BIT_MAP_H
#include "FuzzerDefs.h"
namespace fuzzer {
// A bit map containing kMapSizeInWords bits.
struct ValueBitMap {
static const size_t kMapSizeInBits = 65371; // Prime.
static const size_t kMapSizeInBitsAligned = 65536; // 2^16
static const size_t kBitsInWord = (sizeof(uintptr_t) * 8);
static const size_t kMapSizeInWords = kMapSizeInBitsAligned / kBitsInWord;
public:
static const size_t kNumberOfItems = kMapSizeInBits;
// Clears all bits.
void Reset() { memset(Map, 0, sizeof(Map)); }
// Computes a hash function of Value and sets the corresponding bit.
// Returns true if the bit was changed from 0 to 1.
inline bool AddValue(uintptr_t Value) {
uintptr_t Idx = Value < kMapSizeInBits ? Value : Value % kMapSizeInBits;
uintptr_t WordIdx = Idx / kBitsInWord;
uintptr_t BitIdx = Idx % kBitsInWord;
uintptr_t Old = Map[WordIdx];
uintptr_t New = Old | (1UL << BitIdx);
Map[WordIdx] = New;
return New != Old;
}
inline bool Get(uintptr_t Idx) {
assert(Idx < kMapSizeInBits);
uintptr_t WordIdx = Idx / kBitsInWord;
uintptr_t BitIdx = Idx % kBitsInWord;
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_popcountl(M);
}
NumBits = Res;
return OldNumBits < NumBits;
}
template <class Callback>
void ForEach(Callback CB) {
for (size_t i = 0; i < kMapSizeInWords; i++)
if (uintptr_t M = Map[i])
for (size_t j = 0; j < sizeof(M) * 8; j++)
if (M & ((uintptr_t)1 << j))
CB(i * sizeof(M) * 8 + j);
}
private:
size_t NumBits = 0;
uintptr_t Map[kMapSizeInWords] __attribute__((aligned(512)));
};
} // namespace fuzzer
#endif // LLVM_FUZZER_VALUE_BIT_MAP_H

View file

@ -0,0 +1,2 @@
Move to http://llvm.org/docs/LibFuzzer.html

View file

@ -0,0 +1,295 @@
//===- afl_driver.cpp - a glue between AFL and libFuzzer --------*- C++ -* ===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//===----------------------------------------------------------------------===//
/* This file allows to fuzz libFuzzer-style target functions
(LLVMFuzzerTestOneInput) with AFL using AFL's persistent (in-process) mode.
Usage:
################################################################################
cat << EOF > test_fuzzer.cc
#include <stdint.h>
#include <stddef.h>
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
if (size > 0 && data[0] == 'H')
if (size > 1 && data[1] == 'I')
if (size > 2 && data[2] == '!')
__builtin_trap();
return 0;
}
EOF
# Build your target with -fsanitize-coverage=trace-pc using fresh clang.
clang -g -fsanitize-coverage=trace-pc test_fuzzer.cc -c
# Build afl-llvm-rt.o.c from the AFL distribution.
clang -c -w $AFL_HOME/llvm_mode/afl-llvm-rt.o.c
# Build this file, link it with afl-llvm-rt.o.o and the target code.
clang++ afl_driver.cpp test_fuzzer.o afl-llvm-rt.o.o
# Run AFL:
rm -rf IN OUT; mkdir IN OUT; echo z > IN/z;
$AFL_HOME/afl-fuzz -i IN -o OUT ./a.out
################################################################################
Environment Variables:
There are a few environment variables that can be set to use features that
afl-fuzz doesn't have.
AFL_DRIVER_STDERR_DUPLICATE_FILENAME: Setting this *appends* stderr to the file
specified. If the file does not exist, it is created. This is useful for getting
stack traces (when using ASAN for example) or original error messages on hard to
reproduce bugs.
AFL_DRIVER_EXTRA_STATS_FILENAME: Setting this causes afl_driver to write extra
statistics to the file specified. Currently these are peak_rss_mb
(the peak amount of virtual memory used in MB) and slowest_unit_time_secs. If
the file does not exist it is created. If the file does exist then
afl_driver assumes it was restarted by afl-fuzz and will try to read old
statistics from the file. If that fails then the process will quit.
*/
#include <assert.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include <sys/resource.h>
#include <sys/time.h>
// Platform detection. Copied from FuzzerInternal.h
#ifdef __linux__
#define LIBFUZZER_LINUX 1
#define LIBFUZZER_APPLE 0
#elif __APPLE__
#define LIBFUZZER_LINUX 0
#define LIBFUZZER_APPLE 1
#else
#error "Support for your platform has not been implemented"
#endif
// Used to avoid repeating error checking boilerplate. If cond is false, a
// fatal error has occured in the program. In this event print error_message
// to stderr and abort(). Otherwise do nothing. Note that setting
// AFL_DRIVER_STDERR_DUPLICATE_FILENAME may cause error_message to be appended
// to the file as well, if the error occurs after the duplication is performed.
#define CHECK_ERROR(cond, error_message) \
if (!(cond)) { \
fprintf(stderr, (error_message)); \
abort(); \
}
// libFuzzer interface is thin, so we don't include any libFuzzer headers.
extern "C" {
int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size);
__attribute__((weak)) int LLVMFuzzerInitialize(int *argc, char ***argv);
}
// Notify AFL about persistent mode.
static volatile char AFL_PERSISTENT[] = "##SIG_AFL_PERSISTENT##";
extern "C" int __afl_persistent_loop(unsigned int);
static volatile char suppress_warning2 = AFL_PERSISTENT[0];
// Notify AFL about deferred forkserver.
static volatile char AFL_DEFER_FORKSVR[] = "##SIG_AFL_DEFER_FORKSRV##";
extern "C" void __afl_manual_init();
static volatile char suppress_warning1 = AFL_DEFER_FORKSVR[0];
// Input buffer.
static const size_t kMaxAflInputSize = 1 << 20;
static uint8_t AflInputBuf[kMaxAflInputSize];
// Variables we need for writing to the extra stats file.
static FILE *extra_stats_file = NULL;
static uint32_t previous_peak_rss = 0;
static time_t slowest_unit_time_secs = 0;
static const int kNumExtraStats = 2;
static const char *kExtraStatsFormatString = "peak_rss_mb : %u\n"
"slowest_unit_time_sec : %u\n";
// Copied from FuzzerUtil.cpp.
size_t GetPeakRSSMb() {
struct rusage usage;
if (getrusage(RUSAGE_SELF, &usage))
return 0;
if (LIBFUZZER_LINUX) {
// ru_maxrss is in KiB
return usage.ru_maxrss >> 10;
} else if (LIBFUZZER_APPLE) {
// ru_maxrss is in bytes
return usage.ru_maxrss >> 20;
}
assert(0 && "GetPeakRSSMb() is not implemented for your platform");
return 0;
}
// Based on SetSigaction in FuzzerUtil.cpp
static void SetSigaction(int signum,
void (*callback)(int, siginfo_t *, void *)) {
struct sigaction sigact;
memset(&sigact, 0, sizeof(sigact));
sigact.sa_sigaction = callback;
if (sigaction(signum, &sigact, 0)) {
fprintf(stderr, "libFuzzer: sigaction failed with %d\n", errno);
exit(1);
}
}
// Write extra stats to the file specified by the user. If none is specified
// this function will never be called.
static void write_extra_stats() {
uint32_t peak_rss = GetPeakRSSMb();
if (peak_rss < previous_peak_rss)
peak_rss = previous_peak_rss;
int chars_printed = fprintf(extra_stats_file, kExtraStatsFormatString,
peak_rss, slowest_unit_time_secs);
CHECK_ERROR(chars_printed != 0, "Failed to write extra_stats_file");
CHECK_ERROR(fclose(extra_stats_file) == 0,
"Failed to close extra_stats_file");
}
// Call write_extra_stats before we exit.
static void crash_handler(int, siginfo_t *, void *) {
// Make sure we don't try calling write_extra_stats again if we crashed while
// trying to call it.
static bool first_crash = true;
CHECK_ERROR(first_crash,
"Crashed in crash signal handler. This is a bug in the fuzzer.");
first_crash = false;
write_extra_stats();
}
// If the user has specified an extra_stats_file through the environment
// variable AFL_DRIVER_EXTRA_STATS_FILENAME, then perform necessary set up
// to write stats to it on exit. If no file is specified, do nothing. Otherwise
// install signal and exit handlers to write to the file when the process exits.
// Then if the file doesn't exist create it and set extra stats to 0. But if it
// does exist then read the initial values of the extra stats from the file
// and check that the file is writable.
static void maybe_initialize_extra_stats() {
// If AFL_DRIVER_EXTRA_STATS_FILENAME isn't set then we have nothing to do.
char *extra_stats_filename = getenv("AFL_DRIVER_EXTRA_STATS_FILENAME");
if (!extra_stats_filename)
return;
// Open the file and find the previous peak_rss_mb value.
// This is necessary because the fuzzing process is restarted after N
// iterations are completed. So we may need to get this value from a previous
// process to be accurate.
extra_stats_file = fopen(extra_stats_filename, "r");
// If extra_stats_file already exists: read old stats from it.
if (extra_stats_file) {
int matches = fscanf(extra_stats_file, kExtraStatsFormatString,
&previous_peak_rss, &slowest_unit_time_secs);
// Make sure we have read a real extra stats file and that we have used it
// to set slowest_unit_time_secs and previous_peak_rss.
CHECK_ERROR(matches == kNumExtraStats, "Extra stats file is corrupt");
CHECK_ERROR(fclose(extra_stats_file) == 0, "Failed to close file");
// Now open the file for writing.
extra_stats_file = fopen(extra_stats_filename, "w");
CHECK_ERROR(extra_stats_file,
"Failed to open extra stats file for writing");
} else {
// Looks like this is the first time in a fuzzing job this is being called.
extra_stats_file = fopen(extra_stats_filename, "w+");
CHECK_ERROR(extra_stats_file, "failed to create extra stats file");
}
// Make sure that crash_handler gets called on any kind of fatal error.
int crash_signals[] = {SIGSEGV, SIGBUS, SIGABRT, SIGILL, SIGFPE, SIGINT,
SIGTERM};
const size_t num_signals = sizeof(crash_signals) / sizeof(crash_signals[0]);
for (size_t idx = 0; idx < num_signals; idx++)
SetSigaction(crash_signals[idx], crash_handler);
// Make sure it gets called on other kinds of exits.
atexit(write_extra_stats);
}
// If the user asks us to duplicate stderr, then do it.
static void maybe_duplicate_stderr() {
char* stderr_duplicate_filename =
getenv("AFL_DRIVER_STDERR_DUPLICATE_FILENAME");
if (!stderr_duplicate_filename)
return;
FILE* stderr_duplicate_stream =
freopen(stderr_duplicate_filename, "a+", stderr);
if (!stderr_duplicate_stream) {
fprintf(
stderr,
"Failed to duplicate stderr to AFL_DRIVER_STDERR_DUPLICATE_FILENAME");
abort();
}
}
int main(int argc, char **argv) {
fprintf(stderr, "======================= INFO =========================\n"
"This binary is built for AFL-fuzz.\n"
"To run the target function on a single input execute this:\n"
" %s < INPUT_FILE\n"
"To run the fuzzing execute this:\n"
" afl-fuzz [afl-flags] %s [N] "
"-- run N fuzzing iterations before "
"re-spawning the process (default: 1000)\n"
"======================================================\n",
argv[0], argv[0]);
if (LLVMFuzzerInitialize)
LLVMFuzzerInitialize(&argc, &argv);
// Do any other expensive one-time initialization here.
maybe_duplicate_stderr();
maybe_initialize_extra_stats();
__afl_manual_init();
int N = 1000;
if (argc >= 2)
N = atoi(argv[1]);
assert(N > 0);
time_t unit_time_secs;
int num_runs = 0;
while (__afl_persistent_loop(N)) {
ssize_t n_read = read(0, AflInputBuf, kMaxAflInputSize);
if (n_read > 0) {
// Copy AflInputBuf into a separate buffer to let asan find buffer
// overflows. Don't use unique_ptr/etc to avoid extra dependencies.
uint8_t *copy = new uint8_t[n_read];
memcpy(copy, AflInputBuf, n_read);
struct timeval unit_start_time;
CHECK_ERROR(gettimeofday(&unit_start_time, NULL) == 0,
"Calling gettimeofday failed");
num_runs++;
LLVMFuzzerTestOneInput(copy, n_read);
struct timeval unit_stop_time;
CHECK_ERROR(gettimeofday(&unit_stop_time, NULL) == 0,
"Calling gettimeofday failed");
// Update slowest_unit_time_secs if we see a new max.
unit_time_secs = unit_stop_time.tv_sec - unit_start_time.tv_sec;
if (slowest_unit_time_secs < unit_time_secs)
slowest_unit_time_secs = unit_time_secs;
delete[] copy;
}
}
fprintf(stderr, "%s: successfully executed %d input(s)\n", argv[0], num_runs);
}

10
test/fuzz_test/Fuzzer/build.sh Executable file
View file

@ -0,0 +1,10 @@
#!/bin/bash
LIBFUZZER_SRC_DIR=$(dirname $0)
for f in $LIBFUZZER_SRC_DIR/*.cpp; do
clang -g -O2 -fno-omit-frame-pointer -std=c++11 $f -c &
done
wait
rm -f libFuzzer.a
ar ru libFuzzer.a Fuzzer*.o
rm -f Fuzzer*.o

View file

@ -0,0 +1,122 @@
"++"
"--"
"<<"
">>"
"+="
"-="
"*="
"/="
">>="
"<<="
"&="
"|="
"^="
"%="
"!="
"&&"
"||"
"=="
">="
"<="
"->"
"alignas"
"alignof"
"and"
"and_eq"
"asm"
"auto"
"bitand"
"bitor"
"bool"
"break"
"case"
"catch"
"char"
"char16_t"
"char32_t"
"class"
"compl"
"concept"
"const"
"constexpr"
"const_cast"
"continue"
"decltype"
"default"
"delete"
"do"
"double"
"dynamic_cast"
"else"
"enum"
"explicit"
"export"
"extern"
"false"
"float"
"for"
"friend"
"goto"
"if"
"inline"
"int"
"long"
"mutable"
"namespace"
"new"
"noexcept"
"not"
"not_eq"
"nullptr"
"operator"
"or"
"or_eq"
"private"
"protected"
"public"
"register"
"reinterpret_cast"
"requires"
"return"
"short"
"signed"
"sizeof"
"static"
"static_assert"
"static_cast"
"struct"
"switch"
"template"
"this"
"thread_local"
"throw"
"true"
"try"
"typedef"
"typeid"
"typename"
"union"
"unsigned"
"using"
"virtual"
"void"
"volatile"
"wchar_t"
"while"
"xor"
"xor_eq"
"if"
"elif"
"else"
"endif"
"defined"
"ifdef"
"ifndef"
"define"
"undef"
"include"
"line"
"error"
"pragma"
"override"
"final"

View file

@ -0,0 +1,41 @@
/*===- StandaloneFuzzTargetMain.c - standalone main() for fuzz targets. ---===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
// This main() function can be linked to a fuzz target (i.e. a library
// that exports LLVMFuzzerTestOneInput() and possibly LLVMFuzzerInitialize())
// instead of libFuzzer. This main() function will not perform any fuzzing
// but will simply feed all input files one by one to the fuzz target.
//
// Use this file to provide reproducers for bugs when linking against libFuzzer
// or other fuzzing engine is undesirable.
//===----------------------------------------------------------------------===*/
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
extern int LLVMFuzzerTestOneInput(const unsigned char *data, size_t size);
__attribute__((weak)) extern int LLVMFuzzerInitialize(int *argc, char ***argv);
int main(int argc, char **argv) {
fprintf(stderr, "StandaloneFuzzTargetMain: running %d inputs\n", argc - 1);
if (LLVMFuzzerInitialize)
LLVMFuzzerInitialize(&argc, &argv);
for (int i = 1; i < argc; i++) {
fprintf(stderr, "Running: %s\n", argv[i]);
FILE *f = fopen(argv[i], "r");
assert(f);
fseek(f, 0, SEEK_END);
size_t len = ftell(f);
fseek(f, 0, SEEK_SET);
unsigned char *buf = (unsigned char*)malloc(len);
size_t n_read = fread(buf, 1, len, f);
assert(n_read == len);
LLVMFuzzerTestOneInput(buf, len);
free(buf);
fprintf(stderr, "Done: %s: (%zd bytes)\n", argv[i], n_read);
}
}

View file

@ -0,0 +1,22 @@
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
// Contains dummy functions used to avoid dependency on AFL.
#include <stdint.h>
#include <stdlib.h>
extern "C" void __afl_manual_init() {}
extern "C" int __afl_persistent_loop(unsigned int) {
return 0;
}
// This declaration exists to prevent the Darwin linker
// from complaining about this being a missing weak symbol.
extern "C" int LLVMFuzzerInitialize(int *argc, char ***argv) {
return 0;
}
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
return 0;
}

View file

@ -0,0 +1,23 @@
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
// abs(x) < 0 and y == Const puzzle, 64-bit variant.
#include <cstring>
#include <cstdint>
#include <cstdlib>
#include <cstddef>
#include <cstdio>
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
if (Size < 16) return 0;
int64_t x;
uint64_t y;
memcpy(&x, Data, sizeof(x));
memcpy(&y, Data + sizeof(x), sizeof(y));
if (labs(x) < 0 && y == 0xbaddcafedeadbeefUL) {
printf("BINGO; Found the target, exiting; x = 0x%lx y 0x%lx\n", x, y);
exit(1);
}
return 0;
}

View file

@ -0,0 +1,23 @@
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
// abs(x) < 0 and y == Const puzzle.
#include <cstring>
#include <cstdint>
#include <cstdlib>
#include <cstddef>
#include <cstdio>
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
if (Size < 8) return 0;
int x;
unsigned y;
memcpy(&x, Data, sizeof(x));
memcpy(&y, Data + sizeof(x), sizeof(y));
if (abs(x) < 0 && y == 0xbaddcafe) {
printf("BINGO; Found the target, exiting; x = 0x%x y 0x%x\n", x, y);
exit(1);
}
return 0;
}

View file

@ -0,0 +1,17 @@
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
// Test with a more mallocs than frees, but no leak.
#include <cstdint>
#include <cstddef>
const int kAllocatedPointersSize = 10000;
int NumAllocatedPointers = 0;
int *AllocatedPointers[kAllocatedPointersSize];
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
if (NumAllocatedPointers < kAllocatedPointersSize)
AllocatedPointers[NumAllocatedPointers++] = new int;
return 0;
}

View file

@ -0,0 +1,23 @@
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
// Simple test for a fuzzer. The fuzzer must find the string "Hi!".
#include <assert.h>
#include <cstdint>
#include <cstdlib>
#include <cstddef>
#include <iostream>
static volatile bool SeedLargeBuffer;
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
assert(Data);
if (Size >= 4)
SeedLargeBuffer = true;
if (Size == 3 && SeedLargeBuffer && Data[3]) {
std::cout << "Woops, reading Data[3] w/o crashing\n";
exit(1);
}
return 0;
}

View file

@ -0,0 +1,217 @@
# Build all these tests with -O0, otherwise optimizations may merge some
# basic blocks and we'll fail to discover the targets.
# We change the flags for every build type because we might be doing
# a multi-configuration build (e.g. Xcode) where CMAKE_BUILD_TYPE doesn't
# mean anything.
set(variables_to_filter
CMAKE_CXX_FLAGS_RELEASE
CMAKE_CXX_FLAGS_DEBUG
CMAKE_CXX_FLAGS_RELWITHDEBINFO
CMAKE_CXX_FLAGS_MINSIZEREL
LIBFUZZER_FLAGS_BASE
)
foreach (VARNAME ${variables_to_filter})
string(REPLACE " " ";" BUILD_FLAGS_AS_LIST "${${VARNAME}}")
set(new_flags "")
foreach (flag ${BUILD_FLAGS_AS_LIST})
# NOTE: Use of XX here is to avoid a CMake warning due to CMP0054
if (NOT ("XX${flag}" MATCHES "XX-O[0123s]"))
set(new_flags "${new_flags} ${flag}")
else()
set(new_flags "${new_flags} -O0")
endif()
endforeach()
set(${VARNAME} "${new_flags}")
endforeach()
# Enable the coverage instrumentation (it is disabled for the Fuzzer lib).
set(CMAKE_CXX_FLAGS "${LIBFUZZER_FLAGS_BASE} -fsanitize-coverage=trace-pc-guard,indirect-calls,trace-cmp,trace-div,trace-gep -g")
# add_libfuzzer_test(<name>
# SOURCES source0.cpp [source1.cpp ...]
# )
#
# Declares a LibFuzzer test executable with target name LLVMFuzzer-<name>.
#
# One or more source files to be compiled into the binary must be declared
# after the SOURCES keyword.
function(add_libfuzzer_test name)
set(multi_arg_options "SOURCES")
cmake_parse_arguments(
"add_libfuzzer_test" "" "" "${multi_arg_options}" ${ARGN})
if ("${add_libfuzzer_test_SOURCES}" STREQUAL "")
message(FATAL_ERROR "Source files must be specified")
endif()
add_executable(LLVMFuzzer-${name}
${add_libfuzzer_test_SOURCES}
)
target_link_libraries(LLVMFuzzer-${name} LLVMFuzzer)
# Place binary where llvm-lit expects to find it
set_target_properties(LLVMFuzzer-${name}
PROPERTIES RUNTIME_OUTPUT_DIRECTORY
"${CMAKE_BINARY_DIR}/lib/Fuzzer/test"
)
set(TestBinaries ${TestBinaries} LLVMFuzzer-${name} PARENT_SCOPE)
endfunction()
# Variable to keep track of all test targets
set(TestBinaries)
###############################################################################
# Basic tests
###############################################################################
set(Tests
AbsNegAndConstantTest
AbsNegAndConstant64Test
AccumulateAllocationsTest
BufferOverflowOnInput
CallerCalleeTest
CounterTest
CustomCrossOverTest
CustomMutatorTest
DivTest
EmptyTest
FourIndependentBranchesTest
FullCoverageSetTest
InitializeTest
MemcmpTest
LeakTest
LeakTimeoutTest
LoadTest
NullDerefTest
NullDerefOnEmptyTest
NthRunCrashTest
OneHugeAllocTest
OutOfMemoryTest
OutOfMemorySingleLargeMallocTest
RepeatedMemcmp
RepeatedBytesTest
SimpleCmpTest
SimpleDictionaryTest
SimpleHashTest
SimpleTest
SimpleThreadedTest
SingleMemcmpTest
SingleStrcmpTest
SingleStrncmpTest
SpamyTest
ShrinkControlFlowTest
ShrinkValueProfileTest
StrcmpTest
StrncmpOOBTest
StrncmpTest
StrstrTest
SwapCmpTest
SwitchTest
Switch2Test
ThreadedLeakTest
ThreadedTest
TimeoutTest
TimeoutEmptyTest
TraceMallocTest
)
if(APPLE)
# LeakSanitizer is not supported on OSX right now
set(HAS_LSAN 0)
message(WARNING "LeakSanitizer is not supported on Apple platforms."
" Building and running LibFuzzer LeakSanitizer tests is disabled."
)
else()
set(HAS_LSAN 1)
endif()
foreach(Test ${Tests})
add_libfuzzer_test(${Test} SOURCES ${Test}.cpp)
endforeach()
###############################################################################
# Unit tests
###############################################################################
add_executable(LLVMFuzzer-Unittest
FuzzerUnittest.cpp
)
add_executable(LLVMFuzzer-StandaloneInitializeTest
InitializeTest.cpp
../standalone/StandaloneFuzzTargetMain.c
)
target_link_libraries(LLVMFuzzer-Unittest
gtest
gtest_main
LLVMFuzzerNoMain
)
target_include_directories(LLVMFuzzer-Unittest PRIVATE
"${LLVM_MAIN_SRC_DIR}/utils/unittest/googletest/include"
)
set(TestBinaries ${TestBinaries} LLVMFuzzer-Unittest)
set_target_properties(LLVMFuzzer-Unittest
PROPERTIES RUNTIME_OUTPUT_DIRECTORY
"${CMAKE_CURRENT_BINARY_DIR}"
)
set(TestBinaries ${TestBinaries} LLVMFuzzer-StandaloneInitializeTest)
set_target_properties(LLVMFuzzer-StandaloneInitializeTest
PROPERTIES RUNTIME_OUTPUT_DIRECTORY
"${CMAKE_CURRENT_BINARY_DIR}"
)
###############################################################################
# Additional tests
###############################################################################
include_directories(..)
# add_subdirectory(uninstrumented)
add_subdirectory(no-coverage)
add_subdirectory(ubsan)
add_library(LLVMFuzzer-DSO1 SHARED DSO1.cpp)
add_library(LLVMFuzzer-DSO2 SHARED DSO2.cpp)
add_executable(LLVMFuzzer-DSOTest
DSOTestMain.cpp
DSOTestExtra.cpp)
target_link_libraries(LLVMFuzzer-DSOTest
LLVMFuzzer-DSO1
LLVMFuzzer-DSO2
LLVMFuzzer
)
set_target_properties(LLVMFuzzer-DSOTest PROPERTIES RUNTIME_OUTPUT_DIRECTORY
"${CMAKE_BINARY_DIR}/lib/Fuzzer/test")
set_target_properties(LLVMFuzzer-DSO1 PROPERTIES LIBRARY_OUTPUT_DIRECTORY
"${CMAKE_BINARY_DIR}/lib/Fuzzer/lib")
set_target_properties(LLVMFuzzer-DSO2 PROPERTIES LIBRARY_OUTPUT_DIRECTORY
"${CMAKE_BINARY_DIR}/lib/Fuzzer/lib")
set(TestBinaries ${TestBinaries} LLVMFuzzer-DSOTest)
###############################################################################
# Configure lit to run the tests
#
# Note this is done after declaring all tests so we can inform lit if any tests
# need to be disabled.
###############################################################################
configure_lit_site_cfg(
${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in
${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg
)
configure_lit_site_cfg(
${CMAKE_CURRENT_SOURCE_DIR}/unit/lit.site.cfg.in
${CMAKE_CURRENT_BINARY_DIR}/unit/lit.site.cfg
)
add_lit_testsuite(check-fuzzer "Running Fuzzer tests"
${CMAKE_CURRENT_BINARY_DIR}
DEPENDS ${TestBinaries} FileCheck not
)

View file

@ -0,0 +1,59 @@
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
// Simple test for a fuzzer.
// Try to find the target using the indirect caller-callee pairs.
#include <cstdint>
#include <cstdlib>
#include <cstddef>
#include <cstring>
#include <iostream>
typedef void (*F)();
static F t[256];
void f34() {
std::cerr << "BINGO\n";
exit(1);
}
void f23() { t[(unsigned)'d'] = f34;}
void f12() { t[(unsigned)'c'] = f23;}
void f01() { t[(unsigned)'b'] = f12;}
void f00() {}
static F t0[256] = {
f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00,
f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00,
f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00,
f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00,
f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00,
f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00,
f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00,
f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00,
f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00,
f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00,
f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00,
f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00,
f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00,
f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00,
f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00,
f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00,
};
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
if (Size < 4) return 0;
// Spoof the counters.
for (int i = 0; i < 200; i++) {
f23();
f12();
f01();
}
memcpy(t, t0, sizeof(t));
t[(unsigned)'a'] = f01;
t[Data[0]]();
t[Data[1]]();
t[Data[2]]();
t[Data[3]]();
return 0;
}

View file

@ -0,0 +1,18 @@
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
// Test for a fuzzer: must find the case where a particular basic block is
// executed many times.
#include <iostream>
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
int Num = 0;
for (size_t i = 0; i < Size; i++)
if (Data[i] == 'A' + i)
Num++;
if (Num >= 4) {
std::cerr << "BINGO!\n";
exit(1);
}
return 0;
}

View file

@ -0,0 +1,63 @@
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
// Simple test for a cutom mutator.
#include <assert.h>
#include <cstddef>
#include <cstdint>
#include <cstdlib>
#include <iostream>
#include <random>
#include <string.h>
#include "FuzzerInterface.h"
static const char *Separator = "-_^_-";
static const char *Target = "012-_^_-abc";
static volatile int sink;
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
assert(Data);
std::string Str(reinterpret_cast<const char *>(Data), Size);
// Ensure that two different elements exist in the corpus.
if (Size && Data[0] == '0') sink++;
if (Size && Data[0] == 'a') sink--;
if (Str.find(Target) != std::string::npos) {
std::cout << "BINGO; Found the target, exiting\n";
exit(1);
}
return 0;
}
extern "C" 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) {
static bool Printed;
static size_t SeparatorLen = strlen(Separator);
if (!Printed) {
std::cerr << "In LLVMFuzzerCustomCrossover\n";
Printed = true;
}
std::mt19937 R(Seed);
size_t Offset1 = 0;
size_t Len1 = R() % (Size1 - Offset1);
size_t Offset2 = 0;
size_t Len2 = R() % (Size2 - Offset2);
size_t Size = Len1 + Len2 + SeparatorLen;
if (Size > MaxOutSize)
return 0;
memcpy(Out, Data1 + Offset1, Len1);
memcpy(Out + Len1, Separator, SeparatorLen);
memcpy(Out + Len1 + SeparatorLen, Data2 + Offset2, Len2);
return Len1 + Len2 + SeparatorLen;
}

View file

@ -0,0 +1,38 @@
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
// Simple test for a cutom mutator.
#include <assert.h>
#include <cstdint>
#include <cstdlib>
#include <cstddef>
#include <iostream>
#include "FuzzerInterface.h"
static volatile int Sink;
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
assert(Data);
if (Size > 0 && Data[0] == 'H') {
Sink = 1;
if (Size > 1 && Data[1] == 'i') {
Sink = 2;
if (Size > 2 && Data[2] == '!') {
std::cout << "BINGO; Found the target, exiting\n";
exit(1);
}
}
}
return 0;
}
extern "C" size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size,
size_t MaxSize, unsigned int Seed) {
static bool Printed;
if (!Printed) {
std::cerr << "In LLVMFuzzerCustomMutator\n";
Printed = true;
}
return LLVMFuzzerMutate(Data, Size, MaxSize);
}

View file

@ -0,0 +1,12 @@
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
// Source code for a simple DSO.
int DSO1(int a) {
if (a < 123456)
return 0;
return 1;
}
void Uncovered1() { }

View file

@ -0,0 +1,12 @@
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
// Source code for a simple DSO.
int DSO2(int a) {
if (a < 3598235)
return 0;
return 1;
}
void Uncovered2() {}

View file

@ -0,0 +1,11 @@
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
// Source code for a simple DSO.
int DSOTestExtra(int a) {
if (a < 452345)
return 0;
return 1;
}

View file

@ -0,0 +1,31 @@
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
// Source code for a simple DSO.
#include <cstdint>
#include <cstdlib>
#include <cstring>
#include <cstdio>
extern int DSO1(int a);
extern int DSO2(int a);
extern int DSOTestExtra(int a);
static volatile int *nil = 0;
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
int x, y, z;
if (Size < sizeof(int) * 3) {
x = y = z = 0;
} else {
memcpy(&x, Data + 0 * sizeof(int), sizeof(int));
memcpy(&y, Data + 1 * sizeof(int), sizeof(int));
memcpy(&z, Data + 2 * sizeof(int), sizeof(int));
}
int sum = DSO1(x) + DSO2(y) + (z ? DSOTestExtra(z) : 0);
if (sum == 3) {
fprintf(stderr, "BINGO %d %d %d\n", x, y, z);
*nil = 0;
}
return 0;
}

View file

@ -0,0 +1,20 @@
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
// Simple test for a fuzzer: find the interesting argument for div.
#include <assert.h>
#include <cstdint>
#include <cstring>
#include <cstddef>
#include <iostream>
static volatile int Sink;
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
if (Size < 4) return 0;
int a;
memcpy(&a, Data, 4);
Sink = 12345678 / (987654 - a);
return 0;
}

View file

@ -0,0 +1,11 @@
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
// A fuzzer with empty target function.
#include <cstdint>
#include <cstdlib>
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
return 0;
}

View file

@ -0,0 +1,22 @@
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
// Simple test for a fuzzer. The fuzzer must find the string "FUZZ".
#include <cstdint>
#include <cstdlib>
#include <cstddef>
#include <iostream>
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
int bits = 0;
if (Size > 0 && Data[0] == 'F') bits |= 1;
if (Size > 1 && Data[1] == 'U') bits |= 2;
if (Size > 2 && Data[2] == 'Z') bits |= 4;
if (Size > 3 && Data[3] == 'Z') bits |= 8;
if (bits == 15) {
std::cerr << "BINGO!\n";
exit(1);
}
return 0;
}

View file

@ -0,0 +1,24 @@
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
// Simple test for a fuzzer. The fuzzer must find the string "FUZZER".
#include <cstdint>
#include <cstdlib>
#include <cstddef>
#include <iostream>
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
int bits = 0;
if (Size > 0 && Data[0] == 'F') bits |= 1;
if (Size > 1 && Data[1] == 'U') bits |= 2;
if (Size > 2 && Data[2] == 'Z') bits |= 4;
if (Size > 3 && Data[3] == 'Z') bits |= 8;
if (Size > 4 && Data[4] == 'E') bits |= 16;
if (Size > 5 && Data[5] == 'R') bits |= 32;
if (bits == 63) {
std::cerr << "BINGO!\n";
exit(1);
}
return 0;
}

View file

@ -0,0 +1,738 @@
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
// Avoid ODR violations (LibFuzzer is built without ASan and this test is built
// with ASan) involving C++ standard library types when using libcxx.
#define _LIBCPP_HAS_NO_ASAN
#include "FuzzerCorpus.h"
#include "FuzzerInternal.h"
#include "FuzzerDictionary.h"
#include "FuzzerMerge.h"
#include "FuzzerMutate.h"
#include "FuzzerRandom.h"
#include "gtest/gtest.h"
#include <memory>
#include <set>
using namespace fuzzer;
// For now, have LLVMFuzzerTestOneInput just to make it link.
// Later we may want to make unittests that actually call LLVMFuzzerTestOneInput.
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
abort();
}
TEST(Fuzzer, CrossOver) {
std::unique_ptr<ExternalFunctions> t(new ExternalFunctions());
fuzzer::EF = t.get();
Random Rand(0);
MutationDispatcher MD(Rand, {});
Unit A({0, 1, 2}), B({5, 6, 7});
Unit C;
Unit Expected[] = {
{ 0 },
{ 0, 1 },
{ 0, 5 },
{ 0, 1, 2 },
{ 0, 1, 5 },
{ 0, 5, 1 },
{ 0, 5, 6 },
{ 0, 1, 2, 5 },
{ 0, 1, 5, 2 },
{ 0, 1, 5, 6 },
{ 0, 5, 1, 2 },
{ 0, 5, 1, 6 },
{ 0, 5, 6, 1 },
{ 0, 5, 6, 7 },
{ 0, 1, 2, 5, 6 },
{ 0, 1, 5, 2, 6 },
{ 0, 1, 5, 6, 2 },
{ 0, 1, 5, 6, 7 },
{ 0, 5, 1, 2, 6 },
{ 0, 5, 1, 6, 2 },
{ 0, 5, 1, 6, 7 },
{ 0, 5, 6, 1, 2 },
{ 0, 5, 6, 1, 7 },
{ 0, 5, 6, 7, 1 },
{ 0, 1, 2, 5, 6, 7 },
{ 0, 1, 5, 2, 6, 7 },
{ 0, 1, 5, 6, 2, 7 },
{ 0, 1, 5, 6, 7, 2 },
{ 0, 5, 1, 2, 6, 7 },
{ 0, 5, 1, 6, 2, 7 },
{ 0, 5, 1, 6, 7, 2 },
{ 0, 5, 6, 1, 2, 7 },
{ 0, 5, 6, 1, 7, 2 },
{ 0, 5, 6, 7, 1, 2 }
};
for (size_t Len = 1; Len < 8; Len++) {
std::set<Unit> FoundUnits, ExpectedUnitsWitThisLength;
for (int Iter = 0; Iter < 3000; Iter++) {
C.resize(Len);
size_t NewSize = MD.CrossOver(A.data(), A.size(), B.data(), B.size(),
C.data(), C.size());
C.resize(NewSize);
FoundUnits.insert(C);
}
for (const Unit &U : Expected)
if (U.size() <= Len)
ExpectedUnitsWitThisLength.insert(U);
EXPECT_EQ(ExpectedUnitsWitThisLength, FoundUnits);
}
}
TEST(Fuzzer, Hash) {
uint8_t A[] = {'a', 'b', 'c'};
fuzzer::Unit U(A, A + sizeof(A));
EXPECT_EQ("a9993e364706816aba3e25717850c26c9cd0d89d", fuzzer::Hash(U));
U.push_back('d');
EXPECT_EQ("81fe8bfe87576c3ecb22426f8e57847382917acf", fuzzer::Hash(U));
}
typedef size_t (MutationDispatcher::*Mutator)(uint8_t *Data, size_t Size,
size_t MaxSize);
void TestEraseBytes(Mutator M, int NumIter) {
std::unique_ptr<ExternalFunctions> t(new ExternalFunctions());
fuzzer::EF = t.get();
uint8_t REM0[8] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77};
uint8_t REM1[8] = {0x00, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77};
uint8_t REM2[8] = {0x00, 0x11, 0x33, 0x44, 0x55, 0x66, 0x77};
uint8_t REM3[8] = {0x00, 0x11, 0x22, 0x44, 0x55, 0x66, 0x77};
uint8_t REM4[8] = {0x00, 0x11, 0x22, 0x33, 0x55, 0x66, 0x77};
uint8_t REM5[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x66, 0x77};
uint8_t REM6[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x77};
uint8_t REM7[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66};
uint8_t REM8[6] = {0x22, 0x33, 0x44, 0x55, 0x66, 0x77};
uint8_t REM9[6] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55};
uint8_t REM10[6] = {0x00, 0x11, 0x22, 0x55, 0x66, 0x77};
uint8_t REM11[5] = {0x33, 0x44, 0x55, 0x66, 0x77};
uint8_t REM12[5] = {0x00, 0x11, 0x22, 0x33, 0x44};
uint8_t REM13[5] = {0x00, 0x44, 0x55, 0x66, 0x77};
Random Rand(0);
MutationDispatcher MD(Rand, {});
int FoundMask = 0;
for (int i = 0; i < NumIter; i++) {
uint8_t T[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77};
size_t NewSize = (MD.*M)(T, sizeof(T), sizeof(T));
if (NewSize == 7 && !memcmp(REM0, T, 7)) FoundMask |= 1 << 0;
if (NewSize == 7 && !memcmp(REM1, T, 7)) FoundMask |= 1 << 1;
if (NewSize == 7 && !memcmp(REM2, T, 7)) FoundMask |= 1 << 2;
if (NewSize == 7 && !memcmp(REM3, T, 7)) FoundMask |= 1 << 3;
if (NewSize == 7 && !memcmp(REM4, T, 7)) FoundMask |= 1 << 4;
if (NewSize == 7 && !memcmp(REM5, T, 7)) FoundMask |= 1 << 5;
if (NewSize == 7 && !memcmp(REM6, T, 7)) FoundMask |= 1 << 6;
if (NewSize == 7 && !memcmp(REM7, T, 7)) FoundMask |= 1 << 7;
if (NewSize == 6 && !memcmp(REM8, T, 6)) FoundMask |= 1 << 8;
if (NewSize == 6 && !memcmp(REM9, T, 6)) FoundMask |= 1 << 9;
if (NewSize == 6 && !memcmp(REM10, T, 6)) FoundMask |= 1 << 10;
if (NewSize == 5 && !memcmp(REM11, T, 5)) FoundMask |= 1 << 11;
if (NewSize == 5 && !memcmp(REM12, T, 5)) FoundMask |= 1 << 12;
if (NewSize == 5 && !memcmp(REM13, T, 5)) FoundMask |= 1 << 13;
}
EXPECT_EQ(FoundMask, (1 << 14) - 1);
}
TEST(FuzzerMutate, EraseBytes1) {
TestEraseBytes(&MutationDispatcher::Mutate_EraseBytes, 200);
}
TEST(FuzzerMutate, EraseBytes2) {
TestEraseBytes(&MutationDispatcher::Mutate, 2000);
}
void TestInsertByte(Mutator M, int NumIter) {
std::unique_ptr<ExternalFunctions> t(new ExternalFunctions());
fuzzer::EF = t.get();
Random Rand(0);
MutationDispatcher MD(Rand, {});
int FoundMask = 0;
uint8_t INS0[8] = {0xF1, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66};
uint8_t INS1[8] = {0x00, 0xF2, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66};
uint8_t INS2[8] = {0x00, 0x11, 0xF3, 0x22, 0x33, 0x44, 0x55, 0x66};
uint8_t INS3[8] = {0x00, 0x11, 0x22, 0xF4, 0x33, 0x44, 0x55, 0x66};
uint8_t INS4[8] = {0x00, 0x11, 0x22, 0x33, 0xF5, 0x44, 0x55, 0x66};
uint8_t INS5[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0xF6, 0x55, 0x66};
uint8_t INS6[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0xF7, 0x66};
uint8_t INS7[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0xF8};
for (int i = 0; i < NumIter; i++) {
uint8_t T[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66};
size_t NewSize = (MD.*M)(T, 7, 8);
if (NewSize == 8 && !memcmp(INS0, T, 8)) FoundMask |= 1 << 0;
if (NewSize == 8 && !memcmp(INS1, T, 8)) FoundMask |= 1 << 1;
if (NewSize == 8 && !memcmp(INS2, T, 8)) FoundMask |= 1 << 2;
if (NewSize == 8 && !memcmp(INS3, T, 8)) FoundMask |= 1 << 3;
if (NewSize == 8 && !memcmp(INS4, T, 8)) FoundMask |= 1 << 4;
if (NewSize == 8 && !memcmp(INS5, T, 8)) FoundMask |= 1 << 5;
if (NewSize == 8 && !memcmp(INS6, T, 8)) FoundMask |= 1 << 6;
if (NewSize == 8 && !memcmp(INS7, T, 8)) FoundMask |= 1 << 7;
}
EXPECT_EQ(FoundMask, 255);
}
TEST(FuzzerMutate, InsertByte1) {
TestInsertByte(&MutationDispatcher::Mutate_InsertByte, 1 << 15);
}
TEST(FuzzerMutate, InsertByte2) {
TestInsertByte(&MutationDispatcher::Mutate, 1 << 17);
}
void TestInsertRepeatedBytes(Mutator M, int NumIter) {
std::unique_ptr<ExternalFunctions> t(new ExternalFunctions());
fuzzer::EF = t.get();
Random Rand(0);
MutationDispatcher MD(Rand, {});
int FoundMask = 0;
uint8_t INS0[7] = {0x00, 0x11, 0x22, 0x33, 'a', 'a', 'a'};
uint8_t INS1[7] = {0x00, 0x11, 0x22, 'a', 'a', 'a', 0x33};
uint8_t INS2[7] = {0x00, 0x11, 'a', 'a', 'a', 0x22, 0x33};
uint8_t INS3[7] = {0x00, 'a', 'a', 'a', 0x11, 0x22, 0x33};
uint8_t INS4[7] = {'a', 'a', 'a', 0x00, 0x11, 0x22, 0x33};
uint8_t INS5[8] = {0x00, 0x11, 0x22, 0x33, 'b', 'b', 'b', 'b'};
uint8_t INS6[8] = {0x00, 0x11, 0x22, 'b', 'b', 'b', 'b', 0x33};
uint8_t INS7[8] = {0x00, 0x11, 'b', 'b', 'b', 'b', 0x22, 0x33};
uint8_t INS8[8] = {0x00, 'b', 'b', 'b', 'b', 0x11, 0x22, 0x33};
uint8_t INS9[8] = {'b', 'b', 'b', 'b', 0x00, 0x11, 0x22, 0x33};
for (int i = 0; i < NumIter; i++) {
uint8_t T[8] = {0x00, 0x11, 0x22, 0x33};
size_t NewSize = (MD.*M)(T, 4, 8);
if (NewSize == 7 && !memcmp(INS0, T, 7)) FoundMask |= 1 << 0;
if (NewSize == 7 && !memcmp(INS1, T, 7)) FoundMask |= 1 << 1;
if (NewSize == 7 && !memcmp(INS2, T, 7)) FoundMask |= 1 << 2;
if (NewSize == 7 && !memcmp(INS3, T, 7)) FoundMask |= 1 << 3;
if (NewSize == 7 && !memcmp(INS4, T, 7)) FoundMask |= 1 << 4;
if (NewSize == 8 && !memcmp(INS5, T, 8)) FoundMask |= 1 << 5;
if (NewSize == 8 && !memcmp(INS6, T, 8)) FoundMask |= 1 << 6;
if (NewSize == 8 && !memcmp(INS7, T, 8)) FoundMask |= 1 << 7;
if (NewSize == 8 && !memcmp(INS8, T, 8)) FoundMask |= 1 << 8;
if (NewSize == 8 && !memcmp(INS9, T, 8)) FoundMask |= 1 << 9;
}
EXPECT_EQ(FoundMask, (1 << 10) - 1);
}
TEST(FuzzerMutate, InsertRepeatedBytes1) {
TestInsertRepeatedBytes(&MutationDispatcher::Mutate_InsertRepeatedBytes, 10000);
}
TEST(FuzzerMutate, InsertRepeatedBytes2) {
TestInsertRepeatedBytes(&MutationDispatcher::Mutate, 300000);
}
void TestChangeByte(Mutator M, int NumIter) {
std::unique_ptr<ExternalFunctions> t(new ExternalFunctions());
fuzzer::EF = t.get();
Random Rand(0);
MutationDispatcher MD(Rand, {});
int FoundMask = 0;
uint8_t CH0[8] = {0xF0, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77};
uint8_t CH1[8] = {0x00, 0xF1, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77};
uint8_t CH2[8] = {0x00, 0x11, 0xF2, 0x33, 0x44, 0x55, 0x66, 0x77};
uint8_t CH3[8] = {0x00, 0x11, 0x22, 0xF3, 0x44, 0x55, 0x66, 0x77};
uint8_t CH4[8] = {0x00, 0x11, 0x22, 0x33, 0xF4, 0x55, 0x66, 0x77};
uint8_t CH5[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0xF5, 0x66, 0x77};
uint8_t CH6[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0xF5, 0x77};
uint8_t CH7[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0xF7};
for (int i = 0; i < NumIter; i++) {
uint8_t T[9] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77};
size_t NewSize = (MD.*M)(T, 8, 9);
if (NewSize == 8 && !memcmp(CH0, T, 8)) FoundMask |= 1 << 0;
if (NewSize == 8 && !memcmp(CH1, T, 8)) FoundMask |= 1 << 1;
if (NewSize == 8 && !memcmp(CH2, T, 8)) FoundMask |= 1 << 2;
if (NewSize == 8 && !memcmp(CH3, T, 8)) FoundMask |= 1 << 3;
if (NewSize == 8 && !memcmp(CH4, T, 8)) FoundMask |= 1 << 4;
if (NewSize == 8 && !memcmp(CH5, T, 8)) FoundMask |= 1 << 5;
if (NewSize == 8 && !memcmp(CH6, T, 8)) FoundMask |= 1 << 6;
if (NewSize == 8 && !memcmp(CH7, T, 8)) FoundMask |= 1 << 7;
}
EXPECT_EQ(FoundMask, 255);
}
TEST(FuzzerMutate, ChangeByte1) {
TestChangeByte(&MutationDispatcher::Mutate_ChangeByte, 1 << 15);
}
TEST(FuzzerMutate, ChangeByte2) {
TestChangeByte(&MutationDispatcher::Mutate, 1 << 17);
}
void TestChangeBit(Mutator M, int NumIter) {
std::unique_ptr<ExternalFunctions> t(new ExternalFunctions());
fuzzer::EF = t.get();
Random Rand(0);
MutationDispatcher MD(Rand, {});
int FoundMask = 0;
uint8_t CH0[8] = {0x01, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77};
uint8_t CH1[8] = {0x00, 0x13, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77};
uint8_t CH2[8] = {0x00, 0x11, 0x02, 0x33, 0x44, 0x55, 0x66, 0x77};
uint8_t CH3[8] = {0x00, 0x11, 0x22, 0x37, 0x44, 0x55, 0x66, 0x77};
uint8_t CH4[8] = {0x00, 0x11, 0x22, 0x33, 0x54, 0x55, 0x66, 0x77};
uint8_t CH5[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x54, 0x66, 0x77};
uint8_t CH6[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x76, 0x77};
uint8_t CH7[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0xF7};
for (int i = 0; i < NumIter; i++) {
uint8_t T[9] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77};
size_t NewSize = (MD.*M)(T, 8, 9);
if (NewSize == 8 && !memcmp(CH0, T, 8)) FoundMask |= 1 << 0;
if (NewSize == 8 && !memcmp(CH1, T, 8)) FoundMask |= 1 << 1;
if (NewSize == 8 && !memcmp(CH2, T, 8)) FoundMask |= 1 << 2;
if (NewSize == 8 && !memcmp(CH3, T, 8)) FoundMask |= 1 << 3;
if (NewSize == 8 && !memcmp(CH4, T, 8)) FoundMask |= 1 << 4;
if (NewSize == 8 && !memcmp(CH5, T, 8)) FoundMask |= 1 << 5;
if (NewSize == 8 && !memcmp(CH6, T, 8)) FoundMask |= 1 << 6;
if (NewSize == 8 && !memcmp(CH7, T, 8)) FoundMask |= 1 << 7;
}
EXPECT_EQ(FoundMask, 255);
}
TEST(FuzzerMutate, ChangeBit1) {
TestChangeBit(&MutationDispatcher::Mutate_ChangeBit, 1 << 16);
}
TEST(FuzzerMutate, ChangeBit2) {
TestChangeBit(&MutationDispatcher::Mutate, 1 << 18);
}
void TestShuffleBytes(Mutator M, int NumIter) {
std::unique_ptr<ExternalFunctions> t(new ExternalFunctions());
fuzzer::EF = t.get();
Random Rand(0);
MutationDispatcher MD(Rand, {});
int FoundMask = 0;
uint8_t CH0[7] = {0x00, 0x22, 0x11, 0x33, 0x44, 0x55, 0x66};
uint8_t CH1[7] = {0x11, 0x00, 0x33, 0x22, 0x44, 0x55, 0x66};
uint8_t CH2[7] = {0x00, 0x33, 0x11, 0x22, 0x44, 0x55, 0x66};
uint8_t CH3[7] = {0x00, 0x11, 0x22, 0x44, 0x55, 0x66, 0x33};
uint8_t CH4[7] = {0x00, 0x11, 0x22, 0x33, 0x55, 0x44, 0x66};
for (int i = 0; i < NumIter; i++) {
uint8_t T[7] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66};
size_t NewSize = (MD.*M)(T, 7, 7);
if (NewSize == 7 && !memcmp(CH0, T, 7)) FoundMask |= 1 << 0;
if (NewSize == 7 && !memcmp(CH1, T, 7)) FoundMask |= 1 << 1;
if (NewSize == 7 && !memcmp(CH2, T, 7)) FoundMask |= 1 << 2;
if (NewSize == 7 && !memcmp(CH3, T, 7)) FoundMask |= 1 << 3;
if (NewSize == 7 && !memcmp(CH4, T, 7)) FoundMask |= 1 << 4;
}
EXPECT_EQ(FoundMask, 31);
}
TEST(FuzzerMutate, ShuffleBytes1) {
TestShuffleBytes(&MutationDispatcher::Mutate_ShuffleBytes, 1 << 16);
}
TEST(FuzzerMutate, ShuffleBytes2) {
TestShuffleBytes(&MutationDispatcher::Mutate, 1 << 20);
}
void TestCopyPart(Mutator M, int NumIter) {
std::unique_ptr<ExternalFunctions> t(new ExternalFunctions());
fuzzer::EF = t.get();
Random Rand(0);
MutationDispatcher MD(Rand, {});
int FoundMask = 0;
uint8_t CH0[7] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x00, 0x11};
uint8_t CH1[7] = {0x55, 0x66, 0x22, 0x33, 0x44, 0x55, 0x66};
uint8_t CH2[7] = {0x00, 0x55, 0x66, 0x33, 0x44, 0x55, 0x66};
uint8_t CH3[7] = {0x00, 0x11, 0x22, 0x00, 0x11, 0x22, 0x66};
uint8_t CH4[7] = {0x00, 0x11, 0x11, 0x22, 0x33, 0x55, 0x66};
for (int i = 0; i < NumIter; i++) {
uint8_t T[7] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66};
size_t NewSize = (MD.*M)(T, 7, 7);
if (NewSize == 7 && !memcmp(CH0, T, 7)) FoundMask |= 1 << 0;
if (NewSize == 7 && !memcmp(CH1, T, 7)) FoundMask |= 1 << 1;
if (NewSize == 7 && !memcmp(CH2, T, 7)) FoundMask |= 1 << 2;
if (NewSize == 7 && !memcmp(CH3, T, 7)) FoundMask |= 1 << 3;
if (NewSize == 7 && !memcmp(CH4, T, 7)) FoundMask |= 1 << 4;
}
uint8_t CH5[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x00, 0x11, 0x22};
uint8_t CH6[8] = {0x22, 0x33, 0x44, 0x00, 0x11, 0x22, 0x33, 0x44};
uint8_t CH7[8] = {0x00, 0x11, 0x22, 0x00, 0x11, 0x22, 0x33, 0x44};
uint8_t CH8[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x22, 0x33, 0x44};
uint8_t CH9[8] = {0x00, 0x11, 0x22, 0x22, 0x33, 0x44, 0x33, 0x44};
for (int i = 0; i < NumIter; i++) {
uint8_t T[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77};
size_t NewSize = (MD.*M)(T, 5, 8);
if (NewSize == 8 && !memcmp(CH5, T, 8)) FoundMask |= 1 << 5;
if (NewSize == 8 && !memcmp(CH6, T, 8)) FoundMask |= 1 << 6;
if (NewSize == 8 && !memcmp(CH7, T, 8)) FoundMask |= 1 << 7;
if (NewSize == 8 && !memcmp(CH8, T, 8)) FoundMask |= 1 << 8;
if (NewSize == 8 && !memcmp(CH9, T, 8)) FoundMask |= 1 << 9;
}
EXPECT_EQ(FoundMask, 1023);
}
TEST(FuzzerMutate, CopyPart1) {
TestCopyPart(&MutationDispatcher::Mutate_CopyPart, 1 << 10);
}
TEST(FuzzerMutate, CopyPart2) {
TestCopyPart(&MutationDispatcher::Mutate, 1 << 13);
}
void TestAddWordFromDictionary(Mutator M, int NumIter) {
std::unique_ptr<ExternalFunctions> t(new ExternalFunctions());
fuzzer::EF = t.get();
Random Rand(0);
MutationDispatcher MD(Rand, {});
uint8_t Word1[4] = {0xAA, 0xBB, 0xCC, 0xDD};
uint8_t Word2[3] = {0xFF, 0xEE, 0xEF};
MD.AddWordToManualDictionary(Word(Word1, sizeof(Word1)));
MD.AddWordToManualDictionary(Word(Word2, sizeof(Word2)));
int FoundMask = 0;
uint8_t CH0[7] = {0x00, 0x11, 0x22, 0xAA, 0xBB, 0xCC, 0xDD};
uint8_t CH1[7] = {0x00, 0x11, 0xAA, 0xBB, 0xCC, 0xDD, 0x22};
uint8_t CH2[7] = {0x00, 0xAA, 0xBB, 0xCC, 0xDD, 0x11, 0x22};
uint8_t CH3[7] = {0xAA, 0xBB, 0xCC, 0xDD, 0x00, 0x11, 0x22};
uint8_t CH4[6] = {0x00, 0x11, 0x22, 0xFF, 0xEE, 0xEF};
uint8_t CH5[6] = {0x00, 0x11, 0xFF, 0xEE, 0xEF, 0x22};
uint8_t CH6[6] = {0x00, 0xFF, 0xEE, 0xEF, 0x11, 0x22};
uint8_t CH7[6] = {0xFF, 0xEE, 0xEF, 0x00, 0x11, 0x22};
for (int i = 0; i < NumIter; i++) {
uint8_t T[7] = {0x00, 0x11, 0x22};
size_t NewSize = (MD.*M)(T, 3, 7);
if (NewSize == 7 && !memcmp(CH0, T, 7)) FoundMask |= 1 << 0;
if (NewSize == 7 && !memcmp(CH1, T, 7)) FoundMask |= 1 << 1;
if (NewSize == 7 && !memcmp(CH2, T, 7)) FoundMask |= 1 << 2;
if (NewSize == 7 && !memcmp(CH3, T, 7)) FoundMask |= 1 << 3;
if (NewSize == 6 && !memcmp(CH4, T, 6)) FoundMask |= 1 << 4;
if (NewSize == 6 && !memcmp(CH5, T, 6)) FoundMask |= 1 << 5;
if (NewSize == 6 && !memcmp(CH6, T, 6)) FoundMask |= 1 << 6;
if (NewSize == 6 && !memcmp(CH7, T, 6)) FoundMask |= 1 << 7;
}
EXPECT_EQ(FoundMask, 255);
}
TEST(FuzzerMutate, AddWordFromDictionary1) {
TestAddWordFromDictionary(
&MutationDispatcher::Mutate_AddWordFromManualDictionary, 1 << 15);
}
TEST(FuzzerMutate, AddWordFromDictionary2) {
TestAddWordFromDictionary(&MutationDispatcher::Mutate, 1 << 15);
}
void TestAddWordFromDictionaryWithHint(Mutator M, int NumIter) {
std::unique_ptr<ExternalFunctions> t(new ExternalFunctions());
fuzzer::EF = t.get();
Random Rand(0);
MutationDispatcher MD(Rand, {});
uint8_t W[] = {0xAA, 0xBB, 0xCC, 0xDD, 0xFF, 0xEE, 0xEF};
size_t PosHint = 7777;
MD.AddWordToAutoDictionary({Word(W, sizeof(W)), PosHint});
int FoundMask = 0;
for (int i = 0; i < NumIter; i++) {
uint8_t T[10000];
memset(T, 0, sizeof(T));
size_t NewSize = (MD.*M)(T, 9000, 10000);
if (NewSize >= PosHint + sizeof(W) &&
!memcmp(W, T + PosHint, sizeof(W)))
FoundMask = 1;
}
EXPECT_EQ(FoundMask, 1);
}
TEST(FuzzerMutate, AddWordFromDictionaryWithHint1) {
TestAddWordFromDictionaryWithHint(
&MutationDispatcher::Mutate_AddWordFromTemporaryAutoDictionary, 1 << 5);
}
TEST(FuzzerMutate, AddWordFromDictionaryWithHint2) {
TestAddWordFromDictionaryWithHint(&MutationDispatcher::Mutate, 1 << 10);
}
void TestChangeASCIIInteger(Mutator M, int NumIter) {
std::unique_ptr<ExternalFunctions> t(new ExternalFunctions());
fuzzer::EF = t.get();
Random Rand(0);
MutationDispatcher MD(Rand, {});
uint8_t CH0[8] = {'1', '2', '3', '4', '5', '6', '7', '7'};
uint8_t CH1[8] = {'1', '2', '3', '4', '5', '6', '7', '9'};
uint8_t CH2[8] = {'2', '4', '6', '9', '1', '3', '5', '6'};
uint8_t CH3[8] = {'0', '6', '1', '7', '2', '8', '3', '9'};
int FoundMask = 0;
for (int i = 0; i < NumIter; i++) {
uint8_t T[8] = {'1', '2', '3', '4', '5', '6', '7', '8'};
size_t NewSize = (MD.*M)(T, 8, 8);
/**/ if (NewSize == 8 && !memcmp(CH0, T, 8)) FoundMask |= 1 << 0;
else if (NewSize == 8 && !memcmp(CH1, T, 8)) FoundMask |= 1 << 1;
else if (NewSize == 8 && !memcmp(CH2, T, 8)) FoundMask |= 1 << 2;
else if (NewSize == 8 && !memcmp(CH3, T, 8)) FoundMask |= 1 << 3;
else if (NewSize == 8) FoundMask |= 1 << 4;
}
EXPECT_EQ(FoundMask, 31);
}
TEST(FuzzerMutate, ChangeASCIIInteger1) {
TestChangeASCIIInteger(&MutationDispatcher::Mutate_ChangeASCIIInteger,
1 << 15);
}
TEST(FuzzerMutate, ChangeASCIIInteger2) {
TestChangeASCIIInteger(&MutationDispatcher::Mutate, 1 << 15);
}
void TestChangeBinaryInteger(Mutator M, int NumIter) {
std::unique_ptr<ExternalFunctions> t(new ExternalFunctions());
fuzzer::EF = t.get();
Random Rand(0);
MutationDispatcher MD(Rand, {});
uint8_t CH0[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x79};
uint8_t CH1[8] = {0x00, 0x11, 0x22, 0x31, 0x44, 0x55, 0x66, 0x77};
uint8_t CH2[8] = {0xff, 0x10, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77};
uint8_t CH3[8] = {0x00, 0x11, 0x2a, 0x33, 0x44, 0x55, 0x66, 0x77};
uint8_t CH4[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x4f, 0x66, 0x77};
uint8_t CH5[8] = {0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88};
uint8_t CH6[8] = {0x00, 0x11, 0x22, 0x00, 0x00, 0x00, 0x08, 0x77}; // Size
uint8_t CH7[8] = {0x00, 0x08, 0x00, 0x33, 0x44, 0x55, 0x66, 0x77}; // Sw(Size)
int FoundMask = 0;
for (int i = 0; i < NumIter; i++) {
uint8_t T[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77};
size_t NewSize = (MD.*M)(T, 8, 8);
/**/ if (NewSize == 8 && !memcmp(CH0, T, 8)) FoundMask |= 1 << 0;
else if (NewSize == 8 && !memcmp(CH1, T, 8)) FoundMask |= 1 << 1;
else if (NewSize == 8 && !memcmp(CH2, T, 8)) FoundMask |= 1 << 2;
else if (NewSize == 8 && !memcmp(CH3, T, 8)) FoundMask |= 1 << 3;
else if (NewSize == 8 && !memcmp(CH4, T, 8)) FoundMask |= 1 << 4;
else if (NewSize == 8 && !memcmp(CH5, T, 8)) FoundMask |= 1 << 5;
else if (NewSize == 8 && !memcmp(CH6, T, 8)) FoundMask |= 1 << 6;
else if (NewSize == 8 && !memcmp(CH7, T, 8)) FoundMask |= 1 << 7;
}
EXPECT_EQ(FoundMask, 255);
}
TEST(FuzzerMutate, ChangeBinaryInteger1) {
TestChangeBinaryInteger(&MutationDispatcher::Mutate_ChangeBinaryInteger,
1 << 12);
}
TEST(FuzzerMutate, ChangeBinaryInteger2) {
TestChangeBinaryInteger(&MutationDispatcher::Mutate, 1 << 15);
}
TEST(FuzzerDictionary, ParseOneDictionaryEntry) {
Unit U;
EXPECT_FALSE(ParseOneDictionaryEntry("", &U));
EXPECT_FALSE(ParseOneDictionaryEntry(" ", &U));
EXPECT_FALSE(ParseOneDictionaryEntry("\t ", &U));
EXPECT_FALSE(ParseOneDictionaryEntry(" \" ", &U));
EXPECT_FALSE(ParseOneDictionaryEntry(" zz\" ", &U));
EXPECT_FALSE(ParseOneDictionaryEntry(" \"zz ", &U));
EXPECT_FALSE(ParseOneDictionaryEntry(" \"\" ", &U));
EXPECT_TRUE(ParseOneDictionaryEntry("\"a\"", &U));
EXPECT_EQ(U, Unit({'a'}));
EXPECT_TRUE(ParseOneDictionaryEntry("\"abc\"", &U));
EXPECT_EQ(U, Unit({'a', 'b', 'c'}));
EXPECT_TRUE(ParseOneDictionaryEntry("abc=\"abc\"", &U));
EXPECT_EQ(U, Unit({'a', 'b', 'c'}));
EXPECT_FALSE(ParseOneDictionaryEntry("\"\\\"", &U));
EXPECT_TRUE(ParseOneDictionaryEntry("\"\\\\\"", &U));
EXPECT_EQ(U, Unit({'\\'}));
EXPECT_TRUE(ParseOneDictionaryEntry("\"\\xAB\"", &U));
EXPECT_EQ(U, Unit({0xAB}));
EXPECT_TRUE(ParseOneDictionaryEntry("\"\\xABz\\xDE\"", &U));
EXPECT_EQ(U, Unit({0xAB, 'z', 0xDE}));
EXPECT_TRUE(ParseOneDictionaryEntry("\"#\"", &U));
EXPECT_EQ(U, Unit({'#'}));
EXPECT_TRUE(ParseOneDictionaryEntry("\"\\\"\"", &U));
EXPECT_EQ(U, Unit({'"'}));
}
TEST(FuzzerDictionary, ParseDictionaryFile) {
std::vector<Unit> Units;
EXPECT_FALSE(ParseDictionaryFile("zzz\n", &Units));
EXPECT_FALSE(ParseDictionaryFile("", &Units));
EXPECT_TRUE(ParseDictionaryFile("\n", &Units));
EXPECT_EQ(Units.size(), 0U);
EXPECT_TRUE(ParseDictionaryFile("#zzzz a b c d\n", &Units));
EXPECT_EQ(Units.size(), 0U);
EXPECT_TRUE(ParseDictionaryFile(" #zzzz\n", &Units));
EXPECT_EQ(Units.size(), 0U);
EXPECT_TRUE(ParseDictionaryFile(" #zzzz\n", &Units));
EXPECT_EQ(Units.size(), 0U);
EXPECT_TRUE(ParseDictionaryFile(" #zzzz\naaa=\"aa\"", &Units));
EXPECT_EQ(Units, std::vector<Unit>({Unit({'a', 'a'})}));
EXPECT_TRUE(
ParseDictionaryFile(" #zzzz\naaa=\"aa\"\n\nabc=\"abc\"", &Units));
EXPECT_EQ(Units,
std::vector<Unit>({Unit({'a', 'a'}), Unit({'a', 'b', 'c'})}));
}
TEST(FuzzerUtil, Base64) {
EXPECT_EQ("", Base64({}));
EXPECT_EQ("YQ==", Base64({'a'}));
EXPECT_EQ("eA==", Base64({'x'}));
EXPECT_EQ("YWI=", Base64({'a', 'b'}));
EXPECT_EQ("eHk=", Base64({'x', 'y'}));
EXPECT_EQ("YWJj", Base64({'a', 'b', 'c'}));
EXPECT_EQ("eHl6", Base64({'x', 'y', 'z'}));
EXPECT_EQ("YWJjeA==", Base64({'a', 'b', 'c', 'x'}));
EXPECT_EQ("YWJjeHk=", Base64({'a', 'b', 'c', 'x', 'y'}));
EXPECT_EQ("YWJjeHl6", Base64({'a', 'b', 'c', 'x', 'y', 'z'}));
}
TEST(Corpus, Distribution) {
Random Rand(0);
InputCorpus C("");
size_t N = 10;
size_t TriesPerUnit = 1<<16;
for (size_t i = 0; i < N; i++)
C.AddToCorpus(Unit{ static_cast<uint8_t>(i) }, 0);
std::vector<size_t> Hist(N);
for (size_t i = 0; i < N * TriesPerUnit; i++) {
Hist[C.ChooseUnitIdxToMutate(Rand)]++;
}
for (size_t i = 0; i < N; i++) {
// A weak sanity check that every unit gets invoked.
EXPECT_GT(Hist[i], TriesPerUnit / N / 3);
}
}
TEST(Merge, Bad) {
const char *kInvalidInputs[] = {
"",
"x",
"3\nx",
"2\n3",
"2\n2",
"2\n2\nA\n",
"2\n2\nA\nB\nC\n",
"0\n0\n",
"1\n1\nA\nDONE 0",
"1\n1\nA\nSTARTED 1",
};
Merger M;
for (auto S : kInvalidInputs) {
// fprintf(stderr, "TESTING:\n%s\n", S);
EXPECT_FALSE(M.Parse(S, false));
}
}
void EQ(const std::vector<uint32_t> &A, const std::vector<uint32_t> &B) {
EXPECT_EQ(A, B);
}
void EQ(const std::vector<std::string> &A, const std::vector<std::string> &B) {
std::set<std::string> a(A.begin(), A.end());
std::set<std::string> b(B.begin(), B.end());
EXPECT_EQ(a, b);
}
static void Merge(const std::string &Input,
const std::vector<std::string> Result,
size_t NumNewFeatures) {
Merger M;
std::vector<std::string> NewFiles;
EXPECT_TRUE(M.Parse(Input, true));
EXPECT_EQ(NumNewFeatures, M.Merge(&NewFiles));
EQ(NewFiles, Result);
}
TEST(Merge, Good) {
Merger M;
EXPECT_TRUE(M.Parse("1\n0\nAA\n", false));
EXPECT_EQ(M.Files.size(), 1U);
EXPECT_EQ(M.NumFilesInFirstCorpus, 0U);
EXPECT_EQ(M.Files[0].Name, "AA");
EXPECT_TRUE(M.LastFailure.empty());
EXPECT_EQ(M.FirstNotProcessedFile, 0U);
EXPECT_TRUE(M.Parse("2\n1\nAA\nBB\nSTARTED 0 42\n", false));
EXPECT_EQ(M.Files.size(), 2U);
EXPECT_EQ(M.NumFilesInFirstCorpus, 1U);
EXPECT_EQ(M.Files[0].Name, "AA");
EXPECT_EQ(M.Files[1].Name, "BB");
EXPECT_EQ(M.LastFailure, "AA");
EXPECT_EQ(M.FirstNotProcessedFile, 1U);
EXPECT_TRUE(M.Parse("3\n1\nAA\nBB\nC\n"
"STARTED 0 1000\n"
"DONE 0 1 2 3\n"
"STARTED 1 1001\n"
"DONE 1 4 5 6 \n"
"STARTED 2 1002\n"
"", true));
EXPECT_EQ(M.Files.size(), 3U);
EXPECT_EQ(M.NumFilesInFirstCorpus, 1U);
EXPECT_EQ(M.Files[0].Name, "AA");
EXPECT_EQ(M.Files[0].Size, 1000U);
EXPECT_EQ(M.Files[1].Name, "BB");
EXPECT_EQ(M.Files[1].Size, 1001U);
EXPECT_EQ(M.Files[2].Name, "C");
EXPECT_EQ(M.Files[2].Size, 1002U);
EXPECT_EQ(M.LastFailure, "C");
EXPECT_EQ(M.FirstNotProcessedFile, 3U);
EQ(M.Files[0].Features, {1, 2, 3});
EQ(M.Files[1].Features, {4, 5, 6});
std::vector<std::string> NewFiles;
EXPECT_TRUE(M.Parse("3\n2\nAA\nBB\nC\n"
"STARTED 0 1000\nDONE 0 1 2 3\n"
"STARTED 1 1001\nDONE 1 4 5 6 \n"
"STARTED 2 1002\nDONE 2 6 1 3 \n"
"", true));
EXPECT_EQ(M.Files.size(), 3U);
EXPECT_EQ(M.NumFilesInFirstCorpus, 2U);
EXPECT_TRUE(M.LastFailure.empty());
EXPECT_EQ(M.FirstNotProcessedFile, 3U);
EQ(M.Files[0].Features, {1, 2, 3});
EQ(M.Files[1].Features, {4, 5, 6});
EQ(M.Files[2].Features, {1, 3, 6});
EXPECT_EQ(0U, M.Merge(&NewFiles));
EQ(NewFiles, {});
EXPECT_TRUE(M.Parse("3\n1\nA\nB\nC\n"
"STARTED 0 1000\nDONE 0 1 2 3\n"
"STARTED 1 1001\nDONE 1 4 5 6 \n"
"STARTED 2 1002\nDONE 2 6 1 3\n"
"", true));
EQ(M.Files[0].Features, {1, 2, 3});
EQ(M.Files[1].Features, {4, 5, 6});
EQ(M.Files[2].Features, {1, 3, 6});
EXPECT_EQ(3U, M.Merge(&NewFiles));
EQ(NewFiles, {"B"});
}
TEST(Merge, Merge) {
Merge("3\n1\nA\nB\nC\n"
"STARTED 0 1000\nDONE 0 1 2 3\n"
"STARTED 1 1001\nDONE 1 4 5 6 \n"
"STARTED 2 1002\nDONE 2 6 1 3 \n",
{"B"}, 3);
Merge("3\n0\nA\nB\nC\n"
"STARTED 0 2000\nDONE 0 1 2 3\n"
"STARTED 1 1001\nDONE 1 4 5 6 \n"
"STARTED 2 1002\nDONE 2 6 1 3 \n",
{"A", "B", "C"}, 6);
Merge("4\n0\nA\nB\nC\nD\n"
"STARTED 0 2000\nDONE 0 1 2 3\n"
"STARTED 1 1101\nDONE 1 4 5 6 \n"
"STARTED 2 1102\nDONE 2 6 1 3 100 \n"
"STARTED 3 1000\nDONE 3 1 \n",
{"A", "B", "C", "D"}, 7);
Merge("4\n1\nA\nB\nC\nD\n"
"STARTED 0 2000\nDONE 0 4 5 6 7 8\n"
"STARTED 1 1100\nDONE 1 1 2 3 \n"
"STARTED 2 1100\nDONE 2 2 3 \n"
"STARTED 3 1000\nDONE 3 1 \n",
{"B", "D"}, 3);
}

View file

@ -0,0 +1,28 @@
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
// Make sure LLVMFuzzerInitialize is called.
#include <assert.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static char *argv0;
extern "C" int LLVMFuzzerInitialize(int *argc, char ***argv) {
assert(*argc > 0);
argv0 = **argv;
fprintf(stderr, "LLVMFuzzerInitialize: %s\n", argv0);
return 0;
}
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
if (Size == strlen(argv0) &&
!strncmp(reinterpret_cast<const char *>(Data), argv0, Size)) {
fprintf(stderr, "BINGO %s\n", argv0);
exit(1);
}
return 0;
}

View file

@ -0,0 +1,17 @@
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
// Test with a leak.
#include <cstdint>
#include <cstddef>
static volatile void *Sink;
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
if (Size > 0 && *Data == 'H') {
Sink = new int;
Sink = nullptr;
}
return 0;
}

View file

@ -0,0 +1,17 @@
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
// Test with a leak.
#include <cstdint>
#include <cstddef>
static volatile int *Sink;
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
if (!Size) return 0;
Sink = new int;
Sink = new int;
while (Sink) *Sink = 0; // Infinite loop.
return 0;
}

View file

@ -0,0 +1,22 @@
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
// Simple test for a fuzzer: find interesting value of array index.
#include <assert.h>
#include <cstdint>
#include <cstring>
#include <cstddef>
#include <iostream>
static volatile int Sink;
const int kArraySize = 1234567;
int array[kArraySize];
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
if (Size < 8) return 0;
size_t a = 0;
memcpy(&a, Data, 8);
Sink = array[a % (kArraySize + 1)];
return 0;
}

View file

@ -0,0 +1,31 @@
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
// Simple test for a fuzzer. The fuzzer must find a particular string.
#include <cstring>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
// TODO: check other sizes.
if (Size >= 8 && memcmp(Data, "01234567", 8) == 0) {
if (Size >= 12 && memcmp(Data + 8, "ABCD", 4) == 0) {
if (Size >= 14 && memcmp(Data + 12, "XY", 2) == 0) {
if (Size >= 17 && memcmp(Data + 14, "KLM", 3) == 0) {
if (Size >= 27 && memcmp(Data + 17, "ABCDE-GHIJ", 10) == 0){
fprintf(stderr, "BINGO %zd\n", Size);
for (size_t i = 0; i < Size; i++) {
uint8_t C = Data[i];
if (C >= 32 && C < 127)
fprintf(stderr, "%c", C);
}
fprintf(stderr, "\n");
exit(1);
}
}
}
}
}
return 0;
}

View file

@ -0,0 +1,18 @@
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
// Crash on the N-th execution.
#include <cstdint>
#include <cstddef>
#include <iostream>
static int Counter;
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
if (Counter++ == 1000) {
std::cout << "BINGO; Found the target, exiting\n";
exit(1);
}
return 0;
}

View file

@ -0,0 +1,19 @@
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
// Simple test for a fuzzer. The fuzzer must find the empty string.
#include <cstdint>
#include <cstdlib>
#include <cstddef>
#include <iostream>
static volatile int *Null = 0;
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
if (Size == 0) {
std::cout << "Found the target, dereferencing NULL\n";
*Null = 1;
}
return 0;
}

View file

@ -0,0 +1,26 @@
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
// Simple test for a fuzzer. The fuzzer must find the string "Hi!".
#include <cstdint>
#include <cstdlib>
#include <cstddef>
#include <iostream>
static volatile int Sink;
static volatile int *Null = 0;
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
if (Size > 0 && Data[0] == 'H') {
Sink = 1;
if (Size > 1 && Data[1] == 'i') {
Sink = 2;
if (Size > 2 && Data[2] == '!') {
std::cout << "Found the target, dereferencing NULL\n";
*Null = 1;
}
}
}
return 0;
}

View file

@ -0,0 +1,28 @@
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
// Tests OOM handling when there is a single large allocation.
#include <assert.h>
#include <cstdint>
#include <cstdlib>
#include <cstddef>
#include <cstring>
#include <iostream>
static volatile char *SinkPtr;
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
if (Size > 0 && Data[0] == 'H') {
if (Size > 1 && Data[1] == 'i') {
if (Size > 2 && Data[2] == '!') {
size_t kSize = (size_t)1 << 31;
char *p = new char[kSize];
memset(p, 0, kSize);
SinkPtr = p;
delete [] p;
}
}
}
return 0;
}

View file

@ -0,0 +1,27 @@
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
// Tests OOM handling.
#include <assert.h>
#include <cstdint>
#include <cstdlib>
#include <cstddef>
#include <cstring>
#include <iostream>
static volatile char *SinkPtr;
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
if (Size > 0 && Data[0] == 'H') {
if (Size > 1 && Data[1] == 'i') {
if (Size > 2 && Data[2] == '!') {
size_t kSize = 0xff000000U;
char *p = new char[kSize];
SinkPtr = p;
delete [] p;
}
}
}
return 0;
}

View file

@ -0,0 +1,31 @@
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
// Tests OOM handling.
#include <assert.h>
#include <cstdint>
#include <cstdlib>
#include <cstddef>
#include <cstring>
#include <iostream>
#include <thread>
static volatile char *SinkPtr;
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
if (Size > 0 && Data[0] == 'H') {
if (Size > 1 && Data[1] == 'i') {
if (Size > 2 && Data[2] == '!') {
while (true) {
size_t kSize = 1 << 28;
char *p = new char[kSize];
memset(p, 0, kSize);
SinkPtr = p;
std::this_thread::sleep_for(std::chrono::seconds(1));
}
}
}
}
return 0;
}

View file

@ -0,0 +1,29 @@
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
// Simple test for a fuzzer. The fuzzer must find repeated bytes.
#include <assert.h>
#include <cstdint>
#include <cstdlib>
#include <cstddef>
#include <iostream>
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
assert(Data);
// Looking for AAAAAAAAAAAAAAAAAAAAAA or some such.
size_t CurA = 0, MaxA = 0;
for (size_t i = 0; i < Size; i++) {
// Make sure there are no conditionals in the loop so that
// coverage can't help the fuzzer.
int EQ = Data[i] == 'A';
CurA = EQ * (CurA + 1);
int GT = CurA > MaxA;
MaxA = GT * CurA + (!GT) * MaxA;
}
if (MaxA >= 20) {
std::cout << "BINGO; Found the target (Max: " << MaxA << "), exiting\n";
exit(0);
}
return 0;
}

View file

@ -0,0 +1,22 @@
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
#include <cstring>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
int Matches = 0;
for (size_t i = 0; i + 2 < Size; i += 3) {
const char *Pat = i % 2 ? "foo" : "bar";
if (!memcmp(Data + i, Pat, 3))
Matches++;
}
if (Matches > 20) {
fprintf(stderr, "BINGO!\n");
exit(1);
}
return 0;
}

View file

@ -0,0 +1,28 @@
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
// Test that we can find the minimal item in the corpus (3 bytes: "FUZ").
#include <cstdint>
#include <cstdlib>
#include <cstddef>
#include <cstring>
#include <cstdio>
static volatile int Sink;
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
int8_t Ids[256];
memset(Ids, -1, sizeof(Ids));
for (size_t i = 0; i < Size; i++)
if (Ids[Data[i]] == -1)
Ids[Data[i]] = i;
int F = Ids[(unsigned char)'F'];
int U = Ids[(unsigned char)'U'];
int Z = Ids[(unsigned char)'Z'];
if (F >= 0 && U > F && Z > U) {
Sink++;
//fprintf(stderr, "IDS: %d %d %d\n", F, U, Z);
}
return 0;
}

View file

@ -0,0 +1,22 @@
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
// Test that we can find the minimal item in the corpus (3 bytes: "FUZ").
#include <cstdint>
#include <cstdlib>
#include <cstddef>
#include <cstring>
#include <cstdio>
static volatile uint32_t Sink;
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
if (Size < sizeof(uint32_t)) return 0;
uint32_t X, Y;
size_t Offset = Size < 8 ? 0 : Size / 2;
memcpy(&X, Data + Offset, sizeof(uint32_t));
memcpy(&Y, "FUZZ", sizeof(uint32_t));
Sink = X == Y;
return 0;
}

View file

@ -0,0 +1,28 @@
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
// Test for signed-integer-overflow.
#include <assert.h>
#include <cstdint>
#include <cstdlib>
#include <cstddef>
#include <iostream>
#include <climits>
static volatile int Sink;
static int Large = INT_MAX;
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
assert(Data);
if (Size > 0 && Data[0] == 'H') {
Sink = 1;
if (Size > 1 && Data[1] == 'i') {
Sink = 2;
if (Size > 2 && Data[2] == '!') {
Large++; // int overflow.
}
}
}
return 0;
}

View file

@ -0,0 +1,46 @@
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
// Simple test for a fuzzer. The fuzzer must find several narrow ranges.
#include <cstdint>
#include <cstdlib>
#include <cstring>
#include <cstdio>
extern int AllLines[];
bool PrintOnce(int Line) {
if (!AllLines[Line])
fprintf(stderr, "Seen line %d\n", Line);
AllLines[Line] = 1;
return true;
}
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
if (Size != 22) return 0;
uint64_t x = 0;
int64_t y = 0;
int32_t z = 0;
uint16_t a = 0;
memcpy(&x, Data, 8); // 8
memcpy(&y, Data + 8, 8); // 16
memcpy(&z, Data + 16, sizeof(z)); // 20
memcpy(&a, Data + 20, sizeof(a)); // 22
if (x > 1234567890 && PrintOnce(__LINE__) &&
x < 1234567895 && PrintOnce(__LINE__) &&
a == 0x4242 && PrintOnce(__LINE__) &&
y >= 987654321 && PrintOnce(__LINE__) &&
y <= 987654325 && PrintOnce(__LINE__) &&
z < -10000 && PrintOnce(__LINE__) &&
z >= -10005 && PrintOnce(__LINE__) &&
z != -10003 && PrintOnce(__LINE__) &&
true) {
fprintf(stderr, "BINGO; Found the target: size %zd (%zd, %zd, %d, %d), exiting.\n",
Size, x, y, z, a);
exit(1);
}
return 0;
}
int AllLines[__LINE__ + 1]; // Must be the last line.

View file

@ -0,0 +1,29 @@
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
// Simple test for a fuzzer.
// The fuzzer must find a string based on dictionary words:
// "Elvis"
// "Presley"
#include <cstdint>
#include <cstdlib>
#include <cstddef>
#include <cstring>
#include <iostream>
static volatile int Zero = 0;
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
const char *Expected = "ElvisPresley";
if (Size < strlen(Expected)) return 0;
size_t Match = 0;
for (size_t i = 0; Expected[i]; i++)
if (Expected[i] + Zero == Data[i])
Match++;
if (Match == strlen(Expected)) {
std::cout << "BINGO; Found the target, exiting\n";
exit(1);
}
return 0;
}

View file

@ -0,0 +1,40 @@
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
// This test computes a checksum of the data (all but the last 4 bytes),
// and then compares the last 4 bytes with the computed value.
// A fuzzer with cmp traces is expected to defeat this check.
#include <cstdint>
#include <cstdlib>
#include <cstring>
#include <cstdio>
// A modified jenkins_one_at_a_time_hash initialized by non-zero,
// so that simple_hash(0) != 0. See also
// https://en.wikipedia.org/wiki/Jenkins_hash_function
static uint32_t simple_hash(const uint8_t *Data, size_t Size) {
uint32_t Hash = 0x12039854;
for (uint32_t i = 0; i < Size; i++) {
Hash += Data[i];
Hash += (Hash << 10);
Hash ^= (Hash >> 6);
}
Hash += (Hash << 3);
Hash ^= (Hash >> 11);
Hash += (Hash << 15);
return Hash;
}
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
if (Size < 14)
return 0;
uint32_t Hash = simple_hash(&Data[0], Size - 4);
uint32_t Want = reinterpret_cast<const uint32_t *>(&Data[Size - 4])[0];
if (Hash != Want)
return 0;
fprintf(stderr, "BINGO; simple_hash defeated: %x == %x\n", (unsigned int)Hash,
(unsigned int)Want);
exit(1);
return 0;
}

View file

@ -0,0 +1,27 @@
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
// Simple test for a fuzzer. The fuzzer must find the string "Hi!".
#include <assert.h>
#include <cstdint>
#include <cstdlib>
#include <cstddef>
#include <iostream>
static volatile int Sink;
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
assert(Data);
if (Size > 0 && Data[0] == 'H') {
Sink = 1;
if (Size > 1 && Data[1] == 'i') {
Sink = 2;
if (Size > 2 && Data[2] == '!') {
std::cout << "BINGO; Found the target, exiting\n";
exit(0);
}
}
}
return 0;
}

View file

@ -0,0 +1,25 @@
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
// Threaded test for a fuzzer. The fuzzer should find "H"
#include <assert.h>
#include <cstdint>
#include <cstddef>
#include <cstring>
#include <iostream>
#include <thread>
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
auto C = [&] {
if (Size >= 2 && Data[0] == 'H') {
std::cout << "BINGO; Found the target, exiting\n";
abort();
}
};
std::thread T[] = {std::thread(C), std::thread(C), std::thread(C),
std::thread(C), std::thread(C), std::thread(C)};
for (auto &X : T)
X.join();
return 0;
}

View file

@ -0,0 +1,17 @@
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
// Simple test for a fuzzer. The fuzzer must find a particular string.
#include <cstring>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
char *S = (char*)Data;
if (Size >= 6 && !memcmp(S, "qwerty", 6)) {
fprintf(stderr, "BINGO\n");
exit(1);
}
return 0;
}

View file

@ -0,0 +1,17 @@
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
// Simple test for a fuzzer. The fuzzer must find a particular string.
#include <cstring>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
char *S = (char*)Data;
if (Size >= 7 && !strcmp(S, "qwerty")) {
fprintf(stderr, "BINGO\n");
exit(1);
}
return 0;
}

View file

@ -0,0 +1,17 @@
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
// Simple test for a fuzzer. The fuzzer must find a particular string.
#include <cstring>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
char *S = (char*)Data;
if (Size >= 6 && !strncmp(S, "qwerty", 6)) {
fprintf(stderr, "BINGO\n");
exit(1);
}
return 0;
}

View file

@ -0,0 +1,21 @@
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
// The test spams to stderr and stdout.
#include <assert.h>
#include <cstdint>
#include <cstdio>
#include <cstddef>
#include <iostream>
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
assert(Data);
printf("PRINTF_STDOUT\n");
fflush(stdout);
fprintf(stderr, "PRINTF_STDERR\n");
std::cout << "STREAM_COUT\n";
std::cout.flush();
std::cerr << "STREAM_CERR\n";
return 0;
}

View file

@ -0,0 +1,32 @@
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
// Break through a series of strcmp.
#include <cstring>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cassert>
bool Eq(const uint8_t *Data, size_t Size, const char *Str) {
char Buff[1024];
size_t Len = strlen(Str);
if (Size < Len) return false;
if (Len >= sizeof(Buff)) return false;
memcpy(Buff, (char*)Data, Len);
Buff[Len] = 0;
int res = strcmp(Buff, Str);
return res == 0;
}
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
if (Eq(Data, Size, "ABC") &&
Size >= 3 && Eq(Data + 3, Size - 3, "QWER") &&
Size >= 7 && Eq(Data + 7, Size - 7, "ZXCVN") &&
Size >= 14 && Data[13] == 42
) {
fprintf(stderr, "BINGO\n");
exit(1);
}
return 0;
}

View file

@ -0,0 +1,21 @@
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
// Test that libFuzzer itself does not read out of bounds.
#include <assert.h>
#include <cstdint>
#include <cstring>
#include <cstdlib>
#include <cstddef>
#include <iostream>
static volatile int Sink;
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
if (Size < 5) return 0;
const char *Ch = reinterpret_cast<const char *>(Data);
if (Ch[Size - 3] == 'a')
Sink = strncmp(Ch + Size - 3, "abcdefg", 6);
return 0;
}

View file

@ -0,0 +1,28 @@
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
// Simple test for a fuzzer. The fuzzer must find a particular string.
#include <cstring>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
static volatile int sink;
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
// TODO: check other sizes.
char *S = (char*)Data;
if (Size >= 8 && strncmp(S, "123", 8))
sink = 1;
if (Size >= 8 && strncmp(S, "01234567", 8) == 0) {
if (Size >= 12 && strncmp(S + 8, "ABCD", 4) == 0) {
if (Size >= 14 && strncmp(S + 12, "XY", 2) == 0) {
if (Size >= 17 && strncmp(S + 14, "KLM", 3) == 0) {
fprintf(stderr, "BINGO\n");
exit(1);
}
}
}
}
return 0;
}

View file

@ -0,0 +1,28 @@
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
// Test strstr and strcasestr hooks.
#include <string>
#include <string.h>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
// Windows does not have strcasestr and memmem, so we are not testing them.
#ifdef _WIN32
#define strcasestr strstr
#define memmem(a, b, c, d) true
#endif
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
if (Size < 4) return 0;
std::string s(reinterpret_cast<const char*>(Data), Size);
if (strstr(s.c_str(), "FUZZ") &&
strcasestr(s.c_str(), "aBcD") &&
memmem(s.data(), s.size(), "kuku", 4)
) {
fprintf(stderr, "BINGO\n");
exit(1);
}
return 0;
}

View file

@ -0,0 +1,34 @@
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
// The fuzzer must find several constants with swapped bytes.
#include <cstdint>
#include <cstdlib>
#include <cstring>
#include <cstdio>
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
if (Size < 14) return 0;
uint64_t x = 0;
uint32_t y = 0;
uint16_t z = 0;
memcpy(&x, Data, sizeof(x));
memcpy(&y, Data + Size / 2, sizeof(y));
memcpy(&z, Data + Size - sizeof(z), sizeof(z));
x = __builtin_bswap64(x);
y = __builtin_bswap32(y);
z = __builtin_bswap16(z);
if (x == 0x46555A5A5A5A5546ULL &&
z == 0x4F4B &&
y == 0x66757A7A &&
true
) {
if (Data[Size - 3] == 'z') {
fprintf(stderr, "BINGO; Found the target\n");
exit(1);
}
}
return 0;
}

View file

@ -0,0 +1,35 @@
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
// Simple test for a fuzzer. The fuzzer must find the interesting switch value.
#include <cstdint>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <cstddef>
int Switch(int a) {
switch(a) {
case 100001: return 1;
case 100002: return 2;
case 100003: return 4;
}
return 0;
}
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
const int N = 3;
if (Size < N * sizeof(int)) return 0;
int Res = 0;
for (int i = 0; i < N; i++) {
int X;
memcpy(&X, Data + i * sizeof(int), sizeof(int));
Res += Switch(X);
}
if (Res == 5 || Res == 3 || Res == 6 || Res == 7) {
fprintf(stderr, "BINGO; Found the target, exiting; Res=%d\n", Res);
exit(1);
}
return 0;
}

View file

@ -0,0 +1,58 @@
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
// Simple test for a fuzzer. The fuzzer must find the interesting switch value.
#include <cstdint>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <cstddef>
static volatile int Sink;
template<class T>
bool Switch(const uint8_t *Data, size_t Size) {
T X;
if (Size < sizeof(X)) return false;
memcpy(&X, Data, sizeof(X));
switch (X) {
case 1: Sink = __LINE__; break;
case 101: Sink = __LINE__; break;
case 1001: Sink = __LINE__; break;
case 10001: Sink = __LINE__; break;
// case 100001: Sink = __LINE__; break;
// case 1000001: Sink = __LINE__; break;
case 10000001: Sink = __LINE__; break;
case 100000001: return true;
}
return false;
}
bool ShortSwitch(const uint8_t *Data, size_t Size) {
short X;
if (Size < sizeof(short)) return false;
memcpy(&X, Data, sizeof(short));
switch(X) {
case 42: Sink = __LINE__; break;
case 402: Sink = __LINE__; break;
case 4002: Sink = __LINE__; break;
case 5002: Sink = __LINE__; break;
case 7002: Sink = __LINE__; break;
case 9002: Sink = __LINE__; break;
case 14002: Sink = __LINE__; break;
case 21402: return true;
}
return false;
}
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
if (Size >= 4 && Switch<int>(Data, Size) &&
Size >= 12 && Switch<uint64_t>(Data + 4, Size - 4) &&
Size >= 14 && ShortSwitch(Data + 12, 2)
) {
fprintf(stderr, "BINGO; Found the target, exiting\n");
exit(1);
}
return 0;
}

View file

@ -0,0 +1,18 @@
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
// The fuzzer should find a leak in a non-main thread.
#include <cstdint>
#include <cstddef>
#include <thread>
static volatile int *Sink;
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
if (Size == 0) return 0;
if (Data[0] != 'F') return 0;
std::thread T([&] { Sink = new int; });
T.join();
return 0;
}

View file

@ -0,0 +1,26 @@
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
// Threaded test for a fuzzer. The fuzzer should not crash.
#include <assert.h>
#include <cstdint>
#include <cstddef>
#include <cstring>
#include <thread>
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
if (Size < 8) return 0;
assert(Data);
auto C = [&] {
size_t Res = 0;
for (size_t i = 0; i < Size / 2; i++)
Res += memcmp(Data, Data + Size / 2, 4);
return Res;
};
std::thread T[] = {std::thread(C), std::thread(C), std::thread(C),
std::thread(C), std::thread(C), std::thread(C)};
for (auto &X : T)
X.join();
return 0;
}

View file

@ -0,0 +1,14 @@
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
// Simple test for a fuzzer. The fuzzer must find the empty string.
#include <cstdint>
#include <cstddef>
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
static volatile int Zero = 0;
if (!Size)
while(!Zero)
;
return 0;
}

View file

@ -0,0 +1,26 @@
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
// Simple test for a fuzzer. The fuzzer must find the string "Hi!".
#include <cstdint>
#include <cstdlib>
#include <cstddef>
#include <iostream>
static volatile int Sink;
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
if (Size > 0 && Data[0] == 'H') {
Sink = 1;
if (Size > 1 && Data[1] == 'i') {
Sink = 2;
if (Size > 2 && Data[2] == '!') {
Sink = 2;
while (Sink)
;
}
}
}
return 0;
}

View file

@ -0,0 +1,27 @@
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
// Tests -trace_malloc
#include <assert.h>
#include <cstdint>
#include <cstdlib>
#include <cstddef>
#include <iostream>
int *Ptr;
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
if (!Size) return 0;
if (*Data == 1) {
delete Ptr;
Ptr = nullptr;
} else if (*Data == 2) {
delete Ptr;
Ptr = new int;
} else if (*Data == 3) {
if (!Ptr)
Ptr = new int;
}
return 0;
}

Some files were not shown because too many files have changed in this diff Show more