aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKostya Serebryany <kcc@google.com>2019-02-12 00:12:33 +0000
committerKostya Serebryany <kcc@google.com>2019-02-12 00:12:33 +0000
commitc8bc1b3d9e088ca7fe256c9ee6a609f385b884b6 (patch)
treee211a457a3960ff5b9d7f3e791cfb0caaf6d033a
parent94a5e8172e123149e71c5e99422f1dfa166aeb4c (diff)
downloadcompiler-rt-c8bc1b3d9e088ca7fe256c9ee6a609f385b884b6.tar.gz
[libFuzzer] extend the -fork=1 functionality. Still not fully usable, but good enough for the first unit test
git-svn-id: https://llvm.org/svn/llvm-project/compiler-rt/trunk@353775 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--lib/fuzzer/FuzzerDriver.cpp66
-rw-r--r--lib/fuzzer/FuzzerFlags.def2
-rw-r--r--lib/fuzzer/FuzzerIO.cpp7
-rw-r--r--lib/fuzzer/FuzzerIO.h4
-rw-r--r--lib/fuzzer/FuzzerIOPosix.cpp9
-rw-r--r--lib/fuzzer/FuzzerIOWindows.cpp8
-rw-r--r--lib/fuzzer/FuzzerInternal.h2
-rw-r--r--lib/fuzzer/FuzzerLoop.cpp5
-rw-r--r--lib/fuzzer/FuzzerMerge.cpp27
-rw-r--r--lib/fuzzer/FuzzerRandom.h5
-rw-r--r--lib/fuzzer/FuzzerUtilLinux.cpp8
-rw-r--r--test/fuzzer/simple.test3
12 files changed, 110 insertions, 36 deletions
diff --git a/lib/fuzzer/FuzzerDriver.cpp b/lib/fuzzer/FuzzerDriver.cpp
index c381fb844..232b3a873 100644
--- a/lib/fuzzer/FuzzerDriver.cpp
+++ b/lib/fuzzer/FuzzerDriver.cpp
@@ -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);
diff --git a/lib/fuzzer/FuzzerFlags.def b/lib/fuzzer/FuzzerFlags.def
index 32965511d..caf541be8 100644
--- a/lib/fuzzer/FuzzerFlags.def
+++ b/lib/fuzzer/FuzzerFlags.def
@@ -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.")
diff --git a/lib/fuzzer/FuzzerIO.cpp b/lib/fuzzer/FuzzerIO.cpp
index 1ff22930e..a18fba717 100644
--- a/lib/fuzzer/FuzzerIO.cpp
+++ b/lib/fuzzer/FuzzerIO.cpp
@@ -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
diff --git a/lib/fuzzer/FuzzerIO.h b/lib/fuzzer/FuzzerIO.h
index c3bc6089e..b31aea9de 100644
--- a/lib/fuzzer/FuzzerIO.h
+++ b/lib/fuzzer/FuzzerIO.h
@@ -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
diff --git a/lib/fuzzer/FuzzerIOPosix.cpp b/lib/fuzzer/FuzzerIOPosix.cpp
index 0daed83e5..54c511ed0 100644
--- a/lib/fuzzer/FuzzerIOPosix.cpp
+++ b/lib/fuzzer/FuzzerIOPosix.cpp
@@ -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
diff --git a/lib/fuzzer/FuzzerIOWindows.cpp b/lib/fuzzer/FuzzerIOWindows.cpp
index e45c13737..bb33343b7 100644
--- a/lib/fuzzer/FuzzerIOWindows.cpp
+++ b/lib/fuzzer/FuzzerIOWindows.cpp
@@ -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
diff --git a/lib/fuzzer/FuzzerInternal.h b/lib/fuzzer/FuzzerInternal.h
index ca4cdf81e..f20dae014 100644
--- a/lib/fuzzer/FuzzerInternal.h
+++ b/lib/fuzzer/FuzzerInternal.h
@@ -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();
diff --git a/lib/fuzzer/FuzzerLoop.cpp b/lib/fuzzer/FuzzerLoop.cpp
index c3be3dd20..5cd0cddc3 100644
--- a/lib/fuzzer/FuzzerLoop.cpp
+++ b/lib/fuzzer/FuzzerLoop.cpp
@@ -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) {
diff --git a/lib/fuzzer/FuzzerMerge.cpp b/lib/fuzzer/FuzzerMerge.cpp
index 9760ac243..4d00f7ed9 100644
--- a/lib/fuzzer/FuzzerMerge.cpp
+++ b/lib/fuzzer/FuzzerMerge.cpp
@@ -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 {
diff --git a/lib/fuzzer/FuzzerRandom.h b/lib/fuzzer/FuzzerRandom.h
index f47579787..af8e1a4eb 100644
--- a/lib/fuzzer/FuzzerRandom.h
+++ b/lib/fuzzer/FuzzerRandom.h
@@ -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);
diff --git a/lib/fuzzer/FuzzerUtilLinux.cpp b/lib/fuzzer/FuzzerUtilLinux.cpp
index 640f3d9c5..d5a15d19f 100644
--- a/lib/fuzzer/FuzzerUtilLinux.cpp
+++ b/lib/fuzzer/FuzzerUtilLinux.cpp
@@ -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
diff --git a/test/fuzzer/simple.test b/test/fuzzer/simple.test
index 97a09be7c..8fbdf2568 100644
--- a/test/fuzzer/simple.test
+++ b/test/fuzzer/simple.test
@@ -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