summaryrefslogtreecommitdiffstats
path: root/compiler-rt/lib/fuzzer
diff options
context:
space:
mode:
authorKostya Serebryany <kcc@google.com>2019-02-08 21:27:23 +0000
committerKostya Serebryany <kcc@google.com>2019-02-08 21:27:23 +0000
commitf762a11544b77bcb5cbb45e9726562018cd5c8fa (patch)
treed9a22b41759cdc51c01d54feeb26a7d1a5a23044 /compiler-rt/lib/fuzzer
parent3bf72d7d64b8465acd4f4af1a469d68d9dc86058 (diff)
downloadbcm5719-llvm-f762a11544b77bcb5cbb45e9726562018cd5c8fa.tar.gz
bcm5719-llvm-f762a11544b77bcb5cbb45e9726562018cd5c8fa.zip
[libFuzzer] introduce an experimental mode -fork=1, where fuzzing happens in a subprocess (still running multiple inputs per process), thus making the fuzzing more resilient to timeouts and OOMs. This is just a skeleton of the code, and some associated refactoring, not a fully working feature yet.
llvm-svn: 353570
Diffstat (limited to 'compiler-rt/lib/fuzzer')
-rw-r--r--compiler-rt/lib/fuzzer/FuzzerDriver.cpp68
-rw-r--r--compiler-rt/lib/fuzzer/FuzzerFlags.def2
-rw-r--r--compiler-rt/lib/fuzzer/FuzzerInternal.h9
-rw-r--r--compiler-rt/lib/fuzzer/FuzzerLoop.cpp4
-rw-r--r--compiler-rt/lib/fuzzer/FuzzerMerge.cpp42
-rw-r--r--compiler-rt/lib/fuzzer/FuzzerMerge.h10
-rw-r--r--compiler-rt/lib/fuzzer/tests/FuzzerUnittest.cpp6
7 files changed, 89 insertions, 52 deletions
diff --git a/compiler-rt/lib/fuzzer/FuzzerDriver.cpp b/compiler-rt/lib/fuzzer/FuzzerDriver.cpp
index 2bc895d008c..418bebb9388 100644
--- a/compiler-rt/lib/fuzzer/FuzzerDriver.cpp
+++ b/compiler-rt/lib/fuzzer/FuzzerDriver.cpp
@@ -16,6 +16,7 @@
#include "FuzzerMutate.h"
#include "FuzzerRandom.h"
#include "FuzzerTracePC.h"
+#include "FuzzerMerge.h"
#include <algorithm>
#include <atomic>
#include <chrono>
@@ -24,6 +25,7 @@
#include <mutex>
#include <string>
#include <thread>
+#include <fstream>
// This function should be present in the libFuzzer so that the client
// binary can test for its existence.
@@ -304,6 +306,11 @@ static std::string GetDedupTokenFromFile(const std::string &Path) {
return S.substr(Beg, End - Beg);
}
+static std::string TempPath(const char *Extension) {
+ return DirPlusFile(TmpDir(),
+ "libFuzzerTemp." + std::to_string(GetPid()) + Extension);
+}
+
int CleanseCrashInput(const Vector<std::string> &Args,
const FuzzingOptions &Options) {
if (Inputs->size() != 1 || !Flags.exact_artifact_path) {
@@ -319,10 +326,8 @@ int CleanseCrashInput(const Vector<std::string> &Args,
assert(Cmd.hasArgument(InputFilePath));
Cmd.removeArgument(InputFilePath);
- auto LogFilePath = DirPlusFile(
- TmpDir(), "libFuzzerTemp." + std::to_string(GetPid()) + ".txt");
- auto TmpFilePath = DirPlusFile(
- TmpDir(), "libFuzzerTemp." + std::to_string(GetPid()) + ".repro");
+ auto LogFilePath = TempPath(".txt");
+ auto TmpFilePath = TempPath(".repro");
Cmd.addArgument(TmpFilePath);
Cmd.setOutputFile(LogFilePath);
Cmd.combineOutAndErr();
@@ -382,8 +387,7 @@ int MinimizeCrashInput(const Vector<std::string> &Args,
BaseCmd.addFlag("max_total_time", "600");
}
- auto LogFilePath = DirPlusFile(
- TmpDir(), "libFuzzerTemp." + std::to_string(GetPid()) + ".txt");
+ auto LogFilePath = TempPath(".txt");
BaseCmd.setOutputFile(LogFilePath);
BaseCmd.combineOutAndErr();
@@ -467,6 +471,36 @@ int MinimizeCrashInputInternalStep(Fuzzer *F, InputCorpus *Corpus) {
return 0;
}
+// This is just a sceleton of an experimental -fork=1 feature.
+void FuzzWithFork(const FuzzingOptions &Options,
+ const Vector<std::string> &Args,
+ const Vector<std::string> &Corpora) {
+ auto CFPath = TempPath(".fork");
+ Printf("INFO: -fork=1: doing fuzzing in a separate process in order to "
+ "be more resistant to crashes, timeouts, and OOMs\n");
+ auto Files =
+ CrashResistantMerge(Args, Corpora, CFPath, nullptr, nullptr);
+ Printf("INFO: -fork=1: seed corpus analyzed, %zd seeds chosen, starting to "
+ "fuzz in separate processes\n", Files.size());
+
+ Command Cmd(Args);
+ Cmd.removeFlag("fork");
+ 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++) {
+ Printf("RUN %s\n", Cmd.toString().c_str());
+ int ExitCode = ExecuteCommand(Cmd);
+ // TODO: sniff the crash, ignore OOMs and timeouts.
+ if (ExitCode != 0) break;
+ }
+
+ RemoveFile(CFPath);
+ exit(0);
+}
+
int AnalyzeDictionary(Fuzzer *F, const Vector<Unit>& Dict,
UnitVector& Corpus) {
Printf("Started dictionary minimization (up to %d tests)\n",
@@ -694,11 +728,25 @@ int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) {
exit(0);
}
+ if (Flags.fork)
+ FuzzWithFork(Options, Args, *Inputs);
+
if (Flags.merge) {
- F->CrashResistantMerge(Args, *Inputs,
- Flags.load_coverage_summary,
- Flags.save_coverage_summary,
- Flags.merge_control_file);
+ if (Inputs->size() < 2) {
+ Printf("INFO: Merge requires two or more corpus dirs\n");
+ exit(0);
+ }
+ std::string CFPath =
+ Flags.merge_control_file ? Flags.merge_control_file : TempPath(".txt");
+ auto Files =
+ CrashResistantMerge(Args, *Inputs, CFPath, Flags.load_coverage_summary,
+ Flags.save_coverage_summary);
+ for (auto &Path : Files)
+ F->WriteToOutputCorpus(FileToVector(Path, Options.MaxLen));
+ // We are done, delete the control file if it was a temporary one.
+ if (!Flags.merge_control_file)
+ RemoveFile(CFPath);
+
exit(0);
}
diff --git a/compiler-rt/lib/fuzzer/FuzzerFlags.def b/compiler-rt/lib/fuzzer/FuzzerFlags.def
index c762bafa7ae..d59ee01ff04 100644
--- a/compiler-rt/lib/fuzzer/FuzzerFlags.def
+++ b/compiler-rt/lib/fuzzer/FuzzerFlags.def
@@ -41,6 +41,8 @@ FUZZER_FLAG_INT(timeout_exitcode, 77, "When libFuzzer reports a timeout "
FUZZER_FLAG_INT(max_total_time, 0, "If positive, indicates the maximal total "
"time in seconds to run the fuzzer.")
FUZZER_FLAG_INT(help, 0, "Print help.")
+FUZZER_FLAG_INT(fork, 0, "Experimental mode where fuzzing happens "
+ "in a subprocess")
FUZZER_FLAG_INT(merge, 0, "If 1, the 2-nd, 3-rd, etc corpora will be "
"merged into the 1-st corpus. Only interesting units will be taken. "
"This flag can be used to minimize a corpus.")
diff --git a/compiler-rt/lib/fuzzer/FuzzerInternal.h b/compiler-rt/lib/fuzzer/FuzzerInternal.h
index e934e111488..ca4cdf81e2d 100644
--- a/compiler-rt/lib/fuzzer/FuzzerInternal.h
+++ b/compiler-rt/lib/fuzzer/FuzzerInternal.h
@@ -73,11 +73,6 @@ public:
// Merge Corpora[1:] into Corpora[0].
void Merge(const Vector<std::string> &Corpora);
- void CrashResistantMerge(const Vector<std::string> &Args,
- const Vector<std::string> &Corpora,
- const char *CoverageSummaryInputPathOrNull,
- const char *CoverageSummaryOutputPathOrNull,
- const char *MergeControlFilePathOrNull);
void CrashResistantMergeInternalStep(const std::string &ControlFilePath);
MutationDispatcher &GetMD() { return MD; }
void PrintFinalStats();
@@ -91,19 +86,19 @@ public:
bool DuringInitialCorpusExecution);
void HandleMalloc(size_t Size);
+ static void MaybeExitGracefully();
+ void WriteToOutputCorpus(const Unit &U);
private:
void AlarmCallback();
void CrashCallback();
void ExitCallback();
- void MaybeExitGracefully();
void CrashOnOverwrittenData();
void InterruptCallback();
void MutateAndTestOne();
void PurgeAllocator();
void ReportNewCoverage(InputInfo *II, const Unit &U);
void PrintPulseAndReportSlowInput(const uint8_t *Data, size_t Size);
- void WriteToOutputCorpus(const Unit &U);
void WriteUnitToFileWithPrefix(const Unit &U, const char *Prefix);
void PrintStats(const char *Where, const char *End = "\n", size_t Units = 0);
void PrintStatusForNewUnit(const Unit &U, const char *Text);
diff --git a/compiler-rt/lib/fuzzer/FuzzerLoop.cpp b/compiler-rt/lib/fuzzer/FuzzerLoop.cpp
index 58fd5c3e97b..3b5e20e68bb 100644
--- a/compiler-rt/lib/fuzzer/FuzzerLoop.cpp
+++ b/compiler-rt/lib/fuzzer/FuzzerLoop.cpp
@@ -256,9 +256,9 @@ void Fuzzer::ExitCallback() {
}
void Fuzzer::MaybeExitGracefully() {
- if (!GracefulExitRequested) return;
+ if (!F->GracefulExitRequested) return;
Printf("==%lu== INFO: libFuzzer: exiting as requested\n", GetPid());
- PrintFinalStats();
+ F->PrintFinalStats();
_Exit(0);
}
diff --git a/compiler-rt/lib/fuzzer/FuzzerMerge.cpp b/compiler-rt/lib/fuzzer/FuzzerMerge.cpp
index 77b8a789a02..b4f05e3b8b0 100644
--- a/compiler-rt/lib/fuzzer/FuzzerMerge.cpp
+++ b/compiler-rt/lib/fuzzer/FuzzerMerge.cpp
@@ -122,7 +122,7 @@ size_t Merger::ApproximateMemoryConsumption() const {
// Decides which files need to be merged (add thost to NewFiles).
// Returns the number of new features added.
-size_t Merger::Merge(const Set<uint32_t> &InitialFeatures,
+size_t Merger::Merge(const Set<uint32_t> &InitialFeatures,
Vector<std::string> *NewFiles) {
NewFiles->clear();
assert(NumFilesInFirstCorpus <= Files.size());
@@ -223,7 +223,7 @@ void Fuzzer::CrashResistantMergeInternalStep(const std::string &CFPath) {
std::ofstream OF(CFPath, std::ofstream::out | std::ofstream::app);
Set<size_t> AllFeatures;
for (size_t i = M.FirstNotProcessedFile; i < M.Files.size(); i++) {
- MaybeExitGracefully();
+ Fuzzer::MaybeExitGracefully();
auto U = FileToVector(M.Files[i].Name);
if (U.size() > MaxInputLen) {
U.resize(MaxInputLen);
@@ -275,27 +275,18 @@ static void WriteNewControlFile(const std::string &CFPath,
}
// Outer process. Does not call the target code and thus sohuld not fail.
-void Fuzzer::CrashResistantMerge(const Vector<std::string> &Args,
- const Vector<std::string> &Corpora,
- const char *CoverageSummaryInputPathOrNull,
- const char *CoverageSummaryOutputPathOrNull,
- const char *MergeControlFilePathOrNull) {
- if (Corpora.size() <= 1) {
- Printf("Merge requires two or more corpus dirs\n");
- return;
- }
- auto CFPath =
- MergeControlFilePathOrNull
- ? MergeControlFilePathOrNull
- : DirPlusFile(TmpDir(),
- "libFuzzerTemp." + std::to_string(GetPid()) + ".txt");
-
+Vector<std::string>
+CrashResistantMerge(const Vector<std::string> &Args,
+ const Vector<std::string> &Corpora,
+ const std::string &CFPath,
+ const char *CoverageSummaryInputPathOrNull,
+ const char *CoverageSummaryOutputPathOrNull) {
size_t NumAttempts = 0;
- if (MergeControlFilePathOrNull && FileSize(MergeControlFilePathOrNull)) {
+ if (FileSize(CFPath)) {
Printf("MERGE-OUTER: non-empty control file provided: '%s'\n",
- MergeControlFilePathOrNull);
+ CFPath.c_str());
Merger M;
- std::ifstream IF(MergeControlFilePathOrNull);
+ std::ifstream IF(CFPath);
if (M.Parse(IF, /*ParseCoverage=*/false)) {
Printf("MERGE-OUTER: control file ok, %zd files total,"
" first not processed file %zd\n",
@@ -334,9 +325,10 @@ void Fuzzer::CrashResistantMerge(const Vector<std::string> &Args,
// Every inner process should execute at least one input.
Command BaseCmd(Args);
BaseCmd.removeFlag("merge");
+ BaseCmd.removeFlag("fork");
bool Success = false;
for (size_t Attempt = 1; Attempt <= NumAttempts; Attempt++) {
- MaybeExitGracefully();
+ Fuzzer::MaybeExitGracefully();
Printf("MERGE-OUTER: attempt %zd\n", Attempt);
Command Cmd(BaseCmd);
Cmd.addFlag("merge_control_file", CFPath);
@@ -368,7 +360,6 @@ void Fuzzer::CrashResistantMerge(const Vector<std::string> &Args,
std::ofstream SummaryOut(CoverageSummaryOutputPathOrNull);
M.PrintSummary(SummaryOut);
}
- Vector<std::string> NewFiles;
Set<uint32_t> InitialFeatures;
if (CoverageSummaryInputPathOrNull) {
std::ifstream SummaryIn(CoverageSummaryInputPathOrNull);
@@ -376,14 +367,11 @@ void Fuzzer::CrashResistantMerge(const Vector<std::string> &Args,
Printf("MERGE-OUTER: coverage summary loaded from %s, %zd features found\n",
CoverageSummaryInputPathOrNull, InitialFeatures.size());
}
+ Vector<std::string> NewFiles;
size_t NumNewFeatures = M.Merge(InitialFeatures, &NewFiles);
Printf("MERGE-OUTER: %zd new files with %zd new features added\n",
NewFiles.size(), NumNewFeatures);
- for (auto &F: NewFiles)
- WriteToOutputCorpus(FileToVector(F, MaxInputLen));
- // We are done, delete the control file if it was a temporary one.
- if (!MergeControlFilePathOrNull)
- RemoveFile(CFPath);
+ return NewFiles;
}
} // namespace fuzzer
diff --git a/compiler-rt/lib/fuzzer/FuzzerMerge.h b/compiler-rt/lib/fuzzer/FuzzerMerge.h
index 49031d3b408..917108e3f09 100644
--- a/compiler-rt/lib/fuzzer/FuzzerMerge.h
+++ b/compiler-rt/lib/fuzzer/FuzzerMerge.h
@@ -67,13 +67,17 @@ struct Merger {
Set<uint32_t> ParseSummary(std::istream &IS);
size_t Merge(const Set<uint32_t> &InitialFeatures,
Vector<std::string> *NewFiles);
- size_t Merge(Vector<std::string> *NewFiles) {
- return Merge(Set<uint32_t>{}, NewFiles);
- }
size_t ApproximateMemoryConsumption() const;
Set<uint32_t> AllFeatures() const;
};
+Vector<std::string>
+CrashResistantMerge(const Vector<std::string> &Args,
+ const Vector<std::string> &Corpora,
+ const std::string &CFPath,
+ const char *CoverageSummaryInputPathOrNull,
+ const char *CoverageSummaryOutputPathOrNull);
+
} // namespace fuzzer
#endif // LLVM_FUZZER_MERGE_H
diff --git a/compiler-rt/lib/fuzzer/tests/FuzzerUnittest.cpp b/compiler-rt/lib/fuzzer/tests/FuzzerUnittest.cpp
index 16a1fc5cdf0..861eaeda6c5 100644
--- a/compiler-rt/lib/fuzzer/tests/FuzzerUnittest.cpp
+++ b/compiler-rt/lib/fuzzer/tests/FuzzerUnittest.cpp
@@ -647,7 +647,7 @@ static void Merge(const std::string &Input,
EXPECT_TRUE(M.Parse(Input, true));
std::stringstream SS;
M.PrintSummary(SS);
- EXPECT_EQ(NumNewFeatures, M.Merge(&NewFiles));
+ EXPECT_EQ(NumNewFeatures, M.Merge({}, &NewFiles));
EXPECT_EQ(M.AllFeatures(), M.ParseSummary(SS));
EQ(NewFiles, Result);
}
@@ -705,7 +705,7 @@ TEST(Merge, Good) {
EQ(M.Files[0].Features, {1, 2, 3});
EQ(M.Files[1].Features, {4, 5, 6});
EQ(M.Files[2].Features, {1, 3, 6});
- EXPECT_EQ(0U, M.Merge(&NewFiles));
+ EXPECT_EQ(0U, M.Merge({}, &NewFiles));
EQ(NewFiles, {});
EXPECT_TRUE(M.Parse("3\n1\nA\nB\nC\n"
@@ -716,7 +716,7 @@ TEST(Merge, Good) {
EQ(M.Files[0].Features, {1, 2, 3});
EQ(M.Files[1].Features, {4, 5, 6});
EQ(M.Files[2].Features, {1, 3, 6});
- EXPECT_EQ(3U, M.Merge(&NewFiles));
+ EXPECT_EQ(3U, M.Merge({}, &NewFiles));
EQ(NewFiles, {"B"});
// Same as the above, but with InitialFeatures.
OpenPOWER on IntegriCloud