1 /* $OpenBSD: fuzz.c,v 1.9 2025/06/13 07:23:07 dtucker Exp $ */
2 /*
3 * Copyright (c) 2011 Damien Miller <djm@mindrot.org>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18 /* Utility functions/framework for fuzz tests */
19
20 #include "includes.h"
21
22 #include <sys/types.h>
23 #include <sys/uio.h>
24
25 #include <assert.h>
26 #include <ctype.h>
27 #include <stdio.h>
28 #include <stdint.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <signal.h>
32 #include <unistd.h>
33
34 #include "test_helper.h"
35 #include "atomicio.h"
36
37 /* #define FUZZ_DEBUG */
38
39 #ifdef FUZZ_DEBUG
40 # define FUZZ_DBG(x) do { \
41 printf("%s:%d %s: ", __FILE__, __LINE__, __func__); \
42 printf x; \
43 printf("\n"); \
44 fflush(stdout); \
45 } while (0)
46 #else
47 # define FUZZ_DBG(x)
48 #endif
49
50 /* For brevity later */
51 typedef unsigned long long fuzz_ullong;
52
53 /* For base-64 fuzzing */
54 static const char fuzz_b64chars[] =
55 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
56
57 struct fuzz {
58 /* Fuzz method currently in use */
59 int strategy;
60
61 /* Fuzz methods remaining */
62 int strategies;
63
64 /* Original seed data blob */
65 void *seed;
66 size_t slen;
67
68 /* Current working copy of seed with fuzz mutations applied */
69 u_char *fuzzed;
70
71 /* Used by fuzz methods */
72 size_t o1, o2;
73 };
74
75 static const char *
fuzz_ntop(u_int n)76 fuzz_ntop(u_int n)
77 {
78 switch (n) {
79 case 0:
80 return "NONE";
81 case FUZZ_1_BIT_FLIP:
82 return "FUZZ_1_BIT_FLIP";
83 case FUZZ_2_BIT_FLIP:
84 return "FUZZ_2_BIT_FLIP";
85 case FUZZ_1_BYTE_FLIP:
86 return "FUZZ_1_BYTE_FLIP";
87 case FUZZ_2_BYTE_FLIP:
88 return "FUZZ_2_BYTE_FLIP";
89 case FUZZ_TRUNCATE_START:
90 return "FUZZ_TRUNCATE_START";
91 case FUZZ_TRUNCATE_END:
92 return "FUZZ_TRUNCATE_END";
93 case FUZZ_BASE64:
94 return "FUZZ_BASE64";
95 default:
96 abort();
97 }
98 }
99
100 static int
fuzz_fmt(struct fuzz * fuzz,char * s,size_t n)101 fuzz_fmt(struct fuzz *fuzz, char *s, size_t n)
102 {
103 if (fuzz == NULL)
104 return -1;
105
106 switch (fuzz->strategy) {
107 case FUZZ_1_BIT_FLIP:
108 snprintf(s, n, "%s case %zu of %zu (bit: %zu)\n",
109 fuzz_ntop(fuzz->strategy),
110 fuzz->o1, fuzz->slen * 8, fuzz->o1);
111 return 0;
112 case FUZZ_2_BIT_FLIP:
113 snprintf(s, n, "%s case %llu of %llu (bits: %zu, %zu)\n",
114 fuzz_ntop(fuzz->strategy),
115 (((fuzz_ullong)fuzz->o2) * fuzz->slen * 8) + fuzz->o1,
116 ((fuzz_ullong)fuzz->slen * 8) * fuzz->slen * 8,
117 fuzz->o1, fuzz->o2);
118 return 0;
119 case FUZZ_1_BYTE_FLIP:
120 snprintf(s, n, "%s case %zu of %zu (byte: %zu)\n",
121 fuzz_ntop(fuzz->strategy),
122 fuzz->o1, fuzz->slen, fuzz->o1);
123 return 0;
124 case FUZZ_2_BYTE_FLIP:
125 snprintf(s, n, "%s case %llu of %llu (bytes: %zu, %zu)\n",
126 fuzz_ntop(fuzz->strategy),
127 (((fuzz_ullong)fuzz->o2) * fuzz->slen) + fuzz->o1,
128 ((fuzz_ullong)fuzz->slen) * fuzz->slen,
129 fuzz->o1, fuzz->o2);
130 return 0;
131 case FUZZ_TRUNCATE_START:
132 snprintf(s, n, "%s case %zu of %zu (offset: %zu)\n",
133 fuzz_ntop(fuzz->strategy),
134 fuzz->o1, fuzz->slen, fuzz->o1);
135 return 0;
136 case FUZZ_TRUNCATE_END:
137 snprintf(s, n, "%s case %zu of %zu (offset: %zu)\n",
138 fuzz_ntop(fuzz->strategy),
139 fuzz->o1, fuzz->slen, fuzz->o1);
140 return 0;
141 case FUZZ_BASE64:
142 assert(fuzz->o2 < sizeof(fuzz_b64chars) - 1);
143 snprintf(s, n, "%s case %llu of %llu (offset: %zu char: %c)\n",
144 fuzz_ntop(fuzz->strategy),
145 (fuzz->o1 * (fuzz_ullong)64) + fuzz->o2,
146 fuzz->slen * (fuzz_ullong)64, fuzz->o1,
147 fuzz_b64chars[fuzz->o2]);
148 return 0;
149 default:
150 return -1;
151 }
152 }
153
154 static void
dump(u_char * p,size_t len)155 dump(u_char *p, size_t len)
156 {
157 size_t i, j;
158
159 for (i = 0; i < len; i += 16) {
160 fprintf(stderr, "%.4zd: ", i);
161 for (j = i; j < i + 16; j++) {
162 if (j < len)
163 fprintf(stderr, "%02x ", p[j]);
164 else
165 fprintf(stderr, " ");
166 }
167 fprintf(stderr, " ");
168 for (j = i; j < i + 16; j++) {
169 if (j < len) {
170 if (isascii(p[j]) && isprint(p[j]))
171 fprintf(stderr, "%c", p[j]);
172 else
173 fprintf(stderr, ".");
174 }
175 }
176 fprintf(stderr, "\n");
177 }
178 }
179
180 void
fuzz_dump(struct fuzz * fuzz)181 fuzz_dump(struct fuzz *fuzz)
182 {
183 char buf[256];
184
185 if (fuzz_fmt(fuzz, buf, sizeof(buf)) != 0) {
186 fprintf(stderr, "%s: fuzz invalid\n", __func__);
187 abort();
188 }
189 fputs(buf, stderr);
190 fprintf(stderr, "fuzz original %p len = %zu\n", fuzz->seed, fuzz->slen);
191 dump(fuzz->seed, fuzz->slen);
192 fprintf(stderr, "fuzz context %p len = %zu\n", fuzz, fuzz_len(fuzz));
193 dump(fuzz_ptr(fuzz), fuzz_len(fuzz));
194 }
195
196 static struct fuzz *last_fuzz;
197
198 static void
siginfo(int unused)199 siginfo(int unused __attribute__((__unused__)))
200 {
201 char buf[256];
202
203 test_info(buf, sizeof(buf));
204 atomicio(vwrite, STDERR_FILENO, buf, strlen(buf));
205 if (last_fuzz != NULL) {
206 fuzz_fmt(last_fuzz, buf, sizeof(buf));
207 atomicio(vwrite, STDERR_FILENO, buf, strlen(buf));
208 }
209 }
210
211 struct fuzz *
fuzz_begin(u_int strategies,const void * p,size_t l)212 fuzz_begin(u_int strategies, const void *p, size_t l)
213 {
214 struct fuzz *ret = calloc(1, sizeof(*ret));
215
216 assert(p != NULL);
217 assert(ret != NULL);
218 ret->seed = malloc(l);
219 assert(ret->seed != NULL);
220 memcpy(ret->seed, p, l);
221 ret->slen = l;
222 ret->strategies = strategies;
223
224 assert(ret->slen < SIZE_MAX / 8);
225 assert(ret->strategies <= (FUZZ_MAX|(FUZZ_MAX-1)));
226
227 FUZZ_DBG(("begin, ret = %p", ret));
228
229 fuzz_next(ret);
230
231 last_fuzz = ret;
232 #ifdef SIGINFO
233 signal(SIGINFO, siginfo);
234 #endif
235 signal(SIGUSR1, siginfo);
236
237 return ret;
238 }
239
240 void
fuzz_cleanup(struct fuzz * fuzz)241 fuzz_cleanup(struct fuzz *fuzz)
242 {
243 FUZZ_DBG(("cleanup, fuzz = %p", fuzz));
244 last_fuzz = NULL;
245 #ifdef SIGINFO
246 signal(SIGINFO, SIG_DFL);
247 #endif
248 signal(SIGUSR1, SIG_DFL);
249 assert(fuzz != NULL);
250 assert(fuzz->seed != NULL);
251 assert(fuzz->fuzzed != NULL);
252 free(fuzz->seed);
253 free(fuzz->fuzzed);
254 free(fuzz);
255 }
256
257 static int
fuzz_strategy_done(struct fuzz * fuzz)258 fuzz_strategy_done(struct fuzz *fuzz)
259 {
260 FUZZ_DBG(("fuzz = %p, strategy = %s, o1 = %zu, o2 = %zu, slen = %zu",
261 fuzz, fuzz_ntop(fuzz->strategy), fuzz->o1, fuzz->o2, fuzz->slen));
262
263 switch (fuzz->strategy) {
264 case FUZZ_1_BIT_FLIP:
265 return fuzz->o1 >= fuzz->slen * 8;
266 case FUZZ_2_BIT_FLIP:
267 return fuzz->o2 >= fuzz->slen * 8;
268 case FUZZ_2_BYTE_FLIP:
269 return fuzz->o2 >= fuzz->slen;
270 case FUZZ_1_BYTE_FLIP:
271 case FUZZ_TRUNCATE_START:
272 case FUZZ_TRUNCATE_END:
273 case FUZZ_BASE64:
274 return fuzz->o1 >= fuzz->slen;
275 default:
276 abort();
277 }
278 }
279
280 void
fuzz_next(struct fuzz * fuzz)281 fuzz_next(struct fuzz *fuzz)
282 {
283 u_int i;
284
285 FUZZ_DBG(("start, fuzz = %p, strategy = %s, strategies = 0x%lx, "
286 "o1 = %zu, o2 = %zu, slen = %zu", fuzz, fuzz_ntop(fuzz->strategy),
287 (u_long)fuzz->strategies, fuzz->o1, fuzz->o2, fuzz->slen));
288
289 if (fuzz->strategy == 0 || fuzz_strategy_done(fuzz)) {
290 /* If we are just starting out, we need to allocate too */
291 if (fuzz->fuzzed == NULL) {
292 FUZZ_DBG(("alloc"));
293 fuzz->fuzzed = calloc(fuzz->slen, 1);
294 }
295 /* Pick next strategy */
296 FUZZ_DBG(("advance"));
297 for (i = 1; i <= FUZZ_MAX; i <<= 1) {
298 if ((fuzz->strategies & i) != 0) {
299 fuzz->strategy = i;
300 break;
301 }
302 }
303 FUZZ_DBG(("selected = %u", fuzz->strategy));
304 if (fuzz->strategy == 0) {
305 FUZZ_DBG(("done, no more strategies"));
306 return;
307 }
308 fuzz->strategies &= ~(fuzz->strategy);
309 fuzz->o1 = fuzz->o2 = 0;
310 }
311
312 assert(fuzz->fuzzed != NULL);
313
314 switch (fuzz->strategy) {
315 case FUZZ_1_BIT_FLIP:
316 assert(fuzz->o1 / 8 < fuzz->slen);
317 memcpy(fuzz->fuzzed, fuzz->seed, fuzz->slen);
318 fuzz->fuzzed[fuzz->o1 / 8] ^= 1 << (fuzz->o1 % 8);
319 fuzz->o1++;
320 break;
321 case FUZZ_2_BIT_FLIP:
322 assert(fuzz->o1 / 8 < fuzz->slen);
323 assert(fuzz->o2 / 8 < fuzz->slen);
324 memcpy(fuzz->fuzzed, fuzz->seed, fuzz->slen);
325 fuzz->fuzzed[fuzz->o1 / 8] ^= 1 << (fuzz->o1 % 8);
326 fuzz->fuzzed[fuzz->o2 / 8] ^= 1 << (fuzz->o2 % 8);
327 fuzz->o1++;
328 if (fuzz->o1 >= fuzz->slen * 8) {
329 fuzz->o1 = 0;
330 fuzz->o2++;
331 }
332 break;
333 case FUZZ_1_BYTE_FLIP:
334 assert(fuzz->o1 < fuzz->slen);
335 memcpy(fuzz->fuzzed, fuzz->seed, fuzz->slen);
336 fuzz->fuzzed[fuzz->o1] ^= 0xff;
337 fuzz->o1++;
338 break;
339 case FUZZ_2_BYTE_FLIP:
340 assert(fuzz->o1 < fuzz->slen);
341 assert(fuzz->o2 < fuzz->slen);
342 memcpy(fuzz->fuzzed, fuzz->seed, fuzz->slen);
343 fuzz->fuzzed[fuzz->o1] ^= 0xff;
344 fuzz->fuzzed[fuzz->o2] ^= 0xff;
345 fuzz->o1++;
346 if (fuzz->o1 >= fuzz->slen) {
347 fuzz->o1 = 0;
348 fuzz->o2++;
349 }
350 break;
351 case FUZZ_TRUNCATE_START:
352 case FUZZ_TRUNCATE_END:
353 assert(fuzz->o1 < fuzz->slen);
354 memcpy(fuzz->fuzzed, fuzz->seed, fuzz->slen);
355 fuzz->o1++;
356 break;
357 case FUZZ_BASE64:
358 assert(fuzz->o1 < fuzz->slen);
359 assert(fuzz->o2 < sizeof(fuzz_b64chars) - 1);
360 memcpy(fuzz->fuzzed, fuzz->seed, fuzz->slen);
361 fuzz->fuzzed[fuzz->o1] = fuzz_b64chars[fuzz->o2];
362 fuzz->o2++;
363 if (fuzz->o2 >= sizeof(fuzz_b64chars) - 1) {
364 fuzz->o2 = 0;
365 fuzz->o1++;
366 }
367 break;
368 default:
369 abort();
370 }
371
372 FUZZ_DBG(("done, fuzz = %p, strategy = %s, strategies = 0x%lx, "
373 "o1 = %zu, o2 = %zu, slen = %zu", fuzz, fuzz_ntop(fuzz->strategy),
374 (u_long)fuzz->strategies, fuzz->o1, fuzz->o2, fuzz->slen));
375 }
376
377 int
fuzz_matches_original(struct fuzz * fuzz)378 fuzz_matches_original(struct fuzz *fuzz)
379 {
380 if (fuzz_len(fuzz) != fuzz->slen)
381 return 0;
382 return memcmp(fuzz_ptr(fuzz), fuzz->seed, fuzz->slen) == 0;
383 }
384
385 int
fuzz_done(struct fuzz * fuzz)386 fuzz_done(struct fuzz *fuzz)
387 {
388 FUZZ_DBG(("fuzz = %p, strategies = 0x%lx", fuzz,
389 (u_long)fuzz->strategies));
390
391 return fuzz_strategy_done(fuzz) && fuzz->strategies == 0;
392 }
393
394 size_t
fuzz_len(struct fuzz * fuzz)395 fuzz_len(struct fuzz *fuzz)
396 {
397 assert(fuzz->fuzzed != NULL);
398 switch (fuzz->strategy) {
399 case FUZZ_1_BIT_FLIP:
400 case FUZZ_2_BIT_FLIP:
401 case FUZZ_1_BYTE_FLIP:
402 case FUZZ_2_BYTE_FLIP:
403 case FUZZ_BASE64:
404 return fuzz->slen;
405 case FUZZ_TRUNCATE_START:
406 case FUZZ_TRUNCATE_END:
407 assert(fuzz->o1 <= fuzz->slen);
408 return fuzz->slen - fuzz->o1;
409 default:
410 abort();
411 }
412 }
413
414 u_char *
fuzz_ptr(struct fuzz * fuzz)415 fuzz_ptr(struct fuzz *fuzz)
416 {
417 assert(fuzz->fuzzed != NULL);
418 switch (fuzz->strategy) {
419 case FUZZ_1_BIT_FLIP:
420 case FUZZ_2_BIT_FLIP:
421 case FUZZ_1_BYTE_FLIP:
422 case FUZZ_2_BYTE_FLIP:
423 case FUZZ_BASE64:
424 return fuzz->fuzzed;
425 case FUZZ_TRUNCATE_START:
426 assert(fuzz->o1 <= fuzz->slen);
427 return fuzz->fuzzed + fuzz->o1;
428 case FUZZ_TRUNCATE_END:
429 assert(fuzz->o1 <= fuzz->slen);
430 return fuzz->fuzzed;
431 default:
432 abort();
433 }
434 }
435
436