[libFuzzer] add -merge flag to merge corpora
authorKostya Serebryany <kcc@google.com>
Sat, 24 Oct 2015 01:16:40 +0000 (01:16 +0000)
committerKostya Serebryany <kcc@google.com>
Sat, 24 Oct 2015 01:16:40 +0000 (01:16 +0000)
git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@251168 91177308-0d34-0410-b5e6-96231b3b80d8

docs/LibFuzzer.rst
lib/Fuzzer/FuzzerDriver.cpp
lib/Fuzzer/FuzzerFlags.def
lib/Fuzzer/FuzzerInternal.h
lib/Fuzzer/FuzzerLoop.cpp
lib/Fuzzer/test/merge.test [new file with mode: 0644]

index 85c110f272ffc709fac04c7d712e9fadaf6837ed..97ae928988b06aa79a9874458ecaa1c46746f579 100644 (file)
@@ -64,6 +64,7 @@ The most important flags are::
   max_total_time                        0       If positive, indicates the maximal total time in seconds to run the fuzzer.
   help                                 0       Print help.
   save_minimized_corpus                0       If 1, the minimized corpus is saved into the first input directory. Example: ./fuzzer -save_minimized_corpus=1 NEW_EMPTY_DIR OLD_CORPUS
+  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.
   jobs                                 0       Number of jobs to run. If jobs >= 1 we spawn this number of jobs in separate worker processes with stdout/stderr redirected to fuzz-JOB.log.
   workers                              0       Number of simultaneous worker processes to run the jobs. If zero, "min(jobs,NumberOfCpuCores()/2)" is used.
   sync_command                         0       Execute an external command "<sync_command> <test_corpus>" to synchronize the test corpus.
index f9cb28a6e5154e407487207c313722aa02cfd9b1..821d34cccc2bf906f000118609a8786625093920 100644 (file)
@@ -269,6 +269,11 @@ int FuzzerDriver(const std::vector<std::string> &Args,
   if (Flags.test_single_input)
     return RunOneTest(&F, Flags.test_single_input);
 
+  if (Flags.merge) {
+    F.Merge(*Inputs);
+    exit(0);
+  }
+
   unsigned Seed = Flags.seed;
   // Initialize Seed.
   if (Seed == 0)
index cea28e3bf043e3100028d929cce480c13c48cd71..9cd2dedb37a2d79896f5899399e071607af1e82f 100644 (file)
@@ -36,6 +36,8 @@ FUZZER_FLAG_INT(
     save_minimized_corpus, 0,
     "If 1, the minimized corpus is saved into the first input directory. "
     "Example: ./fuzzer -save_minimized_corpus=1 NEW_EMPTY_DIR OLD_CORPUS")
+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.")
 FUZZER_FLAG_INT(use_counters, 1, "Use coverage counters")
 FUZZER_FLAG_INT(use_indir_calls, 1, "Use indirect caller-callee counters")
 FUZZER_FLAG_INT(use_traces, 0, "Experimental: use instruction traces")
index 2dc44f98def23c82680cdbbe4bbe42ba0089859d..d6e1cb85a2350f97f8e9ab8244f6bd3f57b4a4a4 100644 (file)
@@ -104,6 +104,7 @@ class Fuzzer {
   void InitializeTraceState();
   size_t CorpusSize() const { return Corpus.size(); }
   void ReadDir(const std::string &Path, long *Epoch) {
+    Printf("Loading corpus: %s\n", Path.c_str());
     ReadDirToVectorOfUnits(Path.c_str(), &Corpus, Epoch);
   }
   void RereadOutputCorpus();
@@ -121,6 +122,9 @@ class Fuzzer {
 
   void ExecuteCallback(const Unit &U);
 
+  // Merge Corpora[1:] into Corpora[0].
+  void Merge(const std::vector<std::string> &Corpora);
+
  private:
   void AlarmCallback();
   void MutateAndTestOne(Unit *U);
index 268989f103a4b865ca1157151bcc12578de36469..4f07961e066fedf3c549b87a619d092153d97aee 100644 (file)
@@ -286,6 +286,38 @@ void Fuzzer::ReportNewCoverage(const Unit &U) {
     exit(0);
 }
 
+void Fuzzer::Merge(const std::vector<std::string> &Corpora) {
+  if (Corpora.size() <= 1) {
+    Printf("Merge requires two or more corpus dirs\n");
+    return;
+  }
+  auto InitialCorpusDir = Corpora[0];
+  ReadDir(InitialCorpusDir, nullptr);
+  Printf("Merge: running the initial corpus '%s' of %d units\n",
+         InitialCorpusDir.c_str(), Corpus.size());
+  for (auto &U : Corpus)
+    RunOne(U);
+
+  std::vector<std::string> ExtraCorpora(Corpora.begin() + 1, Corpora.end());
+
+  size_t NumTried = 0;
+  size_t NumMerged = 0;
+  for (auto &C : ExtraCorpora) {
+    Corpus.clear();
+    ReadDir(C, nullptr);
+    Printf("Merge: merging the extra corpus '%s' of %zd units\n", C.c_str(),
+           Corpus.size());
+    for (auto &U : Corpus) {
+      NumTried++;
+      if (RunOne(U)) {
+        WriteToOutputCorpus(U);
+        NumMerged++;
+      }
+    }
+  }
+  Printf("Merge: written %zd out of %zd units\n", NumMerged, NumTried);
+}
+
 void Fuzzer::MutateAndTestOne(Unit *U) {
   for (int i = 0; i < Options.MutateDepth; i++) {
     StartTraceRecording();
diff --git a/lib/Fuzzer/test/merge.test b/lib/Fuzzer/test/merge.test
new file mode 100644 (file)
index 0000000..57ecc14
--- /dev/null
@@ -0,0 +1,29 @@
+CHECK: BINGO
+
+RUN: rm -rf  %tmp/T1 %tmp/T2
+RUN: mkdir -p %tmp/T1 %tmp/T2
+RUN: echo F..... > %tmp/T1/1
+RUN: echo .U.... > %tmp/T1/2
+RUN: echo ..Z... > %tmp/T1/3
+
+# T1 has 3 elements, T2 is empty.
+RUN: LLVMFuzzer-FullCoverageSetTest -merge=1 %tmp/T1 %tmp/T2 2>&1 | FileCheck %s --check-prefix=CHECK1
+CHECK1: Merge: running the initial corpus {{.*}} of 3 units
+CHECK1: Merge: written 0 out of 0 units
+
+RUN: echo ...Z.. > %tmp/T2/1
+RUN: echo ....E. > %tmp/T2/2
+RUN: echo .....R > %tmp/T2/3
+RUN: echo F..... > %tmp/T2/a
+RUN: echo .U.... > %tmp/T2/b
+RUN: echo ..Z... > %tmp/T2/c
+
+# T1 has 3 elements, T2 has 6 elements, only 3 are new.
+RUN: LLVMFuzzer-FullCoverageSetTest -merge=1 %tmp/T1 %tmp/T2 2>&1 | FileCheck %s --check-prefix=CHECK2
+CHECK2: Merge: running the initial corpus {{.*}} of 3 units
+CHECK2: Merge: written 3 out of 6 units
+
+# Now, T1 has 6 units and T2 has no new interesting units.
+RUN: LLVMFuzzer-FullCoverageSetTest -merge=1 %tmp/T1 %tmp/T2 2>&1 | FileCheck %s --check-prefix=CHECK3
+CHECK3: Merge: running the initial corpus {{.*}} of 6 units
+CHECK3: Merge: written 0 out of 6 units