9
9
// / Fuzzing test for llvm-libc hashtable implementations.
10
10
// /
11
11
// ===----------------------------------------------------------------------===//
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"
16
14
#include " src/__support/HashTable/table.h"
17
- # include < stdint.h >
15
+
18
16
namespace LIBC_NAMESPACE {
19
17
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 ;
22
36
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;
40
63
}
41
- operator bool () { return ptr != nullptr ; }
42
- T * get () { return ptr; }
43
- };
64
+ // return the new size
65
+ return i;
66
+ }
44
67
45
68
// a tagged union
46
69
struct Action {
47
70
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;
52
72
};
53
73
54
74
static struct {
55
- UniquePtr<Action> actions = nullptr ;
56
75
size_t remaining;
57
76
const char *buffer;
58
77
59
- template <typename T> cpp::optional<T> next () {
78
+ template <typename T> T next () {
60
79
static_assert (cpp::is_integral<T>::value, " T must be an integral type" );
61
80
union {
62
81
T result;
63
82
char data[sizeof (T)];
64
83
};
65
- if (remaining < sizeof (result))
66
- return cpp::nullopt;
67
84
for (size_t i = 0 ; i < sizeof (result); i++)
68
85
data[i] = buffer[i];
69
86
buffer += sizeof (result);
70
87
remaining -= sizeof (result);
71
88
return result;
72
89
}
73
90
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;
87
96
}
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 ()};
114
107
}
115
- return nullptr ;
116
108
}
117
109
} global_status;
118
110
@@ -136,35 +128,35 @@ class HashTable {
136
128
};
137
129
138
130
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);
144
134
}
145
135
146
136
extern " C" int LLVMFuzzerTestOneInput (const uint8_t *data, size_t size) {
147
137
global_status.buffer = reinterpret_cast <const char *>(data);
148
138
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)
152
141
return 0 ;
153
142
143
+ HashTable table_a = next_hashtable ();
144
+ HashTable table_b = next_hashtable ();
154
145
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 ) {
159
150
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 ())))
162
153
__builtin_trap ();
163
154
break ;
164
155
}
165
156
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});
168
160
if (a->data != b->data )
169
161
__builtin_trap ();
170
162
break ;
0 commit comments