From: Kostya Serebryany Date: Tue, 27 Jan 2015 22:08:41 +0000 (+0000) Subject: Add a Fuzzer library X-Git-Url: http://plrg.eecs.uci.edu/git/?p=oota-llvm.git;a=commitdiff_plain;h=c9baf3befbdbfd43da91d99929cebe4f2266b32d Add a Fuzzer library Summary: A simple genetic in-process coverage-guided fuzz testing library. I've used this fuzzer to test clang-format (it found 12+ bugs, thanks djasper@ for the fixes!) and it may also help us test other parts of LLVM. So why not keep it in the LLVM repository? I plan to add the cmake build rules later (in a separate patch, if that's ok) and also add a clang-format-fuzzer target. See README.txt for details. Test Plan: Tests will follow separately. Reviewers: djasper, chandlerc, rnk Reviewed By: rnk Subscribers: majnemer, ygribov, dblaikie, llvm-commits Differential Revision: http://reviews.llvm.org/D7184 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@227252 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index fab1c8747b9..8ab2d6e9de7 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -17,3 +17,4 @@ add_subdirectory(Target) add_subdirectory(AsmParser) add_subdirectory(LineEditor) add_subdirectory(ProfileData) +add_subdirectory(Fuzzer) diff --git a/lib/Fuzzer/CMakeLists.txt b/lib/Fuzzer/CMakeLists.txt new file mode 100644 index 00000000000..62207abb7d6 --- /dev/null +++ b/lib/Fuzzer/CMakeLists.txt @@ -0,0 +1,9 @@ +add_library(LLVMFuzzer STATIC + EXCLUDE_FROM_ALL # Do not build if you are not building fuzzers. + FuzzerCrossOver.cpp + FuzzerIO.cpp + FuzzerLoop.cpp + FuzzerMain.cpp + FuzzerMutate.cpp + FuzzerUtil.cpp + ) diff --git a/lib/Fuzzer/FuzzerCrossOver.cpp b/lib/Fuzzer/FuzzerCrossOver.cpp new file mode 100644 index 00000000000..87ad555d314 --- /dev/null +++ b/lib/Fuzzer/FuzzerCrossOver.cpp @@ -0,0 +1,46 @@ +//===- 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 "FuzzerInternal.h" + +namespace fuzzer { + +// Cross A and B, store the result (ap to MaxLen bytes) in U. +void CrossOver(const Unit &A, const Unit &B, Unit *U, size_t MaxLen) { + size_t Size = rand() % MaxLen + 1; + U->clear(); + const Unit *V = &A; + size_t PosA = 0; + size_t PosB = 0; + size_t *Pos = &PosA; + while (U->size() < Size && (PosA < A.size() || PosB < B.size())) { + // Merge a part of V into U. + size_t SizeLeftU = Size - U->size(); + if (*Pos < V->size()) { + size_t SizeLeftV = V->size() - *Pos; + size_t MaxExtraSize = std::min(SizeLeftU, SizeLeftV); + size_t ExtraSize = rand() % MaxExtraSize + 1; + U->insert(U->end(), V->begin() + *Pos, V->begin() + *Pos + ExtraSize); + (*Pos) += ExtraSize; + } + + // Use the other Unit on the next iteration. + if (Pos == &PosA) { + Pos = &PosB; + V = &B; + } else { + Pos = &PosA; + V = &A; + } + } +} + +} // namespace fuzzer diff --git a/lib/Fuzzer/FuzzerFlags.def b/lib/Fuzzer/FuzzerFlags.def new file mode 100644 index 00000000000..af5d81b5386 --- /dev/null +++ b/lib/Fuzzer/FuzzerFlags.def @@ -0,0 +1,24 @@ +//===- 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 macro 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(int, seed, 0, "Random seed. If 0, seed is generated.") +FUZZER_FLAG(int, iterations, -1, + "Number of iterations of the fuzzer (-1 for infinite runs).") +FUZZER_FLAG(int, max_len, 64, "Maximal length of the test input.") +FUZZER_FLAG(int, cross_over, 1, "If 1, cross over inputs.") +FUZZER_FLAG(int, mutate_depth, 10, + "Apply this number of consecutive mutations to each input.") +FUZZER_FLAG(int, exit_on_first, 0, + "If 1, exit after the first new interesting input is found.") +FUZZER_FLAG(int, timeout, -1, "Timeout in seconds (if positive).") +FUZZER_FLAG(int, help, 0, "Print help.") diff --git a/lib/Fuzzer/FuzzerIO.cpp b/lib/Fuzzer/FuzzerIO.cpp new file mode 100644 index 00000000000..172540c4603 --- /dev/null +++ b/lib/Fuzzer/FuzzerIO.cpp @@ -0,0 +1,44 @@ +//===- 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 "FuzzerInternal.h" +#include +#include +namespace fuzzer { + +std::vector ListFilesInDir(const std::string &Dir) { + std::vector V; + DIR *D = opendir(Dir.c_str()); + if (!D) return V; + while (auto E = readdir(D)) { + if (E->d_type == DT_REG || E->d_type == DT_LNK) + V.push_back(E->d_name); + } + closedir(D); + return V; +} + +Unit FileToVector(const std::string &Path) { + std::ifstream T(Path); + return Unit((std::istreambuf_iterator(T)), + std::istreambuf_iterator()); +} + +void WriteToFile(const Unit &U, const std::string &Path) { + std::ofstream OF(Path); + OF.write((const char*)U.data(), U.size()); +} + +void ReadDirToVectorOfUnits(const char *Path, std::vector *V) { + for (auto &X : ListFilesInDir(Path)) + V->push_back(FileToVector(std::string(Path) + "/" + X)); +} + +} // namespace fuzzer diff --git a/lib/Fuzzer/FuzzerInternal.h b/lib/Fuzzer/FuzzerInternal.h new file mode 100644 index 00000000000..f5492182208 --- /dev/null +++ b/lib/Fuzzer/FuzzerInternal.h @@ -0,0 +1,77 @@ +//===- 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. +//===----------------------------------------------------------------------===// +#include +#include +#include +#include +#include +#include + +namespace fuzzer { +typedef std::vector Unit; +using namespace std::chrono; + +Unit ReadFile(const char *Path); +std::vector ListFilesInDir(const std::string &Dir); +void ReadDirToVectorOfUnits(const char *Path, std::vector *V); +void WriteToFile(const Unit &U, const std::string &Path); + +void Mutate(Unit *U, size_t MaxLen); + +void CrossOver(const Unit &A, const Unit &B, Unit *U, size_t MaxLen); + +void Print(const Unit &U, const char *PrintAfter = ""); +void PrintASCII(const Unit &U, const char *PrintAfter = ""); +std::string Hash(const Unit &U); +void SetTimer(int Seconds); + +class Fuzzer { + public: + struct FuzzingOptions { + int Verbosity = 1; + int MaxLen = 0; + bool DoCrossOver = true; + bool MutateDepth = 10; + bool ExitOnFirst = false; + std::string OutputCorpus; + }; + Fuzzer(FuzzingOptions Options) : Options(Options) { + SetDeathCallback(); + } + void AddToCorpus(const Unit &U) { Corpus.push_back(U); } + size_t Loop(size_t NumIterations); + void ShuffleAndMinimize(); + size_t CorpusSize() const { return Corpus.size(); } + void ReadDir(const std::string &Path) { + ReadDirToVectorOfUnits(Path.c_str(), &Corpus); + } + + static void AlarmCallback(); + + private: + size_t MutateAndTestOne(Unit *U); + size_t RunOne(const Unit &U); + void WriteToOutputCorpus(const Unit &U); + static void WriteToCrash(const Unit &U, const char *Prefix); + + void SetDeathCallback(); + static void DeathCallback(); + static Unit CurrentUnit; + + size_t TotalNumberOfRuns = 0; + + std::vector Corpus; + FuzzingOptions Options; + system_clock::time_point ProcessStartTime = system_clock::now(); + static system_clock::time_point UnitStartTime; +}; + +}; // namespace fuzzer diff --git a/lib/Fuzzer/FuzzerLoop.cpp b/lib/Fuzzer/FuzzerLoop.cpp new file mode 100644 index 00000000000..9ad2bd729aa --- /dev/null +++ b/lib/Fuzzer/FuzzerLoop.cpp @@ -0,0 +1,161 @@ +//===- 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 "FuzzerInternal.h" +#include +#include +#include +#include +#include + +// This function should be defined by the user. +extern "C" void TestOneInput(const uint8_t *Data, size_t Size); + +namespace fuzzer { + +// static +Unit Fuzzer::CurrentUnit; +system_clock::time_point Fuzzer::UnitStartTime; + +void Fuzzer::SetDeathCallback() { + __sanitizer_set_death_callback(DeathCallback); +} + +void Fuzzer::DeathCallback() { + std::cerr << "DEATH: " << std::endl; + Print(CurrentUnit, "\n"); + PrintASCII(CurrentUnit, "\n"); + WriteToCrash(CurrentUnit, "crash-"); +} + +void Fuzzer::AlarmCallback() { + size_t Seconds = + duration_cast(system_clock::now() - UnitStartTime).count(); + std::cerr << "ALARM: working on the last Unit for " << Seconds << " seconds" + << std::endl; + if (Seconds > 60) { + Print(CurrentUnit, "\n"); + PrintASCII(CurrentUnit, "\n"); + WriteToCrash(CurrentUnit, "timeout-"); + } + abort(); +} + +void Fuzzer::ShuffleAndMinimize() { + if (Options.Verbosity) + std::cerr << "Shuffle: " << Corpus.size() << "\n"; + std::vector NewCorpus; + random_shuffle(Corpus.begin(), Corpus.end()); + size_t MaxCov = 0; + Unit &U = CurrentUnit; + for (const auto &C : Corpus) { + for (size_t First = 0; First < 1; First++) { + U.clear(); + size_t Last = std::min(First + Options.MaxLen, C.size()); + U.insert(U.begin(), C.begin() + First, C.begin() + Last); + size_t NewCoverage = RunOne(U); + if (NewCoverage) { + MaxCov = NewCoverage; + NewCorpus.push_back(U); + if (Options.Verbosity >= 2) + std::cerr << "NEW0: " << NewCoverage << "\n"; + } + } + } + Corpus = NewCorpus; + if (Options.Verbosity) + std::cerr << "Shuffle done: " << Corpus.size() << " IC: " << MaxCov << "\n"; +} + +size_t Fuzzer::RunOne(const Unit &U) { + UnitStartTime = system_clock::now(); + TotalNumberOfRuns++; + size_t OldCoverage = __sanitizer_get_total_unique_coverage(); + TestOneInput(U.data(), U.size()); + size_t NewCoverage = __sanitizer_get_total_unique_coverage(); + if (!(TotalNumberOfRuns & (TotalNumberOfRuns - 1)) && Options.Verbosity) { + size_t Seconds = + duration_cast(system_clock::now() - ProcessStartTime).count(); + std::cerr + << "#" << TotalNumberOfRuns + << "\tcov: " << NewCoverage + << "\texec/s: " << (Seconds ? TotalNumberOfRuns / Seconds : 0) << "\n"; + } + if (NewCoverage > OldCoverage) + return NewCoverage; + return 0; +} + +void Fuzzer::WriteToOutputCorpus(const Unit &U) { + if (Options.OutputCorpus.empty()) return; + std::string Path = Options.OutputCorpus + "/" + Hash(U); + WriteToFile(U, Path); + if (Options.Verbosity >= 2) + std::cerr << "Written to " << Path << std::endl; +} + +void Fuzzer::WriteToCrash(const Unit &U, const char *Prefix) { + std::string Path = Prefix + Hash(U); + WriteToFile(U, Path); + std::cerr << "CRASHED; file written to " << Path << std::endl; +} + +size_t Fuzzer::MutateAndTestOne(Unit *U) { + size_t NewUnits = 0; + for (size_t i = 0; i < Options.MutateDepth; i++) { + Mutate(U, Options.MaxLen); + if (U->empty()) continue; + size_t NewCoverage = RunOne(*U); + if (NewCoverage) { + Corpus.push_back(*U); + NewUnits++; + if (Options.Verbosity) { + std::cerr << "#" << TotalNumberOfRuns + << "\tNEW: " << NewCoverage + << " L: " << U->size() + << "\t"; + if (U->size() < 30) { + PrintASCII(*U); + std::cerr << "\t"; + Print(*U); + } + std::cerr << "\n"; + } + WriteToOutputCorpus(*U); + if (Options.ExitOnFirst) + exit(0); + } + } + return NewUnits; +} + +size_t Fuzzer::Loop(size_t NumIterations) { + size_t NewUnits = 0; + for (size_t i = 1; i <= NumIterations; i++) { + if (Options.DoCrossOver) { + for (size_t J1 = 0; J1 < Corpus.size(); J1++) { + for (size_t J2 = 0; J2 < Corpus.size(); J2++) { + CurrentUnit.clear(); + CrossOver(Corpus[J1], Corpus[J2], &CurrentUnit, Options.MaxLen); + NewUnits += MutateAndTestOne(&CurrentUnit); + } + } + } else { // No CrossOver + for (size_t J = 0; J < Corpus.size(); J++) { + CurrentUnit = Corpus[J]; + NewUnits += MutateAndTestOne(&CurrentUnit); + } + } + } + return NewUnits; +} + +} // namespace fuzzer diff --git a/lib/Fuzzer/FuzzerMain.cpp b/lib/Fuzzer/FuzzerMain.cpp new file mode 100644 index 00000000000..3a4b9c07254 --- /dev/null +++ b/lib/Fuzzer/FuzzerMain.cpp @@ -0,0 +1,143 @@ +//===- 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 "FuzzerInternal.h" + +#include +#include +#include +#include + +// ASAN options: +// * don't dump the coverage to disk. +extern "C" const char* __asan_default_options() { return "coverage_pcs=0"; } + +// Program arguments. +struct FlagDescription { + const char *Name; + const char *Description; + int Default; + int *Flag; +}; + +struct { +#define FUZZER_FLAG(Type, Name, Default, Description) Type Name; +#include "FuzzerFlags.def" +#undef FUZZER_FLAG +} Flags; + +static FlagDescription FlagDescriptions [] { +#define FUZZER_FLAG(Type, Name, Default, Description) {#Name, Description, Default, &Flags.Name}, +#include "FuzzerFlags.def" +#undef FUZZER_FLAG +}; + +static const size_t kNumFlags = + sizeof(FlagDescriptions) / sizeof(FlagDescriptions[0]); + +static std::vector inputs; +static const char *ProgName; + +static void PrintHelp() { + std::cerr << "Usage: " << ProgName + << " [-flag1=val1 [-flag2=val2 ...] ] [dir1 [dir2 ...] ]\n"; + std::cerr << "\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]; + std::cerr << " " << D.Name; + for (size_t i = 0, n = MaxFlagLen - strlen(D.Name); i < n; i++) + std::cerr << " "; + std::cerr << "\t"; + std::cerr << D.Default << "\t" << D.Description << "\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; +} + +static bool ParseOneFlag(const char *Param) { + if (Param[0] != '-') return false; + for (size_t F = 0; F < kNumFlags; F++) { + const char *Name = FlagDescriptions[F].Name; + const char *Str = FlagValue(Param, Name); + if (Str) { + int Val = std::stol(Str); + *FlagDescriptions[F].Flag = Val; + if (Flags.verbosity >= 2) + std::cerr << "Flag: " << Name << " " << Val << "\n"; + return true; + } + } + PrintHelp(); + exit(1); +} + +// We don't use any library to minimize dependencies. +static void ParseFlags(int argc, char **argv) { + for (size_t F = 0; F < kNumFlags; F++) + *FlagDescriptions[F].Flag = FlagDescriptions[F].Default; + for (int A = 1; A < argc; A++) { + if (ParseOneFlag(argv[A])) continue; + inputs.push_back(argv[A]); + } +} + +int main(int argc, char **argv) { + using namespace fuzzer; + + ProgName = argv[0]; + ParseFlags(argc, argv); + if (Flags.help) { + PrintHelp(); + return 0; + } + Fuzzer::FuzzingOptions Options; + Options.Verbosity = Flags.verbosity; + Options.MaxLen = Flags.max_len; + Options.DoCrossOver = Flags.cross_over; + Options.MutateDepth = Flags.mutate_depth; + Options.ExitOnFirst = Flags.exit_on_first; + if (!inputs.empty()) + Options.OutputCorpus = inputs[0]; + Fuzzer F(Options); + + unsigned seed = Flags.seed; + // Initialize seed. + if (seed == 0) + seed = time(0) * 10000 + getpid(); + if (Flags.verbosity) + std::cerr << "Seed: " << seed << "\n"; + srand(seed); + + // Timer + if (Flags.timeout > 0) + SetTimer(Flags.timeout); + + for (auto &inp : inputs) + F.ReadDir(inp); + + if (F.CorpusSize() == 0) + F.AddToCorpus(Unit()); // Can't fuzz empty corpus, so add an empty input. + F.ShuffleAndMinimize(); + F.Loop(Flags.iterations < 0 ? INT_MAX : Flags.iterations); + if (Flags.verbosity) + std::cerr << "Done\n"; + return 1; +} diff --git a/lib/Fuzzer/FuzzerMutate.cpp b/lib/Fuzzer/FuzzerMutate.cpp new file mode 100644 index 00000000000..2db8fac9bc6 --- /dev/null +++ b/lib/Fuzzer/FuzzerMutate.cpp @@ -0,0 +1,62 @@ +//===- 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 "FuzzerInternal.h" + +namespace fuzzer { + +static char FlipRandomBit(char X) { + int Bit = rand() % 8; + char Mask = 1 << Bit; + char R; + if (X & (1 << Bit)) + R = X & ~Mask; + else + R = X | Mask; + assert(R != X); + return R; +} + +static char RandCh() { + if (rand() % 2) return rand(); + const char *Special = "!*'();:@&=+$,/?%#[]123ABCxyz-`~."; + return Special[rand() % (sizeof(Special) - 1)]; +} + +void Mutate(Unit *U, size_t MaxLen) { + assert(MaxLen > 0); + assert(U->size() <= MaxLen); + switch (rand() % 3) { + case 0: + if (U->size()) + U->erase(U->begin() + rand() % U->size()); + break; + case 1: + if (U->empty()) { + U->push_back(RandCh()); + } else if (U->size() < MaxLen) { + U->insert(U->begin() + rand() % U->size(), RandCh()); + } else { // At MaxLen. + uint8_t Ch = RandCh(); + size_t Idx = rand() % U->size(); + (*U)[Idx] = Ch; + } + break; + default: + if (!U->empty()) { + size_t idx = rand() % U->size(); + (*U)[idx] = FlipRandomBit((*U)[idx]); + } + break; + } +} + +} // namespace fuzzer diff --git a/lib/Fuzzer/FuzzerUtil.cpp b/lib/Fuzzer/FuzzerUtil.cpp new file mode 100644 index 00000000000..679f289a1c3 --- /dev/null +++ b/lib/Fuzzer/FuzzerUtil.cpp @@ -0,0 +1,61 @@ +//===- 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 "FuzzerInternal.h" +#include +#include +#include +#include +#include + +namespace fuzzer { + +void Print(const Unit &v, const char *PrintAfter) { + std::cerr << v.size() << ": "; + for (auto x : v) + std::cerr << (unsigned) x << " "; + std::cerr << PrintAfter; +} + +void PrintASCII(const Unit &U, const char *PrintAfter) { + for (auto X : U) + std::cerr << (char)((isascii(X) && X >= ' ') ? X : '?'); + std::cerr << PrintAfter; +} + +std::string Hash(const Unit &in) { + size_t h1 = 0, h2 = 0; + for (auto x : in) { + h1 += x; + h1 *= 5; + h2 += x; + h2 *= 7; + } + return std::to_string(h1) + std::to_string(h2); +} + +static void AlarmHandler(int, siginfo_t *, void *) { + Fuzzer::AlarmCallback(); +} + +void SetTimer(int Seconds) { + struct itimerval T {{Seconds, 0}, {Seconds, 0}}; + std::cerr << "SetTimer " << Seconds << "\n"; + int Res = setitimer(ITIMER_REAL, &T, nullptr); + assert(Res == 0); + struct sigaction sigact; + memset(&sigact, 0, sizeof(sigact)); + sigact.sa_sigaction = AlarmHandler; + Res = sigaction(SIGALRM, &sigact, 0); + assert(Res == 0); +} + +} // namespace fuzzer diff --git a/lib/Fuzzer/README.txt b/lib/Fuzzer/README.txt new file mode 100644 index 00000000000..59e0cd4f6ad --- /dev/null +++ b/lib/Fuzzer/README.txt @@ -0,0 +1,56 @@ +=============================== +Fuzzer -- a library for coverage-guided fuzz testing. +=============================== + +This library is intended primarily for in-process coverage-guided fuzz testing +(fuzzing) of other libraries. The typical workflow looks like this: + + * Build the Fuzzer library as a static archive (or just a set of .o files). + Note that the Fuzzer contains the main() function. + Preferably do *not* use sanitizers while building the Fuzzer. + * Build the library you are going to test with -fsanitize-coverage=[234] + and one of the sanitizers. We recommend to build the library in several + different modes (e.g. asan, msan, lsan, ubsan, etc) and even using different + optimizations options (e.g. -O0, -O1, -O2) to diversify testing. + * Build a test driver using the same options as the library. + The test driver is a C/C++ file containing interesting calls to the library + inside a single function: + extern "C" void TestOneInput(const uint8_t *Data, size_t Size); + * Link the Fuzzer, the library and the driver together into an executable + using the same sanitizer options as for the library. + * Collect the initial corpus of inputs for the + fuzzer (a directory with test inputs, one file per input). + The better your inputs are the faster you will find something interesting. + Also try to keep your inputs small, otherwise the Fuzzer will run too slow. + * Run the fuzzer with the test corpus. As new interesting test cases are + discovered they will be added to the corpus. If a bug is discovered by + the sanitizer (asan, etc) it will be reported as usual and the reproducer + will be written to disk. + Each Fuzzer process is single-threaded (unless the library starts its own + threads). You can run the Fuzzer on the same corpus in multiple processes. + in parallel. For run-time options run the Fuzzer binary with '-help=1'. + + +The Fuzzer is similar in concept to AFL (http://lcamtuf.coredump.cx/afl/), +but uses in-process Fuzzing, which is more fragile, more restrictive, but +potentially much faster as it has no overhead for process start-up. +It uses LLVM's "Sanitizer Coverage" instrumentation to get in-process +coverage-feedback https://code.google.com/p/address-sanitizer/wiki/AsanCoverage + +The code resides in the LLVM repository and is (or will be) used by various +parts of LLVM, but the Fuzzer itself does not (and should not) depend on any +part of LLVM and can be used for other projects. Ideally, the Fuzzer's code +should not have any external dependencies. Right now it uses STL, which may need +to be fixed later. + +Examples of usage in LLVM: + * clang-format-fuzzer. The inputs are random pieces of C++-like text. + * TODO: add more + +Toy example (see SimpleTest.cpp): +a simple function that does something interesting if it receives bytes "Hi!". + # Build the Fuzzer with asan: + % clang++ -std=c++11 -fsanitize=address -fsanitize-coverage=3 -O1 -g \ + Fuzzer*.cpp test/SimpleTest.cpp + # Run the fuzzer with no corpus (assuming on empty input) + % ./a.out diff --git a/lib/Fuzzer/test/ExactTest.cpp b/lib/Fuzzer/test/ExactTest.cpp new file mode 100644 index 00000000000..662dd333eea --- /dev/null +++ b/lib/Fuzzer/test/ExactTest.cpp @@ -0,0 +1,21 @@ +// Simple test for a fuzzer. The fuzzer must find the string "FUZZER". +#include +#include +#include + +static volatile int Sink; + +extern "C" void TestOneInput(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"; + abort(); + } +} + diff --git a/lib/Fuzzer/test/InfiniteTest.cpp b/lib/Fuzzer/test/InfiniteTest.cpp new file mode 100644 index 00000000000..9f4864abdd4 --- /dev/null +++ b/lib/Fuzzer/test/InfiniteTest.cpp @@ -0,0 +1,19 @@ +// Simple test for a fuzzer. The fuzzer must find the string "Hi!". +#include +#include +#include + +static volatile int Sink; + +extern "C" void TestOneInput(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] == '!') { + Size = 2; + } + } + } +} + diff --git a/lib/Fuzzer/test/NullDerefTest.cpp b/lib/Fuzzer/test/NullDerefTest.cpp new file mode 100644 index 00000000000..602276f3c81 --- /dev/null +++ b/lib/Fuzzer/test/NullDerefTest.cpp @@ -0,0 +1,21 @@ +// Simple test for a fuzzer. The fuzzer must find the string "Hi!". +#include +#include +#include + +static volatile int Sink; +static volatile int *Null = 0; + +extern "C" void TestOneInput(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; + } + } + } +} + diff --git a/lib/Fuzzer/test/SimpleTest.cpp b/lib/Fuzzer/test/SimpleTest.cpp new file mode 100644 index 00000000000..9e20d8e6ac9 --- /dev/null +++ b/lib/Fuzzer/test/SimpleTest.cpp @@ -0,0 +1,20 @@ +// Simple test for a fuzzer. The fuzzer must find the string "Hi!". +#include +#include +#include + +static volatile int Sink; + +extern "C" void TestOneInput(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, exiting\n"; + exit(0); + } + } + } +} + diff --git a/lib/Fuzzer/test/TestFuzzerCrossOver.cpp b/lib/Fuzzer/test/TestFuzzerCrossOver.cpp new file mode 100644 index 00000000000..7f7619618ec --- /dev/null +++ b/lib/Fuzzer/test/TestFuzzerCrossOver.cpp @@ -0,0 +1,13 @@ +#include "FuzzerInternal.h" + +int main() { + using namespace fuzzer; + Unit A({0, 1, 2, 3, 4}), B({5, 6, 7, 8, 9}); + Unit C; + for (size_t Len = 1; Len < 15; Len++) { + for (int Iter = 0; Iter < 1000; Iter++) { + CrossOver(A, B, &C, Len); + Print(C); + } + } +} diff --git a/lib/Fuzzer/test/TimeoutTest.cpp b/lib/Fuzzer/test/TimeoutTest.cpp new file mode 100644 index 00000000000..c5a8ac00579 --- /dev/null +++ b/lib/Fuzzer/test/TimeoutTest.cpp @@ -0,0 +1,21 @@ +// Simple test for a fuzzer. The fuzzer must find the string "Hi!". +#include +#include +#include + +static volatile int Sink; + +extern "C" void TestOneInput(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] == '!') { + Size = 2; + while (Sink) + ; + } + } + } +} +