Skip to content

Commit bb54bcf

Browse files
committed
[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 790878f 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
1 parent aa48a48 commit bb54bcf

File tree

5 files changed

+56
-17
lines changed

5 files changed

+56
-17
lines changed

compiler-rt/lib/fuzzer/FuzzerMutate.cpp

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -425,26 +425,26 @@ size_t MutationDispatcher::Mutate_CrossOver(uint8_t *Data, size_t Size,
425425
if (!CrossOverWith) return 0;
426426
const Unit &O = *CrossOverWith;
427427
if (O.empty()) return 0;
428-
MutateInPlaceHere.resize(MaxSize);
429-
auto &U = MutateInPlaceHere;
430428
size_t NewSize = 0;
431429
switch(Rand(3)) {
432430
case 0:
433-
NewSize = CrossOver(Data, Size, O.data(), O.size(), U.data(), U.size());
431+
MutateInPlaceHere.resize(MaxSize);
432+
NewSize = CrossOver(Data, Size, O.data(), O.size(),
433+
MutateInPlaceHere.data(), MaxSize);
434+
memcpy(Data, MutateInPlaceHere.data(), NewSize);
434435
break;
435436
case 1:
436-
NewSize = InsertPartOf(O.data(), O.size(), U.data(), U.size(), MaxSize);
437+
NewSize = InsertPartOf(O.data(), O.size(), Data, Size, MaxSize);
437438
if (!NewSize)
438-
NewSize = CopyPartOf(O.data(), O.size(), U.data(), U.size());
439+
NewSize = CopyPartOf(O.data(), O.size(), Data, Size);
439440
break;
440441
case 2:
441-
NewSize = CopyPartOf(O.data(), O.size(), U.data(), U.size());
442+
NewSize = CopyPartOf(O.data(), O.size(), Data, Size);
442443
break;
443444
default: assert(0);
444445
}
445446
assert(NewSize > 0 && "CrossOver returned empty unit");
446447
assert(NewSize <= MaxSize && "CrossOver returned overisized unit");
447-
memcpy(Data, U.data(), NewSize);
448448
return NewSize;
449449
}
450450

compiler-rt/test/fuzzer/CrossOverTest.cpp

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,11 @@
44

55
// Test for a fuzzer. The fuzzer must find the string
66
// ABCDEFGHIJ
7-
// We use it as a test for CrossOver functionality
8-
// by passing two inputs to it:
9-
// ABCDE00000
10-
// ZZZZZFGHIJ
7+
// We use it as a test for each of CrossOver functionalities
8+
// by passing the following sets of two inputs to it:
9+
// {ABCDE00000, ZZZZZFGHIJ}
10+
// {ABCDEHIJ, ZFG} to specifically test InsertPartOf
11+
// {ABCDE00HIJ, ZFG} to specifically test CopyPartOf
1112
//
1213
#include <assert.h>
1314
#include <cstddef>
@@ -42,13 +43,11 @@ static const uint32_t ExpectedHash = 0xe1677acb;
4243

4344
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
4445
// fprintf(stderr, "ExpectedHash: %x\n", ExpectedHash);
45-
if (Size != 10) return 0;
46+
if (Size == 10 && ExpectedHash == simple_hash(Data, Size))
47+
*NullPtr = 0;
4648
if (*Data == 'A')
4749
Sink++;
4850
if (*Data == 'Z')
4951
Sink--;
50-
if (ExpectedHash == simple_hash(Data, Size))
51-
*NullPtr = 0;
5252
return 0;
5353
}
54-

compiler-rt/test/fuzzer/cross_over.test

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ RUN: echo -n ABCDE00000 > %t-corpus/A
1212
RUN: echo -n ZZZZZFGHIJ > %t-corpus/B
1313

1414

15-
RUN: not %run %t-CrossOverTest -max_len=10 -seed=1 -runs=10000000 %t-corpus
15+
RUN: not %run %t-CrossOverTest -max_len=10 -reduce_inputs=0 -seed=1 -runs=10000000 %t-corpus
1616

1717
# Test the same thing but using -seed_inputs instead of passing the corpus dir.
18-
RUN: not %run %t-CrossOverTest -max_len=10 -seed=1 -runs=10000000 -seed_inputs=%t-corpus/A,%t-corpus/B
18+
RUN: not %run %t-CrossOverTest -max_len=10 -reduce_inputs=0 -seed=1 -runs=10000000 -seed_inputs=%t-corpus/A,%t-corpus/B
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Tests CrossOver CopyPartOf.
2+
# We want to make sure that the test can find the input
3+
# ABCDEFGHIJ when given two other inputs in the seed corpus:
4+
# ABCDE00HIJ and
5+
# (Z) FG
6+
#
7+
RUN: %cpp_compiler %S/CrossOverTest.cpp -o %t-CrossOverTest
8+
9+
RUN: rm -rf %t-corpus
10+
RUN: mkdir %t-corpus
11+
RUN: echo -n ABCDE00HIJ > %t-corpus/A
12+
RUN: echo -n ZFG > %t-corpus/B
13+
14+
15+
RUN: not %run %t-CrossOverTest -mutate_depth=1 -max_len=1024 -reduce_inputs=0 -seed=1 -runs=10000000 %t-corpus 2>&1 | FileCheck %s
16+
17+
# Test the same thing but using -seed_inputs instead of passing the corpus dir.
18+
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
19+
20+
CHECK: MS: 1 CrossOver-
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Tests CrossOver InsertPartOf.
2+
# We want to make sure that the test can find the input
3+
# ABCDEFGHIJ when given two other inputs in the seed corpus:
4+
# ABCDE HIJ and
5+
# (Z) FG
6+
#
7+
RUN: %cpp_compiler %S/CrossOverTest.cpp -o %t-CrossOverTest
8+
9+
RUN: rm -rf %t-corpus
10+
RUN: mkdir %t-corpus
11+
RUN: echo -n ABCDEHIJ > %t-corpus/A
12+
RUN: echo -n ZFG > %t-corpus/B
13+
14+
15+
RUN: not %run %t-CrossOverTest -mutate_depth=1 -max_len=1024 -reduce_inputs=0 -seed=1 -runs=10000000 %t-corpus 2>&1 | FileCheck %s
16+
17+
# Test the same thing but using -seed_inputs instead of passing the corpus dir.
18+
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
19+
20+
CHECK: MS: 1 CrossOver-

0 commit comments

Comments
 (0)