/work/testing/fuzzing/fuzz_support.cc
Line | Count | Source (jump to first uncovered line) |
1 | | /* SPDX-License-Identifier: GPL-3.0-or-later |
2 | | * Copyright © 2021-2022 The TokTok team. |
3 | | */ |
4 | | |
5 | | #include "fuzz_support.h" |
6 | | |
7 | | #include <arpa/inet.h> |
8 | | #include <sys/socket.h> |
9 | | |
10 | | #include <algorithm> |
11 | | #include <cassert> |
12 | | #include <cerrno> |
13 | | #include <climits> |
14 | | #include <cstdio> |
15 | | #include <cstring> |
16 | | #include <memory> |
17 | | |
18 | | #include "../../toxcore/crypto_core.h" |
19 | | #include "../../toxcore/network.h" |
20 | | #include "../../toxcore/tox_private.h" |
21 | | #include "func_conversion.h" |
22 | | |
23 | | // TODO(iphydf): Put this somewhere shared. |
24 | | struct Network_Addr { |
25 | | struct sockaddr_storage addr; |
26 | | size_t size; |
27 | | }; |
28 | | |
29 | | System::System(std::unique_ptr<Tox_System> in_sys, std::unique_ptr<Memory> in_mem, |
30 | | std::unique_ptr<Network> in_ns, std::unique_ptr<Random> in_rng) |
31 | | : sys(std::move(in_sys)) |
32 | | , mem(std::move(in_mem)) |
33 | | , ns(std::move(in_ns)) |
34 | | , rng(std::move(in_rng)) |
35 | 3.39k | { |
36 | 3.39k | } |
37 | 0 | System::System(System &&) = default; |
38 | | |
39 | 3.39k | System::~System() { } |
40 | | |
41 | | static int recv_common(Fuzz_Data &input, uint8_t *buf, size_t buf_len) |
42 | 18.9k | { |
43 | 18.9k | if (input.size() < 2) { |
44 | 568 | errno = ENOMEM; |
45 | 568 | return -1; |
46 | 568 | } |
47 | | |
48 | 36.6k | CONSUME_OR_ABORT(const uint8_t *fuzz_len_bytes, input, 2); |
49 | 36.6k | const std::size_t fuzz_len = (fuzz_len_bytes[0] << 8) | fuzz_len_bytes[1]; |
50 | | |
51 | 36.6k | if (fuzz_len == 0xffff) { |
52 | 9.13k | errno = EWOULDBLOCK; |
53 | 9.13k | if (Fuzz_Data::DEBUG) { |
54 | 0 | std::printf("recvfrom: no data for tox1\n"); |
55 | 0 | } |
56 | 9.13k | return -1; |
57 | 9.13k | } |
58 | | |
59 | 9.20k | if (Fuzz_Data::DEBUG) { |
60 | 0 | std::printf( |
61 | 0 | "recvfrom: %zu (%02x, %02x) for tox1\n", fuzz_len, input.data()[-2], input.data()[-1]); |
62 | 0 | } |
63 | 9.20k | const size_t res = std::min(buf_len, std::min(fuzz_len, input.size())); |
64 | | |
65 | 9.20k | CONSUME_OR_ABORT(const uint8_t *data, input, res); |
66 | 9.20k | std::copy(data, data + res, buf); |
67 | | |
68 | 9.20k | return res; |
69 | 9.20k | } |
70 | | |
71 | | static void *report_alloc(const char *name, const char *func, std::size_t size, void *ptr) |
72 | 44.2k | { |
73 | 44.2k | if (Fuzz_Data::DEBUG) { |
74 | 0 | printf("%s: %s(%zu): %s\n", name, func, size, ptr == nullptr ? "false" : "true"); |
75 | 0 | } |
76 | 44.2k | return ptr; |
77 | 44.2k | } |
78 | | |
79 | | template <typename F, F Func, typename... Args> |
80 | | static void *alloc_common(const char *func, std::size_t size, Fuzz_Data &data, Args... args) |
81 | 48.8k | { |
82 | 48.8k | CONSUME1_OR_RETURN_VAL( |
83 | 46.0k | const bool, want_alloc, data, report_alloc("tox1", func, size, Func(args...))); |
84 | 46.0k | if (!want_alloc) { |
85 | 4.60k | return nullptr; |
86 | 4.60k | } |
87 | 41.4k | return report_alloc("tox1", func, size, Func(args...)); |
88 | 46.0k | } unity_0_cxx.cxx:_ZL12alloc_commonIDoFPvmEXadL_Z6mallocEEJjEES0_PKcmR9Fuzz_DataDpT1_ Line | Count | Source | 81 | 1.43k | { | 82 | 1.43k | CONSUME1_OR_RETURN_VAL( | 83 | 1.31k | const bool, want_alloc, data, report_alloc("tox1", func, size, Func(args...))); | 84 | 1.31k | if (!want_alloc) { | 85 | 336 | return nullptr; | 86 | 336 | } | 87 | 981 | return report_alloc("tox1", func, size, Func(args...)); | 88 | 1.31k | } |
unity_0_cxx.cxx:_ZL12alloc_commonIDoFPvmmEXadL_Z6callocEEJjjEES0_PKcmR9Fuzz_DataDpT1_ Line | Count | Source | 81 | 38.0k | { | 82 | 38.0k | CONSUME1_OR_RETURN_VAL( | 83 | 36.1k | const bool, want_alloc, data, report_alloc("tox1", func, size, Func(args...))); | 84 | 36.1k | if (!want_alloc) { | 85 | 1.19k | return nullptr; | 86 | 1.19k | } | 87 | 34.9k | return report_alloc("tox1", func, size, Func(args...)); | 88 | 36.1k | } |
unity_0_cxx.cxx:_ZL12alloc_commonIDoFPvS0_mEXadL_Z7reallocEEJS0_jEES0_PKcmR9Fuzz_DataDpT1_ Line | Count | Source | 81 | 9.40k | { | 82 | 9.40k | CONSUME1_OR_RETURN_VAL( | 83 | 8.53k | const bool, want_alloc, data, report_alloc("tox1", func, size, Func(args...))); | 84 | 8.53k | if (!want_alloc) { | 85 | 3.07k | return nullptr; | 86 | 3.07k | } | 87 | 5.45k | return report_alloc("tox1", func, size, Func(args...)); | 88 | 8.53k | } |
|
89 | | |
90 | | static constexpr Memory_Funcs fuzz_memory_funcs = { |
91 | | /* .malloc = */ |
92 | 1.43k |  { |
93 | 1.43k | return alloc_common<decltype(std::malloc), std::malloc>("malloc", size, self->data, size); |
94 | 1.43k | }, |
95 | | /* .calloc = */ |
96 | 38.0k |  { |
97 | 38.0k | return alloc_common<decltype(std::calloc), std::calloc>( |
98 | 38.0k | "calloc", nmemb * size, self->data, nmemb, size); |
99 | 38.0k | }, |
100 | | /* .realloc = */ |
101 | 9.40k |  { |
102 | 9.40k | return alloc_common<decltype(std::realloc), std::realloc>( |
103 | 9.40k | "realloc", size, self->data, ptr, size); |
104 | 9.40k | }, |
105 | | /* .free = */ |
106 | 45.1k |  { std::free(ptr); }, |
107 | | }; |
108 | | |
109 | | static constexpr Network_Funcs fuzz_network_funcs = { |
110 | 1.27k | /* .close = */  { return 0; }, |
111 | 2.81k | /* .accept = */  { return 1337; }, |
112 | 651 | /* .bind = */  { return 0; }, |
113 | 16 | /* .listen = */  { return 0; }, |
114 | | /* .recvbuf = */ |
115 | 7.22k |  { |
116 | 7.22k | assert(sock == 42 || sock == 1337); |
117 | 7.22k | const size_t count = random_u16(self->rng.get()); |
118 | 7.22k | return static_cast<int>(std::min(count, self->data.size())); |
119 | 7.22k | }, |
120 | | /* .recv = */ |
121 | 4.05k |  { |
122 | 4.05k | assert(sock == 42 || sock == 1337); |
123 | | // Receive data from the fuzzer. |
124 | 4.05k | return recv_common(self->data, buf, len); |
125 | 4.05k | }, |
126 | | /* .recvfrom = */ |
127 | 14.8k |  { |
128 | 14.8k | assert(sock == 42 || sock == 1337); |
129 | | |
130 | 14.8k | addr->addr = sockaddr_storage{}; |
131 | | // Dummy Addr |
132 | 14.8k | addr->addr.ss_family = AF_INET; |
133 | | |
134 | | // We want an AF_INET address with dummy values |
135 | 14.8k | sockaddr_in *addr_in = reinterpret_cast<sockaddr_in *>(&addr->addr); |
136 | 14.8k | addr_in->sin_port = htons(33446); |
137 | 14.8k | addr_in->sin_addr.s_addr = htonl(0x7f000002); // 127.0.0.2 |
138 | 14.8k | addr->size = sizeof(struct sockaddr); |
139 | | |
140 | 14.8k | return recv_common(self->data, buf, len); |
141 | 14.8k | }, |
142 | | /* .send = */ |
143 | 690 |  { |
144 | 690 | assert(sock == 42 || sock == 1337); |
145 | | // Always succeed. |
146 | 690 | return static_cast<int>(len); |
147 | 690 | }, |
148 | | /* .sendto = */ |
149 | 15.2k |  { |
150 | 15.2k | assert(sock == 42 || sock == 1337); |
151 | | // Always succeed. |
152 | 15.2k | return static_cast<int>(len); |
153 | 15.2k | }, |
154 | 1.01k | /* .socket = */  { return 42; }, |
155 | 3.83k | /* .socket_nonblock = */  { return 0; }, |
156 | | /* .getsockopt = */ |
157 | 632 |  { |
158 | 632 | std::memset(optval, 0, *optlen); |
159 | 632 | return 0; |
160 | 632 | }, |
161 | | /* .setsockopt = */ |
162 | 2.53k |  { |
163 | 2.53k | return 0; |
164 | 2.53k | }, |
165 | | }; |
166 | | |
167 | | static constexpr Random_Funcs fuzz_random_funcs = { |
168 | | /* .random_bytes = */ |
169 | 33.4k |  { |
170 | | // Amount of data is limited |
171 | 33.4k | const size_t bytes_read = std::min(length, self->data.size()); |
172 | | // Initialize everything to make MSAN and others happy |
173 | 33.4k | std::memset(bytes, 0, length); |
174 | 33.4k | CONSUME_OR_ABORT(const uint8_t *data, self->data, bytes_read); |
175 | 33.4k | std::copy(data, data + bytes_read, bytes); |
176 | 33.4k | if (Fuzz_Data::DEBUG) { |
177 | 0 | if (length == 1) { |
178 | 0 | std::printf("rng: %d (0x%02x)\n", bytes[0], bytes[0]); |
179 | 0 | } else { |
180 | 0 | std::printf("rng: %02x..%02x[%zu]\n", bytes[0], bytes[length - 1], length); |
181 | 0 | } |
182 | 0 | } |
183 | 33.4k | }, |
184 | | /* .random_uniform = */ |
185 | 0 |  { |
186 | 0 | uint32_t randnum = 0; |
187 | 0 | if (upper_bound > 0) { |
188 | 0 | self->rng->funcs->random_bytes( |
189 | 0 | self, reinterpret_cast<uint8_t *>(&randnum), sizeof(randnum)); |
190 | 0 | randnum %= upper_bound; |
191 | 0 | } |
192 | 0 | return randnum; |
193 | 0 | }, |
194 | | }; |
195 | | |
196 | | Fuzz_System::Fuzz_System(Fuzz_Data &input) |
197 | | : System{ |
198 | | std::make_unique<Tox_System>(), |
199 | | std::make_unique<Memory>(Memory{&fuzz_memory_funcs, this}), |
200 | | std::make_unique<Network>(Network{&fuzz_network_funcs, this}), |
201 | | std::make_unique<Random>(Random{&fuzz_random_funcs, this}), |
202 | | } |
203 | | , data(input) |
204 | 1.43k | { |
205 | 127k | sys->mono_time_callback = [](void *self) { return static_cast<Fuzz_System *>(self)->clock; }; |
206 | 1.43k | sys->mono_time_user_data = this; |
207 | 1.43k | sys->mem = mem.get(); |
208 | 1.43k | sys->ns = ns.get(); |
209 | 1.43k | sys->rng = rng.get(); |
210 | 1.43k | } |
211 | | |
212 | | static constexpr Memory_Funcs null_memory_funcs = { |
213 | | /* .malloc = */ |
214 | 126k |  { return std::malloc(size); }, |
215 | | /* .calloc = */ |
216 | 38.4k |  { return std::calloc(nmemb, size); }, |
217 | | /* .realloc = */ |
218 | 13.2k |  { return std::realloc(ptr, size); }, |
219 | | /* .free = */ |
220 | 174k |  { std::free(ptr); }, |
221 | | }; |
222 | | |
223 | | static constexpr Network_Funcs null_network_funcs = { |
224 | 1.15k | /* .close = */  { return 0; }, |
225 | 0 | /* .accept = */  { return 1337; }, |
226 | 1.15k | /* .bind = */  { return 0; }, |
227 | 0 | /* .listen = */  { return 0; }, |
228 | 0 | /* .recvbuf = */  { return 0; }, |
229 | | /* .recv = */ |
230 | 0 |  { |
231 | | // Always fail. |
232 | 0 | errno = ENOMEM; |
233 | 0 | return -1; |
234 | 0 | }, |
235 | | /* .recvfrom = */ |
236 | 0 |  { |
237 | | // Always fail. |
238 | 0 | errno = ENOMEM; |
239 | 0 | return -1; |
240 | 0 | }, |
241 | | /* .send = */ |
242 | 0 |  { |
243 | | // Always succeed. |
244 | 0 | return static_cast<int>(len); |
245 | 0 | }, |
246 | | /* .sendto = */ |
247 | 0 |  { |
248 | | // Always succeed. |
249 | 0 | return static_cast<int>(len); |
250 | 0 | }, |
251 | 1.15k | /* .socket = */  { return 42; }, |
252 | 1.15k | /* .socket_nonblock = */  { return 0; }, |
253 | | /* .getsockopt = */ |
254 | 1.15k |  { |
255 | 1.15k | std::memset(optval, 0, *optlen); |
256 | 1.15k | return 0; |
257 | 1.15k | }, |
258 | | /* .setsockopt = */ |
259 | 4.63k |  { |
260 | 4.63k | return 0; |
261 | 4.63k | }, |
262 | | }; |
263 | | |
264 | | static uint64_t simple_rng(uint64_t &seed) |
265 | 453k | { |
266 | | // https://nuclear.llnl.gov/CNP/rng/rngman/node4.html |
267 | 453k | seed = 2862933555777941757LL * seed + 3037000493LL; |
268 | 453k | return seed; |
269 | 453k | } |
270 | | |
271 | | static constexpr Random_Funcs null_random_funcs = { |
272 | | /* .random_bytes = */ |
273 | 17.6k |  { |
274 | 471k | for (size_t i = 0; i < length; ++i) { |
275 | 453k | bytes[i] = simple_rng(self->seed) & 0xff; |
276 | 453k | } |
277 | 17.6k | }, |
278 | | /* .random_uniform = */ |
279 | 0 |  { |
280 | 0 | return static_cast<uint32_t>(simple_rng(self->seed)) % upper_bound; |
281 | 0 | }, |
282 | | }; |
283 | | |
284 | | Null_System::Null_System() |
285 | | : System{ |
286 | | std::make_unique<Tox_System>(), |
287 | | std::make_unique<Memory>(Memory{&null_memory_funcs, this}), |
288 | | std::make_unique<Network>(Network{&null_network_funcs, this}), |
289 | | std::make_unique<Random>(Random{&null_random_funcs, this}), |
290 | | } |
291 | 1.96k | { |
292 | 1.96k | sys->mono_time_callback = [](void *self) { return static_cast<Null_System *>(self)->clock; }; |
293 | 1.96k | sys->mono_time_user_data = this; |
294 | 1.96k | sys->mem = mem.get(); |
295 | 1.96k | sys->ns = ns.get(); |
296 | 1.96k | sys->rng = rng.get(); |
297 | 1.96k | } |
298 | | |
299 | | static uint16_t get_port(const Network_Addr *addr) |
300 | 0 | { |
301 | 0 | if (addr->addr.ss_family == AF_INET6) { |
302 | 0 | return reinterpret_cast<const sockaddr_in6 *>(&addr->addr)->sin6_port; |
303 | 0 | } else { |
304 | 0 | assert(addr->addr.ss_family == AF_INET); |
305 | 0 | return reinterpret_cast<const sockaddr_in *>(&addr->addr)->sin_port; |
306 | 0 | } |
307 | 0 | } |
308 | | |
309 | | static constexpr Memory_Funcs record_memory_funcs = { |
310 | | /* .malloc = */ |
311 | 0 |  { |
312 | 0 | self->push(true); |
313 | 0 | return report_alloc(self->name_, "malloc", size, std::malloc(size)); |
314 | 0 | }, |
315 | | /* .calloc = */ |
316 | 0 |  { |
317 | 0 | self->push(true); |
318 | 0 | return report_alloc(self->name_, "calloc", nmemb * size, std::calloc(nmemb, size)); |
319 | 0 | }, |
320 | | /* .realloc = */ |
321 | 0 |  { |
322 | 0 | self->push(true); |
323 | 0 | return report_alloc(self->name_, "realloc", size, std::realloc(ptr, size)); |
324 | 0 | }, |
325 | | /* .free = */ |
326 | 0 |  { std::free(ptr); }, |
327 | | }; |
328 | | |
329 | | static constexpr Network_Funcs record_network_funcs = { |
330 | 0 | /* .close = */  { return 0; }, |
331 | 0 | /* .accept = */  { return 2; }, |
332 | | /* .bind = */ |
333 | 0 |  { |
334 | 0 | const uint16_t port = get_port(addr); |
335 | 0 | if (self->global_.bound.find(port) != self->global_.bound.end()) { |
336 | 0 | errno = EADDRINUSE; |
337 | 0 | return -1; |
338 | 0 | } |
339 | 0 | self->global_.bound.emplace(port, self); |
340 | 0 | self->port = port; |
341 | 0 | return 0; |
342 | 0 | }, |
343 | 0 | /* .listen = */  { return 0; }, |
344 | 0 | /* .recvbuf = */  { return 0; }, |
345 | | /* .recv = */ |
346 | 0 |  { |
347 | | // Always fail. |
348 | 0 | errno = ENOMEM; |
349 | 0 | return -1; |
350 | 0 | }, |
351 | | /* .recvfrom = */ |
352 | 0 |  { |
353 | 0 | assert(sock == 42); |
354 | 0 | if (self->recvq.empty()) { |
355 | 0 | self->push("\xff\xff"); |
356 | 0 | errno = EWOULDBLOCK; |
357 | 0 | if (Fuzz_Data::DEBUG) { |
358 | 0 | std::printf("%s: recvfrom: no data\n", self->name_); |
359 | 0 | } |
360 | 0 | return -1; |
361 | 0 | } |
362 | 0 | const auto [from, packet] = std::move(self->recvq.front()); |
363 | 0 | self->recvq.pop_front(); |
364 | 0 | const size_t recvlen = std::min(len, packet.size()); |
365 | 0 | std::copy(packet.begin(), packet.end(), buf); |
366 | |
|
367 | 0 | addr->addr = sockaddr_storage{}; |
368 | | // Dummy Addr |
369 | 0 | addr->addr.ss_family = AF_INET; |
370 | | |
371 | | // We want an AF_INET address with dummy values |
372 | 0 | sockaddr_in *addr_in = reinterpret_cast<sockaddr_in *>(&addr->addr); |
373 | 0 | addr_in->sin_port = from; |
374 | 0 | addr_in->sin_addr.s_addr = htonl(0x7f000002); // 127.0.0.2 |
375 | 0 | addr->size = sizeof(struct sockaddr); |
376 | |
|
377 | 0 | assert(recvlen > 0 && recvlen <= INT_MAX); |
378 | 0 | self->push(uint8_t(recvlen >> 8)); |
379 | 0 | self->push(uint8_t(recvlen & 0xff)); |
380 | 0 | if (Fuzz_Data::DEBUG) { |
381 | 0 | std::printf("%s: recvfrom: %zu (%02x, %02x)\n", self->name_, recvlen, |
382 | 0 | self->recording().end()[-2], self->recording().end()[-1]); |
383 | 0 | } |
384 | 0 | self->push(buf, recvlen); |
385 | 0 | return static_cast<int>(recvlen); |
386 | 0 | }, |
387 | | /* .send = */ |
388 | 0 |  { |
389 | | // Always succeed. |
390 | 0 | return static_cast<int>(len); |
391 | 0 | }, |
392 | | /* .sendto = */ |
393 | 0 |  { |
394 | 0 | assert(sock == 42); |
395 | 0 | auto backend = self->global_.bound.find(get_port(addr)); |
396 | 0 | assert(backend != self->global_.bound.end()); |
397 | 0 | backend->second->receive(self->port, buf, len); |
398 | 0 | return static_cast<int>(len); |
399 | 0 | }, |
400 | 0 | /* .socket = */  { return 42; }, |
401 | 0 | /* .socket_nonblock = */  { return 0; }, |
402 | | /* .getsockopt = */ |
403 | 0 |  { |
404 | 0 | std::memset(optval, 0, *optlen); |
405 | 0 | return 0; |
406 | 0 | }, |
407 | | /* .setsockopt = */ |
408 | 0 |  { |
409 | 0 | return 0; |
410 | 0 | }, |
411 | | }; |
412 | | |
413 | | static constexpr Random_Funcs record_random_funcs = { |
414 | | /* .random_bytes = */ |
415 | 0 |  { |
416 | 0 | for (size_t i = 0; i < length; ++i) { |
417 | 0 | bytes[i] = simple_rng(self->seed_) & 0xff; |
418 | 0 | self->push(bytes[i]); |
419 | 0 | } |
420 | 0 | if (Fuzz_Data::DEBUG) { |
421 | 0 | std::printf( |
422 | 0 | "%s: rng: %02x..%02x[%zu]\n", self->name_, bytes[0], bytes[length - 1], length); |
423 | 0 | } |
424 | 0 | }, |
425 | | /* .random_uniform = */ |
426 | | fuzz_random_funcs.random_uniform, |
427 | | }; |
428 | | |
429 | | Record_System::Record_System(Global &global, uint64_t seed, const char *name) |
430 | | : System{ |
431 | | std::make_unique<Tox_System>(), |
432 | | std::make_unique<Memory>(Memory{&record_memory_funcs, this}), |
433 | | std::make_unique<Network>(Network{&record_network_funcs, this}), |
434 | | std::make_unique<Random>(Random{&record_random_funcs, this}), |
435 | | } |
436 | | , global_(global) |
437 | | , seed_(seed) |
438 | | , name_(name) |
439 | 0 | { |
440 | 0 | sys->mono_time_callback = [](void *self) { return static_cast<Record_System *>(self)->clock; }; |
441 | 0 | sys->mono_time_user_data = this; |
442 | 0 | sys->mem = mem.get(); |
443 | 0 | sys->ns = ns.get(); |
444 | 0 | sys->rng = rng.get(); |
445 | 0 | } |
446 | | |
447 | | void Record_System::receive(uint16_t send_port, const uint8_t *buf, size_t len) |
448 | 0 | { |
449 | 0 | assert(port != 0); |
450 | 0 | recvq.emplace_back(send_port, std::vector<uint8_t>{buf, buf + len}); |
451 | 0 | } |