Skip to content

Commit d632fba

Browse files
use custom mutator instead
1 parent 5a28733 commit d632fba

File tree

2 files changed

+83
-93
lines changed

2 files changed

+83
-93
lines changed

libc/fuzzing/__support/CMakeLists.txt

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ add_libc_fuzzer(
1212
hashtable_fuzz.cpp
1313
DEPENDS
1414
libc.src.__support.HashTable.table
15-
libc.src.string.memcpy
1615
)
1716

1817
add_libc_fuzzer(
@@ -21,7 +20,6 @@ add_libc_fuzzer(
2120
hashtable_fuzz.cpp
2221
DEPENDS
2322
libc.src.__support.HashTable.table
24-
libc.src.string.memcpy
2523
COMPILE_OPTIONS
2624
-D__LIBC_EXPLICIT_SIMD_OPT
2725
)

libc/fuzzing/__support/hashtable_fuzz.cpp

Lines changed: 83 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -9,110 +9,102 @@
99
/// Fuzzing test for llvm-libc hashtable implementations.
1010
///
1111
//===----------------------------------------------------------------------===//
12-
#include "src/__support/CPP/new.h"
13-
#include "src/__support/CPP/optional.h"
14-
#include "src/__support/CPP/string.h"
15-
#include "src/__support/CPP/utility/forward.h"
12+
#include "include/llvm-libc-types/ENTRY.h"
13+
#include "src/__support/CPP/string_view.h"
1614
#include "src/__support/HashTable/table.h"
17-
#include <stdint.h>
15+
1816
namespace LIBC_NAMESPACE {
1917

20-
template <typename T> class UniquePtr {
21-
T *ptr;
18+
// A fuzzing payload starts with
19+
// - uint16_t: initial capacity for table A
20+
// - uint64_t: seed for table A
21+
// - uint16_t: initial capacity for table B
22+
// - uint64_t: seed for table B
23+
// Followed by a sequence of actions:
24+
// - CrossCheck: only a single byte valued 3
25+
// - Find: a single byte valued 0 followed by a null-terminated string
26+
// - Insert: a single byte valued 1 followed by a null-terminated string
27+
static constexpr size_t INITIAL_HEADER_SIZE =
28+
2 * (sizeof(uint16_t) + sizeof(uint64_t));
29+
extern "C" size_t LLVMFuzzerMutate(uint8_t *data, size_t size, size_t max_size);
30+
extern "C" size_t LLVMFuzzerCustomMutator(uint8_t *data, size_t size,
31+
size_t max_size, unsigned int seed) {
32+
size = LLVMFuzzerMutate(data, size, max_size);
33+
// not enough to read the initial capacities and seeds
34+
if (size < INITIAL_HEADER_SIZE)
35+
return 0;
2236

23-
public:
24-
UniquePtr(T *ptr) : ptr(ptr) {}
25-
~UniquePtr() { delete ptr; }
26-
UniquePtr(UniquePtr &&other) : ptr(other.ptr) { other.ptr = nullptr; }
27-
UniquePtr &operator=(UniquePtr &&other) {
28-
delete ptr;
29-
ptr = other.ptr;
30-
other.ptr = nullptr;
31-
return *this;
32-
}
33-
T *operator->() { return ptr; }
34-
template <typename... U> static UniquePtr create(U &&...x) {
35-
AllocChecker ac;
36-
T *ptr = new (ac) T(cpp::forward<U>(x)...);
37-
if (!ac)
38-
return {nullptr};
39-
return UniquePtr(ptr);
37+
// skip the initial capacities and seeds
38+
size_t i = INITIAL_HEADER_SIZE;
39+
while (i < size) {
40+
switch (data[i] % 3) {
41+
case 2:
42+
// cross check
43+
break;
44+
45+
default:
46+
// find or insert
47+
// check if there is enough space for the action byte and the
48+
// null-terminator
49+
if (i + 2 >= max_size)
50+
return i;
51+
// skip the action byte
52+
++i;
53+
// skip the null-terminated string
54+
while (i < max_size && data[i] != 0)
55+
++i;
56+
// in the case the string is not null-terminated, null-terminate it
57+
if (i == max_size && data[i - 1] != 0)
58+
data[i - 1] = 0;
59+
break;
60+
}
61+
// move to the next action
62+
++i;
4063
}
41-
operator bool() { return ptr != nullptr; }
42-
T *get() { return ptr; }
43-
};
64+
// return the new size
65+
return i;
66+
}
4467

4568
// a tagged union
4669
struct Action {
4770
enum class Tag { Find, Insert, CrossCheck } tag;
48-
cpp::string key;
49-
UniquePtr<Action> next;
50-
Action(Tag tag, cpp::string key, UniquePtr<Action> next)
51-
: tag(tag), key(cpp::move(key)), next(cpp::move(next)) {}
71+
cpp::string_view key;
5272
};
5373

5474
static struct {
55-
UniquePtr<Action> actions = nullptr;
5675
size_t remaining;
5776
const char *buffer;
5877

59-
template <typename T> cpp::optional<T> next() {
78+
template <typename T> T next() {
6079
static_assert(cpp::is_integral<T>::value, "T must be an integral type");
6180
union {
6281
T result;
6382
char data[sizeof(T)];
6483
};
65-
if (remaining < sizeof(result))
66-
return cpp::nullopt;
6784
for (size_t i = 0; i < sizeof(result); i++)
6885
data[i] = buffer[i];
6986
buffer += sizeof(result);
7087
remaining -= sizeof(result);
7188
return result;
7289
}
7390

74-
cpp::optional<cpp::string> next_string() {
75-
if (cpp::optional<uint16_t> len = next<uint16_t>()) {
76-
uint64_t length;
77-
for (length = 0; length < *len && length < remaining; length++)
78-
if (buffer[length] == '\0')
79-
break;
80-
cpp::string result(buffer, length);
81-
result += '\0';
82-
buffer += length;
83-
remaining -= length;
84-
return result;
85-
}
86-
return cpp::nullopt;
91+
cpp::string_view next_string() {
92+
cpp::string_view result(buffer);
93+
buffer += result.size() + 1;
94+
remaining -= result.size() + 1;
95+
return result;
8796
}
88-
Action *next_action() {
89-
if (cpp::optional<uint8_t> action = next<uint8_t>()) {
90-
switch (*action % 3) {
91-
case 0: {
92-
if (cpp::optional<cpp::string> key = next_string())
93-
actions = UniquePtr<Action>::create(
94-
Action::Tag::Find, cpp::move(*key), cpp::move(actions));
95-
else
96-
return nullptr;
97-
break;
98-
}
99-
case 1: {
100-
if (cpp::optional<cpp::string> key = next_string())
101-
actions = UniquePtr<Action>::create(
102-
Action::Tag::Insert, cpp::move(*key), cpp::move(actions));
103-
else
104-
return nullptr;
105-
break;
106-
}
107-
case 2: {
108-
actions = UniquePtr<Action>::create(Action::Tag::CrossCheck, "",
109-
cpp::move(actions));
110-
break;
111-
}
112-
}
113-
return actions.get();
97+
98+
Action next_action() {
99+
uint8_t byte = next<uint8_t>();
100+
switch (byte % 3) {
101+
case 2:
102+
return {Action::Tag::CrossCheck, {}};
103+
case 1:
104+
return {Action::Tag::Insert, next_string()};
105+
default:
106+
return {Action::Tag::Find, next_string()};
114107
}
115-
return nullptr;
116108
}
117109
} global_status;
118110

@@ -136,35 +128,35 @@ class HashTable {
136128
};
137129

138130
HashTable next_hashtable() {
139-
if (cpp::optional<uint16_t> size = global_status.next<uint16_t>())
140-
if (cpp::optional<uint64_t> seed = global_status.next<uint64_t>())
141-
return HashTable(*size, *seed);
142-
143-
return HashTable(0, 0);
131+
size_t size = global_status.next<uint16_t>();
132+
uint64_t seed = global_status.next<uint64_t>();
133+
return HashTable(size, seed);
144134
}
145135

146136
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
147137
global_status.buffer = reinterpret_cast<const char *>(data);
148138
global_status.remaining = size;
149-
HashTable table_a = next_hashtable();
150-
HashTable table_b = next_hashtable();
151-
if (!table_a.is_valid() || !table_b.is_valid())
139+
140+
if (global_status.remaining < INITIAL_HEADER_SIZE)
152141
return 0;
153142

143+
HashTable table_a = next_hashtable();
144+
HashTable table_b = next_hashtable();
154145
for (;;) {
155-
Action *action = global_status.next_action();
156-
if (!action)
157-
return 0;
158-
switch (action->tag) {
146+
if (global_status.remaining == 0)
147+
break;
148+
Action action = global_status.next_action();
149+
switch (action.tag) {
159150
case Action::Tag::Find: {
160-
if (static_cast<bool>(table_a.find(action->key.c_str())) !=
161-
static_cast<bool>(table_b.find(action->key.c_str())))
151+
if (static_cast<bool>(table_a.find(action.key.data())) !=
152+
static_cast<bool>(table_b.find(action.key.data())))
162153
__builtin_trap();
163154
break;
164155
}
165156
case Action::Tag::Insert: {
166-
ENTRY *a = table_a.insert(ENTRY{action->key.data(), action->key.data()});
167-
ENTRY *b = table_b.insert(ENTRY{action->key.data(), action->key.data()});
157+
char *ptr = const_cast<char *>(action.key.data());
158+
ENTRY *a = table_a.insert(ENTRY{ptr, ptr});
159+
ENTRY *b = table_b.insert(ENTRY{ptr, ptr});
168160
if (a->data != b->data)
169161
__builtin_trap();
170162
break;

0 commit comments

Comments
 (0)