[compiler-rt] bb54bcf - [libFuzzer] Fix arguments of InsertPartOf/CopyPartOf calls in CrossOver mutator.

Dokyung Song via llvm-commits llvm-commits at lists.llvm.org
Tue Aug 18 09:09:34 PDT 2020


Author: Dokyung Song
Date: 2020-08-18T16:09:18Z
New Revision: bb54bcf84970c04c9748004f3a4cf59b0c1832a7

URL: https://github.com/llvm/llvm-project/commit/bb54bcf84970c04c9748004f3a4cf59b0c1832a7
DIFF: https://github.com/llvm/llvm-project/commit/bb54bcf84970c04c9748004f3a4cf59b0c1832a7.diff

LOG: [libFuzzer] Fix arguments of InsertPartOf/CopyPartOf calls in CrossOver mutator.

The CrossOver mutator is meant to cross over two given buffers (referred to as
the first/second buffer henceforth). Previously InsertPartOf/CopyPartOf calls
used in the CrossOver mutator incorrectly inserted/copied part of the second
buffer into a "scratch buffer" (MutateInPlaceHere of the size
CurrentMaxMutationLen), rather than the first buffer. This is not intended
behavior, because the scratch buffer does not always (i) contain the content of
the first buffer, and (ii) have the same size as the first buffer;
CurrentMaxMutationLen is typically a lot larger than the size of the first
buffer. This patch fixes the issue by using the first buffer instead of the
scratch buffer in InsertPartOf/CopyPartOf calls.

A FuzzBench experiment was run to make sure that this change does not
inadvertently degrade the performance. The performance is largely the same; more
details can be found at:
https://storage.googleapis.com/fuzzer-test-suite-public/fixcrossover-report/index.html

This patch also adds two new tests, namely "cross_over_insert" and
"cross_over_copy", which specifically target InsertPartOf and CopyPartOf,
respectively.

- cross_over_insert.test checks if the fuzzer can use InsertPartOf to trigger
  the crash.

- cross_over_copy.test checks if the fuzzer can use CopyPartOf to trigger the
  crash.

These newly added tests were designed to pass with the current patch, but not
without the it (with 790878f291fa5dc58a1c560cb6cc76fd1bfd1c5a these tests do not
pass). To achieve this, -max_len was intentionally given a high value. Without
this patch, InsertPartOf/CopyPartOf will generate larger inputs, possibly with
unpredictable data in it, thereby failing to trigger the crash.

The test pass condition for these new tests is narrowed down by (i) limiting
mutation depth to 1 (i.e., a single CrossOver mutation should be able to trigger
the crash) and (ii) checking whether the mutation sequence of "CrossOver-" leads
to the crash.

Also note that these newly added tests and an existing test (cross_over.test)
all use "-reduce_inputs=0" flags to prevent reducing inputs; it's easier to
force the fuzzer to keep original input string this way than tweaking
cov-instrumented basic blocks in the source code of the fuzzer executable.

Differential Revision: https://reviews.llvm.org/D85554

Added: 
    compiler-rt/test/fuzzer/cross_over_copy.test
    compiler-rt/test/fuzzer/cross_over_insert.test

Modified: 
    compiler-rt/lib/fuzzer/FuzzerMutate.cpp
    compiler-rt/test/fuzzer/CrossOverTest.cpp
    compiler-rt/test/fuzzer/cross_over.test

Removed: 
    


################################################################################
diff  --git a/compiler-rt/lib/fuzzer/FuzzerMutate.cpp b/compiler-rt/lib/fuzzer/FuzzerMutate.cpp
index 29541eac5dc6..df9ada45bb03 100644
--- a/compiler-rt/lib/fuzzer/FuzzerMutate.cpp
+++ b/compiler-rt/lib/fuzzer/FuzzerMutate.cpp
@@ -425,26 +425,26 @@ size_t MutationDispatcher::Mutate_CrossOver(uint8_t *Data, size_t Size,
   if (!CrossOverWith) return 0;
   const Unit &O = *CrossOverWith;
   if (O.empty()) return 0;
-  MutateInPlaceHere.resize(MaxSize);
-  auto &U = MutateInPlaceHere;
   size_t NewSize = 0;
   switch(Rand(3)) {
     case 0:
-      NewSize = CrossOver(Data, Size, O.data(), O.size(), U.data(), U.size());
+      MutateInPlaceHere.resize(MaxSize);
+      NewSize = CrossOver(Data, Size, O.data(), O.size(),
+                          MutateInPlaceHere.data(), MaxSize);
+      memcpy(Data, MutateInPlaceHere.data(), NewSize);
       break;
     case 1:
-      NewSize = InsertPartOf(O.data(), O.size(), U.data(), U.size(), MaxSize);
+      NewSize = InsertPartOf(O.data(), O.size(), Data, Size, MaxSize);
       if (!NewSize)
-        NewSize = CopyPartOf(O.data(), O.size(), U.data(), U.size());
+        NewSize = CopyPartOf(O.data(), O.size(), Data, Size);
       break;
     case 2:
-      NewSize = CopyPartOf(O.data(), O.size(), U.data(), U.size());
+      NewSize = CopyPartOf(O.data(), O.size(), Data, Size);
       break;
     default: assert(0);
   }
   assert(NewSize > 0 && "CrossOver returned empty unit");
   assert(NewSize <= MaxSize && "CrossOver returned overisized unit");
-  memcpy(Data, U.data(), NewSize);
   return NewSize;
 }
 

diff  --git a/compiler-rt/test/fuzzer/CrossOverTest.cpp b/compiler-rt/test/fuzzer/CrossOverTest.cpp
index a7643570a92b..b4506f665dc7 100644
--- a/compiler-rt/test/fuzzer/CrossOverTest.cpp
+++ b/compiler-rt/test/fuzzer/CrossOverTest.cpp
@@ -4,10 +4,11 @@
 
 // Test for a fuzzer. The fuzzer must find the string
 // ABCDEFGHIJ
-// We use it as a test for CrossOver functionality
-// by passing two inputs to it:
-// ABCDE00000
-// ZZZZZFGHIJ
+// We use it as a test for each of CrossOver functionalities
+// by passing the following sets of two inputs to it:
+// {ABCDE00000, ZZZZZFGHIJ}
+// {ABCDEHIJ, ZFG} to specifically test InsertPartOf
+// {ABCDE00HIJ, ZFG} to specifically test CopyPartOf
 //
 #include <assert.h>
 #include <cstddef>
@@ -42,13 +43,11 @@ static const uint32_t ExpectedHash = 0xe1677acb;
 
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
   // fprintf(stderr, "ExpectedHash: %x\n", ExpectedHash);
-  if (Size != 10) return 0;
+  if (Size == 10 && ExpectedHash == simple_hash(Data, Size))
+    *NullPtr = 0;
   if (*Data == 'A')
     Sink++;
   if (*Data == 'Z')
     Sink--;
-  if (ExpectedHash == simple_hash(Data, Size))
-    *NullPtr = 0;
   return 0;
 }
-

diff  --git a/compiler-rt/test/fuzzer/cross_over.test b/compiler-rt/test/fuzzer/cross_over.test
index 058b5eb2c85c..64e06e8cd366 100644
--- a/compiler-rt/test/fuzzer/cross_over.test
+++ b/compiler-rt/test/fuzzer/cross_over.test
@@ -12,7 +12,7 @@ RUN: echo -n ABCDE00000 > %t-corpus/A
 RUN: echo -n ZZZZZFGHIJ > %t-corpus/B
 
 
-RUN: not %run %t-CrossOverTest -max_len=10 -seed=1 -runs=10000000 %t-corpus
+RUN: not %run %t-CrossOverTest -max_len=10 -reduce_inputs=0 -seed=1 -runs=10000000 %t-corpus
 
 # Test the same thing but using -seed_inputs instead of passing the corpus dir.
-RUN: not %run %t-CrossOverTest -max_len=10 -seed=1 -runs=10000000 -seed_inputs=%t-corpus/A,%t-corpus/B
+RUN: not %run %t-CrossOverTest -max_len=10 -reduce_inputs=0 -seed=1 -runs=10000000 -seed_inputs=%t-corpus/A,%t-corpus/B

diff  --git a/compiler-rt/test/fuzzer/cross_over_copy.test b/compiler-rt/test/fuzzer/cross_over_copy.test
new file mode 100644
index 000000000000..24b2f9b3b113
--- /dev/null
+++ b/compiler-rt/test/fuzzer/cross_over_copy.test
@@ -0,0 +1,20 @@
+# Tests CrossOver CopyPartOf.
+# We want to make sure that the test can find the input
+# ABCDEFGHIJ when given two other inputs in the seed corpus:
+#    ABCDE00HIJ and
+# (Z)     FG
+#
+RUN: %cpp_compiler %S/CrossOverTest.cpp -o %t-CrossOverTest
+
+RUN: rm -rf %t-corpus
+RUN: mkdir %t-corpus
+RUN: echo -n ABCDE00HIJ > %t-corpus/A
+RUN: echo -n ZFG > %t-corpus/B
+
+
+RUN: not %run %t-CrossOverTest -mutate_depth=1 -max_len=1024 -reduce_inputs=0 -seed=1 -runs=10000000 %t-corpus 2>&1 | FileCheck %s
+
+# Test the same thing but using -seed_inputs instead of passing the corpus dir.
+RUN: not %run %t-CrossOverTest -mutate_depth=1 -max_len=1024 -reduce_inputs=0 -seed=1 -runs=10000000 -seed_inputs=%t-corpus/A,%t-corpus/B 2>&1 | FileCheck %s
+
+CHECK: MS: 1 CrossOver-

diff  --git a/compiler-rt/test/fuzzer/cross_over_insert.test b/compiler-rt/test/fuzzer/cross_over_insert.test
new file mode 100644
index 000000000000..cb7d4fab81ef
--- /dev/null
+++ b/compiler-rt/test/fuzzer/cross_over_insert.test
@@ -0,0 +1,20 @@
+# Tests CrossOver InsertPartOf.
+# We want to make sure that the test can find the input
+# ABCDEFGHIJ when given two other inputs in the seed corpus:
+#    ABCDE  HIJ and
+# (Z)     FG
+#
+RUN: %cpp_compiler %S/CrossOverTest.cpp -o %t-CrossOverTest
+
+RUN: rm -rf %t-corpus
+RUN: mkdir %t-corpus
+RUN: echo -n ABCDEHIJ > %t-corpus/A
+RUN: echo -n ZFG > %t-corpus/B
+
+
+RUN: not %run %t-CrossOverTest -mutate_depth=1 -max_len=1024 -reduce_inputs=0 -seed=1 -runs=10000000 %t-corpus 2>&1 | FileCheck %s
+
+# Test the same thing but using -seed_inputs instead of passing the corpus dir.
+RUN: not %run %t-CrossOverTest -mutate_depth=1 -max_len=1024 -reduce_inputs=0 -seed=1 -runs=10000000 -seed_inputs=%t-corpus/A,%t-corpus/B 2>&1 | FileCheck %s
+
+CHECK: MS: 1 CrossOver-


        


More information about the llvm-commits mailing list