From: Kostya Serebryany Date: Thu, 19 Feb 2015 18:45:37 +0000 (+0000) Subject: [fuzzer] split main() into FuzzerDriver() that takes a callback as a parameter and... X-Git-Url: http://plrg.eecs.uci.edu/git/?p=oota-llvm.git;a=commitdiff_plain;h=ce7a848558e49a7347140af9fcffe3dee749f443;hp=7b5766d2f62ccf40de9dd385216d7ba39dcebc0d [fuzzer] split main() into FuzzerDriver() that takes a callback as a parameter and a tiny main() in a separate file git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@229882 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/Fuzzer/CMakeLists.txt b/lib/Fuzzer/CMakeLists.txt index 02370c966af..81e51d1a7cc 100644 --- a/lib/Fuzzer/CMakeLists.txt +++ b/lib/Fuzzer/CMakeLists.txt @@ -3,6 +3,7 @@ set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O2 -fsanitize-coverage= if( LLVM_USE_SANITIZE_COVERAGE ) add_library(LLVMFuzzerNoMain OBJECT FuzzerCrossOver.cpp + FuzzerDriver.cpp FuzzerIO.cpp FuzzerLoop.cpp FuzzerMutate.cpp diff --git a/lib/Fuzzer/FuzzerDriver.cpp b/lib/Fuzzer/FuzzerDriver.cpp new file mode 100644 index 00000000000..8a8659e5f2b --- /dev/null +++ b/lib/Fuzzer/FuzzerDriver.cpp @@ -0,0 +1,198 @@ +//===- 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 "FuzzerInterface.h" +#include "FuzzerInternal.h" + +#include +#include +#include +#include +#include +#include + +namespace fuzzer { + +// 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]); + } +} + +static void WorkerThread(const std::string &Cmd, std::atomic *Counter, + int NumJobs, std::atomic *HasErrors) { + static std::mutex CerrMutex; + while (true) { + int 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) + std::cerr << ToRun; + int ExitCode = system(ToRun.c_str()); + if (ExitCode != 0) + *HasErrors = true; + std::lock_guard Lock(CerrMutex); + std::cerr << "================== Job " << C + << " exited with exit code " << ExitCode + << " =================\n"; + fuzzer::CopyFileToErr(Log); + } +} + +static int RunInMultipleProcesses(int argc, char **argv, int NumWorkers, + int NumJobs) { + std::atomic Counter(0); + std::atomic HasErrors(false); + std::string Cmd; + for (int i = 0; i < argc; i++) { + if (FlagValue(argv[i], "jobs") || FlagValue(argv[i], "workers")) continue; + Cmd += argv[i]; + Cmd += " "; + } + std::vector V; + for (int 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; +} + +int FuzzerDriver(int argc, char **argv, UserCallback Callback) { + using namespace fuzzer; + + ProgName = argv[0]; + ParseFlags(argc, argv); + if (Flags.help) { + PrintHelp(); + return 0; + } + + if (Flags.workers > 0 && Flags.jobs > 0) + return RunInMultipleProcesses(argc, argv, Flags.workers, Flags.jobs); + + 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; + Options.UseFullCoverageSet = Flags.use_full_coverage_set; + Options.PreferSmallDuringInitialShuffle = + Flags.prefer_small_during_initial_shuffle; + if (Flags.runs >= 0) + Options.MaxNumberOfRuns = Flags.runs; + if (!inputs.empty()) + Options.OutputCorpus = inputs[0]; + Fuzzer F(Callback, 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(); + if (Flags.save_minimized_corpus) + F.SaveCorpus(); + F.Loop(Flags.iterations < 0 ? INT_MAX : Flags.iterations); + if (Flags.verbosity) + std::cerr << "Done " << F.getTotalNumberOfRuns() + << " runs in " << F.secondsSinceProcessStartUp() + << " seconds\n"; + return 0; +} + +} // namespace fuzzer diff --git a/lib/Fuzzer/FuzzerInterface.h b/lib/Fuzzer/FuzzerInterface.h new file mode 100644 index 00000000000..49d8c0f6145 --- /dev/null +++ b/lib/Fuzzer/FuzzerInterface.h @@ -0,0 +1,25 @@ +//===- 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 the Fuzzer and the library being tested. +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_INTERFACE_H +#define LLVM_FUZZER_INTERFACE_H + +#include +#include + +namespace fuzzer { + +typedef void (*UserCallback)(const uint8_t *data, size_t size); +int FuzzerDriver(int argc, char **argv, UserCallback Callback); + +} // namespace fuzzer + +#endif // LLVM_FUZZER_INTERFACE_H diff --git a/lib/Fuzzer/FuzzerInternal.h b/lib/Fuzzer/FuzzerInternal.h index 34752ff4944..bc18c551f59 100644 --- a/lib/Fuzzer/FuzzerInternal.h +++ b/lib/Fuzzer/FuzzerInternal.h @@ -17,6 +17,8 @@ #include #include +#include "FuzzerInterface.h" + namespace fuzzer { typedef std::vector Unit; using namespace std::chrono; @@ -51,7 +53,8 @@ class Fuzzer { size_t MaxNumberOfRuns = ULONG_MAX; std::string OutputCorpus; }; - Fuzzer(FuzzingOptions Options) : Options(Options) { + Fuzzer(UserCallback Callback, FuzzingOptions Options) + : Callback(Callback), Options(Options) { SetDeathCallback(); } void AddToCorpus(const Unit &U) { Corpus.push_back(U); } @@ -89,6 +92,7 @@ class Fuzzer { std::vector Corpus; std::unordered_set FullCoverageSets; + UserCallback Callback; FuzzingOptions Options; system_clock::time_point ProcessStartTime = system_clock::now(); static system_clock::time_point UnitStartTime; diff --git a/lib/Fuzzer/FuzzerLoop.cpp b/lib/Fuzzer/FuzzerLoop.cpp index 9c65f257d9c..1529d06b1cc 100644 --- a/lib/Fuzzer/FuzzerLoop.cpp +++ b/lib/Fuzzer/FuzzerLoop.cpp @@ -14,9 +14,6 @@ #include #include -// This function should be defined by the user. -extern "C" void TestOneInput(const uint8_t *Data, size_t Size); - namespace fuzzer { // static @@ -107,7 +104,7 @@ static uintptr_t HashOfArrayOfPCs(uintptr_t *PCs, uintptr_t NumPCs) { // e.g. test/FullCoverageSetTest.cpp. FIXME: make it scale. size_t Fuzzer::RunOneMaximizeFullCoverageSet(const Unit &U) { __sanitizer_reset_coverage(); - TestOneInput(U.data(), U.size()); + Callback(U.data(), U.size()); uintptr_t *PCs; uintptr_t NumPCs =__sanitizer_get_coverage_guards(&PCs); if (FullCoverageSets.insert(HashOfArrayOfPCs(PCs, NumPCs)).second) @@ -117,7 +114,7 @@ size_t Fuzzer::RunOneMaximizeFullCoverageSet(const Unit &U) { size_t Fuzzer::RunOneMaximizeTotalCoverage(const Unit &U) { size_t OldCoverage = __sanitizer_get_total_unique_coverage(); - TestOneInput(U.data(), U.size()); + Callback(U.data(), U.size()); size_t NewCoverage = __sanitizer_get_total_unique_coverage(); if (!(TotalNumberOfRuns & (TotalNumberOfRuns - 1)) && Options.Verbosity) { size_t Seconds = secondsSinceProcessStartUp(); diff --git a/lib/Fuzzer/FuzzerMain.cpp b/lib/Fuzzer/FuzzerMain.cpp index 15a325d3159..d0c3df3b6b5 100644 --- a/lib/Fuzzer/FuzzerMain.cpp +++ b/lib/Fuzzer/FuzzerMain.cpp @@ -9,185 +9,12 @@ // main() and flags. //===----------------------------------------------------------------------===// +#include "FuzzerInterface.h" #include "FuzzerInternal.h" -#include -#include -#include -#include -#include -#include - -// 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]); - } -} - -static void WorkerThread(const std::string &Cmd, std::atomic *Counter, - int NumJobs, std::atomic *HasErrors) { - static std::mutex CerrMutex; - while (true) { - int 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) - std::cerr << ToRun; - int ExitCode = system(ToRun.c_str()); - if (ExitCode != 0) - *HasErrors = true; - std::lock_guard Lock(CerrMutex); - std::cerr << "================== Job " << C - << " exited with exit code " << ExitCode - << " =================\n"; - fuzzer::CopyFileToErr(Log); - } -} - -static int RunInMultipleProcesses(int argc, char **argv, int NumWorkers, - int NumJobs) { - std::atomic Counter(0); - std::atomic HasErrors(false); - std::string Cmd; - for (int i = 0; i < argc; i++) { - if (FlagValue(argv[i], "jobs") || FlagValue(argv[i], "workers")) continue; - Cmd += argv[i]; - Cmd += " "; - } - std::vector V; - for (int 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; -} +// This function should be defined by the user. +extern "C" void TestOneInput(const uint8_t *Data, size_t Size); int main(int argc, char **argv) { - using namespace fuzzer; - - ProgName = argv[0]; - ParseFlags(argc, argv); - if (Flags.help) { - PrintHelp(); - return 0; - } - - if (Flags.workers > 0 && Flags.jobs > 0) - return RunInMultipleProcesses(argc, argv, Flags.workers, Flags.jobs); - - 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; - Options.UseFullCoverageSet = Flags.use_full_coverage_set; - Options.PreferSmallDuringInitialShuffle = - Flags.prefer_small_during_initial_shuffle; - if (Flags.runs >= 0) - Options.MaxNumberOfRuns = Flags.runs; - 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(); - if (Flags.save_minimized_corpus) - F.SaveCorpus(); - F.Loop(Flags.iterations < 0 ? INT_MAX : Flags.iterations); - if (Flags.verbosity) - std::cerr << "Done " << F.getTotalNumberOfRuns() - << " runs in " << F.secondsSinceProcessStartUp() - << " seconds\n"; - return 0; + return fuzzer::FuzzerDriver(argc, argv, TestOneInput); }