Skip to content

Commit b07feef

Browse files
committed
[libc] This commit fixes the strcmp fuzzing test. It uses a single input and
splits it into two by using the value of the first byte to determine the length of the first string. Reviewed-by: PaulkaToast, Differential Revision: https://reviews.llvm.org/D82427 Summary: [libc] Since only one input is given, it is necessary to split the string into two containers so that they can be compared for the purposes of this fuzz test. This is done in the following manner: 1. Take the value of the first byte; this is size1. (Credits to @PaulkaToast for this idea). 2. size2 is the value of size - size1. 3. Copy the characters to new containers, data1 and data2 with corresponding sizes. 4. Add a null terminator to the first container, and verify the second container has a null terminator. 5. Verify output of strcmp. A simpler alternative considered was simply splitting the input data into two, but this means the two strings are always within +- 1 character of each other. This above implementation avoids this. ninja check-libc was run; no issues. Reviewers: PaulkaToast, sivachandra Reviewed By: PaulkaToast Subscribers: mgorny, tschuett, ecnelises, libc-commits, PaulkaToast Tags: #libc-project Differential Revision: https://reviews.llvm.org/D82427
1 parent 2552115 commit b07feef

File tree

2 files changed

+53
-24
lines changed

2 files changed

+53
-24
lines changed

libc/fuzzing/string/CMakeLists.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,11 @@ add_libc_fuzzer(
77
libc.src.string.strcpy
88
libc.src.string.strlen
99
)
10+
11+
add_libc_fuzzer(
12+
strcmp_fuzz
13+
SRCS
14+
strcmp_fuzz.cpp
15+
DEPENDS
16+
libc.src.string.strcmp
17+
)

libc/fuzzing/string/strcmp_fuzz.cpp

Lines changed: 45 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -13,45 +13,66 @@
1313
#include <stddef.h>
1414
#include <stdint.h>
1515

16-
extern "C" int LLVMFuzzerTestTwoInputs(const uint8_t *data1, size_t size1,
17-
const uint8_t *data2, size_t size2) {
18-
// Verify each data source contains at least one character.
19-
if (!size1 || !size2)
20-
return 0;
21-
// Verify that the final character is the null terminator.
22-
if (data1[size1 - 1] != '\0' || data2[size2 - 1] != '\0')
16+
// The general structure is to take the value of the first byte, set size1 to
17+
// that value, and add the null terminator. size2 will then contain the rest of
18+
// the bytes in data.
19+
// For example, with inputs (data={2, 6, 4, 8, 0}, size=5):
20+
// size1: data[0] = 2
21+
// data1: {2, 6} + '\0' = {2, 6, '\0'}
22+
// size2: size - size1 = 3
23+
// data2: {4, 8, '\0'}
24+
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
25+
// Verify the size is at least 1 and the data is null terminated.
26+
if (!size || data[size - 1] != '\0')
2327
return 0;
2428

25-
const char *s1 = reinterpret_cast<const char *>(data1);
26-
const char *s2 = reinterpret_cast<const char *>(data2);
29+
const size_t size1 = (data[0] <= size ? data[0] : size);
30+
const size_t size2 = size - size1;
2731

28-
const size_t minimum_size = size1 < size2 ? size1 : size2;
32+
// The first size will always be at least 1 since
33+
// we need to append the null terminator. The second size
34+
// needs to be checked since it must also contain the null
35+
// terminator.
36+
if (!size2)
37+
return 0;
38+
39+
// Copy the data into new containers.
40+
// Add one to data1 for null terminator.
41+
uint8_t *data1 = new uint8_t[size1 + 1];
42+
uint8_t *data2 = new uint8_t[size2];
43+
if (!data1 || !data2)
44+
__builtin_trap();
2945

30-
// Iterate through until either the minimum size is hit,
31-
// a character is the null terminator, or the first set
32-
// of differed bytes between s1 and s2 are found.
33-
// No bytes following a null byte should be compared.
3446
size_t i;
35-
for (i = 0; i < minimum_size; ++i) {
36-
if (!s1[i] || s1[i] != s2[i])
37-
break;
38-
}
47+
for (i = 0; i < size1; ++i)
48+
data1[i] = data[i];
49+
data1[size1] = '\0'; // Add null terminator to data1.
50+
51+
for (size_t j = 0; j < size2; ++j)
52+
data2[j] = data[i++];
3953

40-
int expected_result = s1[i] - s2[i];
41-
int actual_result = __llvm_libc::strcmp(s1, s2);
54+
const char *s1 = reinterpret_cast<const char *>(data1);
55+
const char *s2 = reinterpret_cast<const char *>(data2);
56+
size_t k = 0;
57+
// Iterate until a null terminator is hit or the character comparison is
58+
// different.
59+
while (s1[k] && s2[k] && s1[k] == s2[k])
60+
++k;
4261

62+
const unsigned char ch1 = static_cast<unsigned char>(s1[k]);
63+
const unsigned char ch2 = static_cast<unsigned char>(s2[k]);
4364
// The expected result should be the difference between the first non-equal
4465
// characters of s1 and s2. If all characters are equal, the expected result
4566
// should be '\0' - '\0' = 0.
46-
if (expected_result != actual_result)
67+
if (__llvm_libc::strcmp(s1, s2) != ch1 - ch2)
4768
__builtin_trap();
4869

4970
// Verify reversed operands. This should be the negated value of the previous
5071
// result, except of course if the previous result was zero.
51-
expected_result = s2[i] - s1[i];
52-
actual_result = __llvm_libc::strcmp(s2, s1);
53-
if (expected_result != actual_result)
72+
if (__llvm_libc::strcmp(s2, s1) != ch2 - ch1)
5473
__builtin_trap();
5574

75+
delete[] data1;
76+
delete[] data2;
5677
return 0;
5778
}

0 commit comments

Comments
 (0)