forked from OSchip/llvm-project
[libFuzzer] extend the -fork=1 functionality. Still not fully usable, but good enough for the first unit test
llvm-svn: 353775
This commit is contained in:
parent
9ebc9dbd3c
commit
63f48717b5
|
@ -472,46 +472,76 @@ int MinimizeCrashInputInternalStep(Fuzzer *F, InputCorpus *Corpus) {
|
|||
}
|
||||
|
||||
// This is just a skeleton of an experimental -fork=1 feature.
|
||||
void FuzzWithFork(const FuzzingOptions &Options,
|
||||
void FuzzWithFork(Fuzzer *F, const FuzzingOptions &Options,
|
||||
const Vector<std::string> &Args,
|
||||
const Vector<std::string> &Corpora) {
|
||||
Printf("INFO: -fork=1: doing fuzzing in a separate process in order to "
|
||||
"be more resistant to crashes, timeouts, and OOMs\n");
|
||||
auto Rand = F->GetMD().GetRand();
|
||||
|
||||
Vector<SizedFile> Corpus;
|
||||
for (auto &Dir : Corpora)
|
||||
GetSizedFilesFromDir(Dir, &Corpus);
|
||||
std::sort(Corpus.begin(), Corpus.end());
|
||||
auto CFPath = TempPath(".fork");
|
||||
|
||||
Vector<std::string> Files;
|
||||
Set<uint32_t> Features;
|
||||
if (!Corpus.empty()) {
|
||||
auto CFPath = TempPath(".fork");
|
||||
CrashResistantMerge(Args, {}, Corpus, &Files, {}, &Features, CFPath);
|
||||
RemoveFile(CFPath);
|
||||
}
|
||||
Printf("INFO: -fork=1: %zd seeds, starting to fuzz\n", Files.size());
|
||||
auto TempDir = TempPath("Dir");
|
||||
MkDir(TempDir);
|
||||
Printf("INFO: -fork=1: %zd seeds, starting to fuzz; scratch: %s\n",
|
||||
Files.size(), TempDir.c_str());
|
||||
|
||||
Command Cmd(Args);
|
||||
Cmd.removeFlag("fork");
|
||||
Command BaseCmd(Args);
|
||||
BaseCmd.removeFlag("fork");
|
||||
for (auto &C : Corpora) // Remove all corpora from the args.
|
||||
Cmd.removeArgument(C);
|
||||
if (Files.size() >= 2)
|
||||
Cmd.addFlag("seed_inputs",
|
||||
Files.back() + "," + Files[Files.size() - 2]);
|
||||
Cmd.addFlag("runs", "1000000");
|
||||
Cmd.addFlag("max_total_time", "30");
|
||||
for (size_t i = 0; i < 1000; i++) {
|
||||
BaseCmd.removeArgument(C);
|
||||
BaseCmd.addFlag("runs", "1000000");
|
||||
BaseCmd.addFlag("max_total_time", "30");
|
||||
BaseCmd.addArgument(TempDir);
|
||||
int ExitCode = 0;
|
||||
for (size_t i = 0; i < 1000000; i++) {
|
||||
// TODO: take new files from disk e.g. those generated by another process.
|
||||
Command Cmd(BaseCmd);
|
||||
if (Files.size() >= 2)
|
||||
Cmd.addFlag("seed_inputs",
|
||||
Files[Rand.SkewTowardsLast(Files.size())] + "," +
|
||||
Files[Rand.SkewTowardsLast(Files.size())]);
|
||||
Printf("RUN %s\n", Cmd.toString().c_str());
|
||||
int ExitCode = ExecuteCommand(Cmd);
|
||||
RmFilesInDir(TempDir);
|
||||
ExitCode = ExecuteCommand(Cmd);
|
||||
Printf("Exit code: %d\n", ExitCode);
|
||||
if (ExitCode == Options.InterruptExitCode)
|
||||
exit(0);
|
||||
if (ExitCode == Options.TimeoutExitCode || ExitCode == Options.OOMExitCode)
|
||||
continue;
|
||||
break;
|
||||
Vector<SizedFile> TempFiles;
|
||||
Vector<std::string>FilesToAdd;
|
||||
Set<uint32_t> NewFeatures;
|
||||
GetSizedFilesFromDir(TempDir, &TempFiles);
|
||||
CrashResistantMerge(Args, {}, TempFiles, &FilesToAdd, Features,
|
||||
&NewFeatures, CFPath);
|
||||
RemoveFile(CFPath);
|
||||
for (auto &Path : FilesToAdd) {
|
||||
auto NewPath = F->WriteToOutputCorpus(FileToVector(Path, Options.MaxLen));
|
||||
if (!NewPath.empty())
|
||||
Files.push_back(NewPath);
|
||||
}
|
||||
Features.insert(NewFeatures.begin(), NewFeatures.end());
|
||||
Printf("INFO: temp_files: %zd files_added: %zd newft: %zd ft: %zd\n",
|
||||
TempFiles.size(), FilesToAdd.size(), NewFeatures.size(),
|
||||
Features.size());
|
||||
if (ExitCode != 0) break;
|
||||
}
|
||||
|
||||
exit(0);
|
||||
RmFilesInDir(TempDir);
|
||||
RmDir(TempDir);
|
||||
|
||||
// Use the exit code from the last child process.
|
||||
Printf("Fork: exiting: %d\n", ExitCode);
|
||||
exit(ExitCode);
|
||||
}
|
||||
|
||||
void Merge(Fuzzer *F, FuzzingOptions &Options, const Vector<std::string> &Args,
|
||||
|
@ -770,7 +800,7 @@ int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) {
|
|||
}
|
||||
|
||||
if (Flags.fork)
|
||||
FuzzWithFork(Options, Args, *Inputs);
|
||||
FuzzWithFork(F, Options, Args, *Inputs);
|
||||
|
||||
if (Flags.merge)
|
||||
Merge(F, Options, Args, *Inputs, Flags.merge_control_file);
|
||||
|
|
|
@ -36,7 +36,7 @@ FUZZER_FLAG_INT(
|
|||
"If one unit runs more than this number of seconds the process will abort.")
|
||||
FUZZER_FLAG_INT(error_exitcode, 77, "When libFuzzer itself reports a bug "
|
||||
"this exit code will be used.")
|
||||
FUZZER_FLAG_INT(timeout_exitcode, 77, "When libFuzzer reports a timeout "
|
||||
FUZZER_FLAG_INT(timeout_exitcode, 70, "When libFuzzer reports a timeout "
|
||||
"this exit code will be used.")
|
||||
FUZZER_FLAG_INT(max_total_time, 0, "If positive, indicates the maximal total "
|
||||
"time in seconds to run the fuzzer.")
|
||||
|
|
|
@ -125,4 +125,11 @@ void Printf(const char *Fmt, ...) {
|
|||
fflush(OutputFile);
|
||||
}
|
||||
|
||||
void RmFilesInDir(const std::string &Path) {
|
||||
Vector<std::string> Files;
|
||||
ListFilesInDirRecursive(Path, 0, &Files, /*TopDir*/true);
|
||||
for (auto &F : Files)
|
||||
RemoveFile(F);
|
||||
}
|
||||
|
||||
} // namespace fuzzer
|
||||
|
|
|
@ -81,6 +81,10 @@ void DiscardOutput(int Fd);
|
|||
|
||||
intptr_t GetHandleFromFd(int fd);
|
||||
|
||||
void MkDir(const std::string &Path);
|
||||
void RmDir(const std::string &Path);
|
||||
void RmFilesInDir(const std::string &Path);
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
#endif // LLVM_FUZZER_IO_H
|
||||
|
|
|
@ -136,11 +136,18 @@ bool IsInterestingCoverageFile(const std::string &FileName) {
|
|||
return true;
|
||||
}
|
||||
|
||||
|
||||
void RawPrint(const char *Str) {
|
||||
write(2, Str, strlen(Str));
|
||||
}
|
||||
|
||||
void MkDir(const std::string &Path) {
|
||||
mkdir(Path.c_str(), 0700);
|
||||
}
|
||||
|
||||
void RmDir(const std::string &Path) {
|
||||
rmdir(Path.c_str());
|
||||
}
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
#endif // LIBFUZZER_POSIX
|
||||
|
|
|
@ -336,6 +336,14 @@ void RawPrint(const char *Str) {
|
|||
_write(2, Str, strlen(Str));
|
||||
}
|
||||
|
||||
void MkDir(const std::string &Path) {
|
||||
Printf("MkDir: unimplemented\n");
|
||||
}
|
||||
|
||||
void RmDir(const std::string &Path) {
|
||||
Printf("RmDir: unimplemented\n");
|
||||
}
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
#endif // LIBFUZZER_WINDOWS
|
||||
|
|
|
@ -87,7 +87,7 @@ public:
|
|||
|
||||
void HandleMalloc(size_t Size);
|
||||
static void MaybeExitGracefully();
|
||||
void WriteToOutputCorpus(const Unit &U);
|
||||
std::string WriteToOutputCorpus(const Unit &U);
|
||||
|
||||
private:
|
||||
void AlarmCallback();
|
||||
|
|
|
@ -537,15 +537,16 @@ void Fuzzer::ExecuteCallback(const uint8_t *Data, size_t Size) {
|
|||
delete[] DataCopy;
|
||||
}
|
||||
|
||||
void Fuzzer::WriteToOutputCorpus(const Unit &U) {
|
||||
std::string Fuzzer::WriteToOutputCorpus(const Unit &U) {
|
||||
if (Options.OnlyASCII)
|
||||
assert(IsASCII(U));
|
||||
if (Options.OutputCorpus.empty())
|
||||
return;
|
||||
return "";
|
||||
std::string Path = DirPlusFile(Options.OutputCorpus, Hash(U));
|
||||
WriteToFile(U, Path);
|
||||
if (Options.Verbosity >= 2)
|
||||
Printf("Written %zd bytes to %s\n", U.size(), Path.c_str());
|
||||
return Path;
|
||||
}
|
||||
|
||||
void Fuzzer::WriteUnitToFileWithPrefix(const Unit &U, const char *Prefix) {
|
||||
|
|
|
@ -120,28 +120,28 @@ size_t Merger::ApproximateMemoryConsumption() const {
|
|||
return Res;
|
||||
}
|
||||
|
||||
// Decides which files need to be merged (add thost to NewFiles).
|
||||
// Decides which files need to be merged (add those to NewFiles).
|
||||
// Returns the number of new features added.
|
||||
size_t Merger::Merge(const Set<uint32_t> &InitialFeatures,
|
||||
Set<uint32_t> *AllFeatures,
|
||||
Set<uint32_t> *NewFeatures,
|
||||
Vector<std::string> *NewFiles) {
|
||||
NewFiles->clear();
|
||||
assert(NumFilesInFirstCorpus <= Files.size());
|
||||
*AllFeatures = InitialFeatures;
|
||||
Set<uint32_t> AllFeatures = InitialFeatures;
|
||||
|
||||
// What features are in the initial corpus?
|
||||
for (size_t i = 0; i < NumFilesInFirstCorpus; i++) {
|
||||
auto &Cur = Files[i].Features;
|
||||
AllFeatures->insert(Cur.begin(), Cur.end());
|
||||
AllFeatures.insert(Cur.begin(), Cur.end());
|
||||
}
|
||||
size_t InitialNumFeatures = AllFeatures->size();
|
||||
size_t InitialNumFeatures = AllFeatures.size();
|
||||
|
||||
// Remove all features that we already know from all other inputs.
|
||||
for (size_t i = NumFilesInFirstCorpus; i < Files.size(); i++) {
|
||||
auto &Cur = Files[i].Features;
|
||||
Vector<uint32_t> Tmp;
|
||||
std::set_difference(Cur.begin(), Cur.end(), AllFeatures->begin(),
|
||||
AllFeatures->end(), std::inserter(Tmp, Tmp.begin()));
|
||||
std::set_difference(Cur.begin(), Cur.end(), AllFeatures.begin(),
|
||||
AllFeatures.end(), std::inserter(Tmp, Tmp.begin()));
|
||||
Cur.swap(Tmp);
|
||||
}
|
||||
|
||||
|
@ -161,12 +161,17 @@ size_t Merger::Merge(const Set<uint32_t> &InitialFeatures,
|
|||
auto &Cur = Files[i].Features;
|
||||
// Printf("%s -> sz %zd ft %zd\n", Files[i].Name.c_str(),
|
||||
// Files[i].Size, Cur.size());
|
||||
size_t OldSize = AllFeatures->size();
|
||||
AllFeatures->insert(Cur.begin(), Cur.end());
|
||||
if (AllFeatures->size() > OldSize)
|
||||
bool FoundNewFeatures = false;
|
||||
for (auto Fe: Cur) {
|
||||
if (AllFeatures.insert(Fe).second) {
|
||||
FoundNewFeatures = true;
|
||||
NewFeatures->insert(Fe);
|
||||
}
|
||||
}
|
||||
if (FoundNewFeatures)
|
||||
NewFiles->push_back(Files[i].Name);
|
||||
}
|
||||
return AllFeatures->size() - InitialNumFeatures;
|
||||
return AllFeatures.size() - InitialNumFeatures;
|
||||
}
|
||||
|
||||
Set<uint32_t> Merger::AllFeatures() const {
|
||||
|
|
|
@ -20,6 +20,11 @@ class Random : public std::mt19937 {
|
|||
result_type operator()() { return this->std::mt19937::operator()(); }
|
||||
size_t Rand() { return this->operator()(); }
|
||||
size_t RandBool() { return Rand() % 2; }
|
||||
size_t SkewTowardsLast(size_t n) {
|
||||
size_t T = this->operator()(n * n);
|
||||
size_t Res = sqrt(T);
|
||||
return Res;
|
||||
}
|
||||
size_t operator()(size_t n) { return n ? Rand() % n : 0; }
|
||||
intptr_t operator()(intptr_t From, intptr_t To) {
|
||||
assert(From < To);
|
||||
|
|
|
@ -13,12 +13,18 @@
|
|||
#include "FuzzerCommand.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
int ExecuteCommand(const Command &Cmd) {
|
||||
std::string CmdLine = Cmd.toString();
|
||||
return system(CmdLine.c_str());
|
||||
int exit_code = system(CmdLine.c_str());
|
||||
if (WIFEXITED(exit_code))
|
||||
return WEXITSTATUS(exit_code);
|
||||
return exit_code;
|
||||
}
|
||||
|
||||
} // namespace fuzzer
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
CHECK: BINGO
|
||||
RUN: %cpp_compiler %S/SimpleTest.cpp -o %t-SimpleTest
|
||||
|
||||
RUN: not %run %t-SimpleTest 2>&1 | FileCheck %s
|
||||
RUN: not %run %t-SimpleTest 2>&1 | FileCheck %s
|
||||
RUN: not %run %t-SimpleTest -fork=1 2>&1 | FileCheck %s
|
||||
|
||||
# only_ascii mode. Will perform some minimal self-validation.
|
||||
RUN: not %run %t-SimpleTest -only_ascii=1 2>&1
|
||||
|
|
Loading…
Reference in New Issue