[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
This commit is contained in:
Kostya Serebryany
2015-02-04 19:10:20 +00:00
parent 70e83e3a1c
commit eb884daa38
6 changed files with 46 additions and 18 deletions

View File

@ -16,7 +16,7 @@ FUZZER_FLAG(int, iterations, -1,
"Number of iterations of the fuzzer (-1 for infinite runs).") "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, max_len, 64, "Maximal length of the test input.")
FUZZER_FLAG(int, cross_over, 1, "If 1, cross over inputs.") 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.") "Apply this number of consecutive mutations to each input.")
FUZZER_FLAG(int, exit_on_first, 0, FUZZER_FLAG(int, exit_on_first, 0,
"If 1, exit after the first new interesting input is found.") "If 1, exit after the first new interesting input is found.")

View File

@ -9,6 +9,8 @@
// IO functions. // IO functions.
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
#include "FuzzerInternal.h" #include "FuzzerInternal.h"
#include <iostream>
#include <iterator>
#include <fstream> #include <fstream>
#include <dirent.h> #include <dirent.h>
namespace fuzzer { namespace fuzzer {
@ -31,6 +33,12 @@ Unit FileToVector(const std::string &Path) {
std::istreambuf_iterator<char>()); std::istreambuf_iterator<char>());
} }
void CopyFileToErr(const std::string &Path) {
std::ifstream T(Path);
std::copy(std::istreambuf_iterator<char>(T), std::istreambuf_iterator<char>(),
std::ostream_iterator<char>(std::cerr, ""));
}
void WriteToFile(const Unit &U, const std::string &Path) { void WriteToFile(const Unit &U, const std::string &Path) {
std::ofstream OF(Path); std::ofstream OF(Path);
OF.write((const char*)U.data(), U.size()); OF.write((const char*)U.data(), U.size());

View File

@ -23,6 +23,7 @@ using namespace std::chrono;
Unit ReadFile(const char *Path); Unit ReadFile(const char *Path);
void ReadDirToVectorOfUnits(const char *Path, std::vector<Unit> *V); void ReadDirToVectorOfUnits(const char *Path, std::vector<Unit> *V);
void WriteToFile(const Unit &U, const std::string &Path); void WriteToFile(const Unit &U, const std::string &Path);
void CopyFileToErr(const std::string &Path);
// Returns "Dir/FileName" or equivalent for the current OS. // Returns "Dir/FileName" or equivalent for the current OS.
std::string DirPlusFile(const std::string &DirPath, std::string DirPlusFile(const std::string &DirPath,
const std::string &FileName); const std::string &FileName);
@ -42,7 +43,7 @@ class Fuzzer {
int Verbosity = 1; int Verbosity = 1;
int MaxLen = 0; int MaxLen = 0;
bool DoCrossOver = true; bool DoCrossOver = true;
bool MutateDepth = 10; int MutateDepth = 5;
bool ExitOnFirst = false; bool ExitOnFirst = false;
bool UseFullCoverageSet = false; bool UseFullCoverageSet = false;
std::string OutputCorpus; std::string OutputCorpus;

View File

@ -146,9 +146,8 @@ void Fuzzer::SaveCorpus() {
size_t Fuzzer::MutateAndTestOne(Unit *U) { size_t Fuzzer::MutateAndTestOne(Unit *U) {
size_t NewUnits = 0; 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); Mutate(U, Options.MaxLen);
if (U->empty()) continue;
size_t NewCoverage = RunOne(*U); size_t NewCoverage = RunOne(*U);
if (NewCoverage) { if (NewCoverage) {
Corpus.push_back(*U); Corpus.push_back(*U);
@ -158,6 +157,7 @@ size_t Fuzzer::MutateAndTestOne(Unit *U) {
<< "\tNEW: " << NewCoverage << "\tNEW: " << NewCoverage
<< " L: " << U->size() << " L: " << U->size()
<< " S: " << Corpus.size() << " S: " << Corpus.size()
<< " I: " << i
<< "\t"; << "\t";
if (U->size() < 30) { if (U->size() < 30) {
PrintASCII(*U); PrintASCII(*U);

View File

@ -17,6 +17,7 @@
#include <iostream> #include <iostream>
#include <thread> #include <thread>
#include <atomic> #include <atomic>
#include <mutex>
// ASAN options: // ASAN options:
// * don't dump the coverage to disk. // * 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<int> *Counter, static void WorkerThread(const std::string &Cmd, std::atomic<int> *Counter,
int NumJobs) { int NumJobs, std::atomic<bool> *HasErrors) {
static std::mutex CerrMutex;
while (true) { while (true) {
int C = (*Counter)++; int C = (*Counter)++;
if (C >= NumJobs) return; if (C >= NumJobs) break;
std::string ToRun = Cmd + " > fuzz-" + std::to_string(C) + ".log 2>&1\n"; std::string Log = "fuzz-" + std::to_string(C) + ".log";
std::string ToRun = Cmd + " > " + Log + " 2>&1\n";
if (Flags.verbosity) if (Flags.verbosity)
std::cerr << ToRun; std::cerr << ToRun;
system(ToRun.c_str()); int ExitCode = system(ToRun.c_str());
if (ExitCode != 0)
*HasErrors = true;
std::lock_guard<std::mutex> Lock(CerrMutex);
std::cerr << "================== Job " << C
<< " exited with exit code " << ExitCode
<< " =================\n";
fuzzer::CopyFileToErr(Log);
} }
} }
static int RunInMultipleProcesses(int argc, char **argv, int NumWorkers, static int RunInMultipleProcesses(int argc, char **argv, int NumWorkers,
int NumJobs) { int NumJobs) {
std::atomic<int> Counter(0); std::atomic<int> Counter(0);
std::atomic<bool> HasErrors(false);
std::string Cmd; std::string Cmd;
for (int i = 0; i < argc; i++) { for (int i = 0; i < argc; i++) {
if (FlagValue(argv[i], "jobs") || FlagValue(argv[i], "workers")) continue; 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<std::thread> V; std::vector<std::thread> V;
for (int i = 0; i < NumWorkers; i++) 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) for (auto &T : V)
T.join(); T.join();
return 0; return HasErrors ? 1 : 0;
} }
int main(int argc, char **argv) { int main(int argc, char **argv) {

View File

@ -31,18 +31,25 @@ static char RandCh() {
return Special[rand() % (sizeof(Special) - 1)]; return Special[rand() % (sizeof(Special) - 1)];
} }
// Mutate U in place.
void Mutate(Unit *U, size_t MaxLen) { void Mutate(Unit *U, size_t MaxLen) {
assert(MaxLen > 0); assert(MaxLen > 0);
assert(U->size() <= MaxLen); 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) { switch (rand() % 3) {
case 0: case 0:
if (U->size()) if (U->size() > 1) {
U->erase(U->begin() + rand() % U->size()); U->erase(U->begin() + rand() % U->size());
break; break;
}
// Fallthrough
case 1: case 1:
if (U->empty()) { if (U->size() < MaxLen) {
U->push_back(RandCh());
} else if (U->size() < MaxLen) {
U->insert(U->begin() + rand() % U->size(), RandCh()); U->insert(U->begin() + rand() % U->size(), RandCh());
} else { // At MaxLen. } else { // At MaxLen.
uint8_t Ch = RandCh(); uint8_t Ch = RandCh();
@ -51,12 +58,13 @@ void Mutate(Unit *U, size_t MaxLen) {
} }
break; break;
default: default:
if (!U->empty()) { {
size_t idx = rand() % U->size(); size_t Idx = rand() % U->size();
(*U)[idx] = FlipRandomBit((*U)[idx]); (*U)[Idx] = FlipRandomBit((*U)[Idx]);
} }
break; break;
} }
assert(!U->empty());
} }
} // namespace fuzzer } // namespace fuzzer