[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. 

git-svn-id: https://llvm.org/svn/llvm-project/compiler-rt/trunk/lib/fuzzer@353570 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/FuzzerDriver.cpp b/FuzzerDriver.cpp
index 2bc895d..418bebb 100644
--- a/FuzzerDriver.cpp
+++ b/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 @@
   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 @@
   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 @@
     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 @@
   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 @@
     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);
   }