From eb884daa38c40a1a8f0fab10b2bf60af592232a9 Mon Sep 17 00:00:00 2001 From: Kostya Serebryany Date: Wed, 4 Feb 2015 19:10:20 +0000 Subject: [PATCH] [fuzzer] make multi-process execution more verbose; fix mutation to actually respect mutation depth and to never produce empty units git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@228170 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Fuzzer/FuzzerFlags.def | 2 +- lib/Fuzzer/FuzzerIO.cpp | 8 ++++++++ lib/Fuzzer/FuzzerInternal.h | 3 ++- lib/Fuzzer/FuzzerLoop.cpp | 4 ++-- lib/Fuzzer/FuzzerMain.cpp | 23 +++++++++++++++++------ lib/Fuzzer/FuzzerMutate.cpp | 24 ++++++++++++++++-------- 6 files changed, 46 insertions(+), 18 deletions(-) diff --git a/lib/Fuzzer/FuzzerFlags.def b/lib/Fuzzer/FuzzerFlags.def index cbaa81e0384..0895fa7bac0 100644 --- a/lib/Fuzzer/FuzzerFlags.def +++ b/lib/Fuzzer/FuzzerFlags.def @@ -16,7 +16,7 @@ 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, +FUZZER_FLAG(int, mutate_depth, 5, "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.") diff --git a/lib/Fuzzer/FuzzerIO.cpp b/lib/Fuzzer/FuzzerIO.cpp index 4e0ac819218..224808c5e16 100644 --- a/lib/Fuzzer/FuzzerIO.cpp +++ b/lib/Fuzzer/FuzzerIO.cpp @@ -9,6 +9,8 @@ // IO functions. //===----------------------------------------------------------------------===// #include "FuzzerInternal.h" +#include +#include #include #include namespace fuzzer { @@ -31,6 +33,12 @@ Unit FileToVector(const std::string &Path) { std::istreambuf_iterator()); } +void CopyFileToErr(const std::string &Path) { + std::ifstream T(Path); + std::copy(std::istreambuf_iterator(T), std::istreambuf_iterator(), + std::ostream_iterator(std::cerr, "")); +} + void WriteToFile(const Unit &U, const std::string &Path) { std::ofstream OF(Path); OF.write((const char*)U.data(), U.size()); diff --git a/lib/Fuzzer/FuzzerInternal.h b/lib/Fuzzer/FuzzerInternal.h index 45379711db4..fbf26309f23 100644 --- a/lib/Fuzzer/FuzzerInternal.h +++ b/lib/Fuzzer/FuzzerInternal.h @@ -23,6 +23,7 @@ using namespace std::chrono; Unit ReadFile(const char *Path); void ReadDirToVectorOfUnits(const char *Path, std::vector *V); void WriteToFile(const Unit &U, const std::string &Path); +void CopyFileToErr(const std::string &Path); // Returns "Dir/FileName" or equivalent for the current OS. std::string DirPlusFile(const std::string &DirPath, const std::string &FileName); @@ -42,7 +43,7 @@ class Fuzzer { int Verbosity = 1; int MaxLen = 0; bool DoCrossOver = true; - bool MutateDepth = 10; + int MutateDepth = 5; bool ExitOnFirst = false; bool UseFullCoverageSet = false; std::string OutputCorpus; diff --git a/lib/Fuzzer/FuzzerLoop.cpp b/lib/Fuzzer/FuzzerLoop.cpp index 88ae6ca7320..04285895181 100644 --- a/lib/Fuzzer/FuzzerLoop.cpp +++ b/lib/Fuzzer/FuzzerLoop.cpp @@ -146,9 +146,8 @@ void Fuzzer::SaveCorpus() { size_t Fuzzer::MutateAndTestOne(Unit *U) { size_t NewUnits = 0; - for (size_t i = 0; i < Options.MutateDepth; i++) { + for (int 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); @@ -158,6 +157,7 @@ size_t Fuzzer::MutateAndTestOne(Unit *U) { << "\tNEW: " << NewCoverage << " L: " << U->size() << " S: " << Corpus.size() + << " I: " << i << "\t"; if (U->size() < 30) { PrintASCII(*U); diff --git a/lib/Fuzzer/FuzzerMain.cpp b/lib/Fuzzer/FuzzerMain.cpp index 3370a80339f..0a852ed4257 100644 --- a/lib/Fuzzer/FuzzerMain.cpp +++ b/lib/Fuzzer/FuzzerMain.cpp @@ -17,6 +17,7 @@ #include #include #include +#include // ASAN options: // * don't dump the coverage to disk. @@ -105,20 +106,30 @@ static void ParseFlags(int argc, char **argv) { } static void WorkerThread(const std::string &Cmd, std::atomic *Counter, - int NumJobs) { + int NumJobs, std::atomic *HasErrors) { + static std::mutex CerrMutex; while (true) { int C = (*Counter)++; - if (C >= NumJobs) return; - std::string ToRun = Cmd + " > fuzz-" + std::to_string(C) + ".log 2>&1\n"; + 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; - system(ToRun.c_str()); + 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; @@ -127,10 +138,10 @@ static int RunInMultipleProcesses(int argc, char **argv, int NumWorkers, } std::vector V; for (int i = 0; i < NumWorkers; i++) - V.push_back(std::thread(WorkerThread, Cmd, &Counter, NumJobs)); + V.push_back(std::thread(WorkerThread, Cmd, &Counter, NumJobs, &HasErrors)); for (auto &T : V) T.join(); - return 0; + return HasErrors ? 1 : 0; } int main(int argc, char **argv) { diff --git a/lib/Fuzzer/FuzzerMutate.cpp b/lib/Fuzzer/FuzzerMutate.cpp index 2db8fac9bc6..60d5623ada6 100644 --- a/lib/Fuzzer/FuzzerMutate.cpp +++ b/lib/Fuzzer/FuzzerMutate.cpp @@ -31,18 +31,25 @@ static char RandCh() { return Special[rand() % (sizeof(Special) - 1)]; } +// Mutate U in place. void Mutate(Unit *U, size_t MaxLen) { assert(MaxLen > 0); assert(U->size() <= MaxLen); + if (U->empty()) { + for (size_t i = 0; i < MaxLen; i++) + U->push_back(RandCh()); + return; + } + assert(!U->empty()); switch (rand() % 3) { case 0: - if (U->size()) + if (U->size() > 1) { U->erase(U->begin() + rand() % U->size()); - break; + break; + } + // Fallthrough case 1: - if (U->empty()) { - U->push_back(RandCh()); - } else if (U->size() < MaxLen) { + if (U->size() < MaxLen) { U->insert(U->begin() + rand() % U->size(), RandCh()); } else { // At MaxLen. uint8_t Ch = RandCh(); @@ -51,12 +58,13 @@ void Mutate(Unit *U, size_t MaxLen) { } break; default: - if (!U->empty()) { - size_t idx = rand() % U->size(); - (*U)[idx] = FlipRandomBit((*U)[idx]); + { + size_t Idx = rand() % U->size(); + (*U)[Idx] = FlipRandomBit((*U)[Idx]); } break; } + assert(!U->empty()); } } // namespace fuzzer