mirror of
https://github.com/c64scene-ar/llvm-6502.git
synced 2025-06-25 16:24:23 +00:00
[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:
@ -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.")
|
||||||
|
@ -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());
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
|
@ -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) {
|
||||||
|
@ -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
|
||||||
|
Reference in New Issue
Block a user