fuzz_common.h (25290B)
1 /* 2 This file is part of libmicrohttpd 3 Copyright (C) 2026 Christian Grothoff 4 5 This library is free software; you can redistribute it and/or 6 modify it under the terms of the GNU Lesser General Public 7 License as published by the Free Software Foundation; either 8 version 2.1 of the License, or (at your option) any later version. 9 10 This library is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 Lesser General Public License for more details. 14 15 You should have received a copy of the GNU Lesser General Public 16 License along with this library. 17 If not, see <http://www.gnu.org/licenses/>. 18 */ 19 20 /** 21 * @file fuzz/fuzz_common.h 22 * @brief Shared, header-only fuzzing driver for the MHD in-process fuzzers. 23 * @author Christian Grothoff 24 * 25 * Every harness in this directory is a single translation unit that 26 * includes this header. The header provides: 27 * - a deterministic, seeded PRNG (splitmix64 / xoshiro256**), 28 * - a generic byte-level mutator, 29 * - crash bookkeeping (the input of the currently running iteration is 30 * dumped to the crash directory whenever the process dies), 31 * - a standalone @c main() driver (generator + mutator loop, corpus 32 * replay, single-file replay) so that the harnesses are usable with 33 * a plain gcc + ASAN/UBSAN build, i.e. without clang/libFuzzer. 34 * 35 * The harness itself must provide: 36 * - @c LLVMFuzzerTestOneInput() (the actual fuzz target), 37 * - @c fuzz_generate() (a structure-aware input generator), 38 * - @c fuzz_seed_count() / @c fuzz_seed_get() (a built-in seed corpus). 39 * 40 * Define @c FUZZ_NO_MAIN when linking against libFuzzer or AFL++'s 41 * driver, which supply their own @c main(). 42 */ 43 44 #ifndef MHD_FUZZ_COMMON_H 45 #define MHD_FUZZ_COMMON_H 1 46 47 #include <stdint.h> 48 #include <stddef.h> 49 #include <stdio.h> 50 #include <stdlib.h> 51 #include <string.h> 52 #include <unistd.h> 53 #include <fcntl.h> 54 #include <signal.h> 55 #include <errno.h> 56 #include <sys/stat.h> 57 #include <sys/types.h> 58 #include <dirent.h> 59 60 #ifndef FUZZ_HARNESS_NAME 61 #define FUZZ_HARNESS_NAME "fuzz" 62 #endif 63 64 /** 65 * Not every harness uses every helper; silence -Wunused-function. 66 */ 67 #define FUZZ_UNUSED __attribute__ ((unused)) 68 69 /** 70 * Hard upper bound on the size of a single fuzz input. 71 */ 72 #ifndef FUZZ_MAX_INPUT 73 #define FUZZ_MAX_INPUT 32768 74 #endif 75 76 /** 77 * Default number of iterations of the built-in driver. Kept small so 78 * that "make check" stays in the "couple of seconds" range; raise with 79 * --iterations=N or the MHD_FUZZ_ITERATIONS environment variable. 80 */ 81 #ifndef FUZZ_DEFAULT_ITERATIONS 82 #define FUZZ_DEFAULT_ITERATIONS 3000 83 #endif 84 85 /** 86 * Default per-iteration watchdog, in seconds. 87 */ 88 #ifndef FUZZ_DEFAULT_TIMEOUT 89 #define FUZZ_DEFAULT_TIMEOUT 20 90 #endif 91 92 93 /* ------------------------------------------------------------------ */ 94 /* Interface to be implemented by each harness */ 95 /* ------------------------------------------------------------------ */ 96 97 /** 98 * The fuzz target. Signature is the libFuzzer one on purpose, so that 99 * the very same harness can be linked with libFuzzer or AFL++ later. 100 */ 101 int 102 LLVMFuzzerTestOneInput (const uint8_t *data, 103 size_t size); 104 105 struct fuzz_rng; 106 107 /** 108 * Structure-aware generator used by the built-in standalone driver. 109 * Must write at most @a cap bytes to @a buf and return the number of 110 * bytes written. Purely random bytes almost never form a valid HTTP 111 * request, so this is what actually makes the gcc-only driver useful. 112 */ 113 FUZZ_UNUSED static size_t 114 fuzz_generate (struct fuzz_rng *rng, 115 uint8_t *buf, 116 size_t cap); 117 118 /** 119 * @return number of entries in the built-in seed corpus 120 */ 121 FUZZ_UNUSED static size_t 122 fuzz_seed_count (void); 123 124 /** 125 * @param idx index of the seed to retrieve 126 * @param[out] len set to the length of the seed 127 * @return pointer to the seed bytes 128 */ 129 FUZZ_UNUSED static const uint8_t * 130 fuzz_seed_get (size_t idx, 131 size_t *len); 132 133 134 /* ------------------------------------------------------------------ */ 135 /* Deterministic PRNG */ 136 /* ------------------------------------------------------------------ */ 137 138 struct fuzz_rng 139 { 140 uint64_t s[4]; 141 }; 142 143 FUZZ_UNUSED static uint64_t 144 fuzz_splitmix64 (uint64_t *x) 145 { 146 uint64_t z; 147 148 *x += UINT64_C (0x9E3779B97F4A7C15); 149 z = *x; 150 z = (z ^ (z >> 30)) * UINT64_C (0xBF58476D1CE4E5B9); 151 z = (z ^ (z >> 27)) * UINT64_C (0x94D049BB133111EB); 152 return z ^ (z >> 31); 153 } 154 155 156 FUZZ_UNUSED static void 157 fuzz_rng_seed (struct fuzz_rng *r, 158 uint64_t seed) 159 { 160 uint64_t x = seed; 161 unsigned int i; 162 163 for (i = 0; i < 4; i++) 164 r->s[i] = fuzz_splitmix64 (&x); 165 } 166 167 168 FUZZ_UNUSED static uint64_t 169 fuzz_rot64 (uint64_t x, 170 unsigned int k) 171 { 172 return (x << k) | (x >> (64 - k)); 173 } 174 175 176 /** 177 * xoshiro256** -- small, fast, deterministic and identical on every 178 * platform, which is what we need for reproducible fuzzing runs. 179 */ 180 FUZZ_UNUSED static uint64_t 181 fuzz_next (struct fuzz_rng *r) 182 { 183 const uint64_t res = fuzz_rot64 (r->s[1] * 5, 7) * 9; 184 const uint64_t t = r->s[1] << 17; 185 186 r->s[2] ^= r->s[0]; 187 r->s[3] ^= r->s[1]; 188 r->s[1] ^= r->s[2]; 189 r->s[0] ^= r->s[3]; 190 r->s[2] ^= t; 191 r->s[3] = fuzz_rot64 (r->s[3], 45); 192 return res; 193 } 194 195 196 /** 197 * @return uniformly distributed value in [0, n), 0 if @a n is 0 198 */ 199 FUZZ_UNUSED static uint32_t 200 fuzz_below (struct fuzz_rng *r, 201 uint32_t n) 202 { 203 if (0 == n) 204 return 0; 205 return (uint32_t) (fuzz_next (r) % n); 206 } 207 208 209 FUZZ_UNUSED static uint8_t 210 fuzz_byte (struct fuzz_rng *r) 211 { 212 return (uint8_t) (fuzz_next (r) & 0xFF); 213 } 214 215 216 /** 217 * @return true with a probability of 1/@a n 218 */ 219 FUZZ_UNUSED static int 220 fuzz_chance (struct fuzz_rng *r, 221 uint32_t n) 222 { 223 return 0 == fuzz_below (r, n); 224 } 225 226 227 /* ------------------------------------------------------------------ */ 228 /* Global driver state */ 229 /* ------------------------------------------------------------------ */ 230 231 /** 232 * Non-zero if the input of the current iteration comes from a trusted 233 * source (the built-in generator, the built-in seed corpus, --file or 234 * --corpus-dir) and has NOT been mutated afterwards. Harnesses use 235 * this to enable "ground truth" oracles, e.g. the expected decoded 236 * request body that a generated chunked request declares. Random 237 * mutations invalidate such declarations, hence the flag. 238 */ 239 FUZZ_UNUSED static int fuzz_pristine; 240 241 /** 242 * Non-zero to let the harness enable MHD's own error log. 243 */ 244 FUZZ_UNUSED static int fuzz_verbose; 245 246 /** 247 * Non-zero to skip the replay of the built-in seed corpus at the start 248 * of a run (useful when a seed is known to trigger an already-reported 249 * finding and one wants to look for others). 250 */ 251 FUZZ_UNUSED static int fuzz_skip_seeds; 252 253 /* Only read by the built-in driver; under -DFUZZ_NO_MAIN (the libFuzzer 254 and AFL++ builds, see contrib/oss-fuzz/build.sh) they are written but 255 never read, which is not a defect. */ 256 FUZZ_UNUSED static uint64_t fuzz_cur_seed; 257 FUZZ_UNUSED static uint64_t fuzz_cur_iter; 258 static const uint8_t *fuzz_cur_input; 259 static size_t fuzz_cur_input_len; 260 static const char *fuzz_crash_dir = "crashes"; 261 262 /* Pre-rendered, so that the death/signal handlers stay 263 async-signal-safe (no snprintf, no malloc). */ 264 static char fuzz_crash_path[512]; 265 static char fuzz_crash_msg[512]; 266 static volatile sig_atomic_t fuzz_dumped; 267 268 269 FUZZ_UNUSED static void 270 fuzz_write_all (int fd, 271 const void *buf, 272 size_t len) 273 { 274 const char *p = (const char *) buf; 275 276 while (0 != len) 277 { 278 ssize_t w = write (fd, p, len); 279 if (0 >= w) 280 break; 281 p += w; 282 len -= (size_t) w; 283 } 284 } 285 286 287 FUZZ_UNUSED static void 288 fuzz_msg (const char *s) 289 { 290 fuzz_write_all (STDERR_FILENO, s, strlen (s)); 291 } 292 293 294 /** 295 * Dump the input of the currently running iteration so that the 296 * failure can be replayed with --file=... Async-signal-safe. 297 */ 298 FUZZ_UNUSED static void 299 fuzz_dump_current (void) 300 { 301 int fd; 302 303 if (fuzz_dumped) 304 return; 305 fuzz_dumped = 1; 306 if ( (NULL == fuzz_cur_input) || 307 ('\0' == fuzz_crash_path[0]) ) 308 return; 309 (void) mkdir (fuzz_crash_dir, 0755); 310 fd = open (fuzz_crash_path, 311 O_WRONLY | O_CREAT | O_TRUNC, 312 0644); 313 if (0 > fd) 314 { 315 fuzz_msg ("\n*** FUZZ: failed to write crash file ***\n"); 316 return; 317 } 318 fuzz_write_all (fd, fuzz_cur_input, fuzz_cur_input_len); 319 (void) close (fd); 320 fuzz_msg ("\n*** FUZZ: reproducer written to "); 321 fuzz_msg (fuzz_crash_path); 322 fuzz_msg (" ***\n*** FUZZ: "); 323 fuzz_msg (fuzz_crash_msg); 324 fuzz_msg (" ***\n"); 325 } 326 327 328 FUZZ_UNUSED static void 329 fuzz_death_callback (void) 330 { 331 fuzz_dump_current (); 332 } 333 334 335 FUZZ_UNUSED static void 336 fuzz_sig_handler (int sig) 337 { 338 fuzz_dump_current (); 339 if (SIGALRM == sig) 340 { 341 fuzz_msg ("*** FUZZ: HANG detected (watchdog fired) ***\n"); 342 _exit (99); 343 } 344 /* restore default handler and re-raise so that the usual 345 ASAN/abort diagnostics are produced */ 346 signal (sig, SIG_DFL); 347 raise (sig); 348 } 349 350 351 /** 352 * Report a logical (non-memory-safety) finding: dump the reproducer 353 * and abort so that the failure is impossible to overlook. 354 */ 355 FUZZ_UNUSED static void 356 fuzz_report_finding (const char *what) 357 { 358 size_t l = strlen (what); 359 360 if (l >= sizeof (fuzz_crash_msg)) 361 l = sizeof (fuzz_crash_msg) - 1; 362 memcpy (fuzz_crash_msg, what, l); 363 fuzz_crash_msg[l] = '\0'; 364 fuzz_msg ("\n*** FUZZ FINDING: "); 365 fuzz_msg (fuzz_crash_msg); 366 fuzz_msg (" ***\n"); 367 fuzz_dump_current (); 368 abort (); 369 } 370 371 372 /* Weak declaration: resolved when built with ASAN (gcc or clang), 373 NULL otherwise. ASAN calls this right before it terminates the 374 process, which is the only reliable hook when abort_on_error=0. */ 375 extern void 376 __sanitizer_set_death_callback (void (*cb)(void)) __attribute__ ((weak)); 377 378 379 /** 380 * Ignore SIGPIPE. Idempotent, so it is safe to call on every execution. 381 * 382 * This deliberately lives OUTSIDE the #ifndef FUZZ_NO_MAIN block below, 383 * because it is needed by the fuzz *target*, not merely by the built-in 384 * driver. fuzz_request writes into an AF_UNIX socketpair whose peer end 385 * MHD may already have closed (any input that makes the daemon drop the 386 * connection early does this), and a write() to a socket with no reader 387 * raises SIGPIPE. 388 * 389 * None of the external engines does this for us: libFuzzer intercepts 390 * SEGV/BUS/ABRT/ILL/FPE/INT/TERM/XFSZ/USR1/USR2 and has no 391 * -handle_sigpipe flag at all, and AFL++/honggfuzz likewise leave the 392 * default disposition in place. So without this call an OSS-Fuzz build 393 * of fuzz_request is killed by SIGPIPE after a few dozen executions, 394 * with no stack trace, no artifact and no report -- the target simply 395 * stops fuzzing. Measured here: dead after ~28 execs without it, 396 * 540000 execs in 31 s with it. 397 */ 398 FUZZ_UNUSED static void 399 fuzz_ignore_sigpipe (void) 400 { 401 static int sigpipe_ignored; 402 403 if (sigpipe_ignored) 404 return; 405 sigpipe_ignored = 1; 406 (void) signal (SIGPIPE, SIG_IGN); 407 } 408 409 410 #ifndef FUZZ_NO_MAIN 411 412 static void 413 fuzz_install_handlers (void) 414 { 415 struct sigaction sa; 416 417 if (NULL != __sanitizer_set_death_callback) 418 __sanitizer_set_death_callback (&fuzz_death_callback); 419 memset (&sa, 0, sizeof (sa)); 420 sa.sa_handler = &fuzz_sig_handler; 421 sigemptyset (&sa.sa_mask); 422 sa.sa_flags = 0; 423 (void) sigaction (SIGABRT, &sa, NULL); 424 (void) sigaction (SIGSEGV, &sa, NULL); 425 (void) sigaction (SIGBUS, &sa, NULL); 426 (void) sigaction (SIGILL, &sa, NULL); 427 (void) sigaction (SIGFPE, &sa, NULL); 428 (void) sigaction (SIGALRM, &sa, NULL); 429 fuzz_ignore_sigpipe (); 430 } 431 432 433 /** 434 * Remember which input we are about to feed to the target, and 435 * pre-render the name of the file it would be dumped to. 436 */ 437 static void 438 fuzz_set_current (const uint8_t *data, 439 size_t size, 440 const char *tag) 441 { 442 fuzz_cur_input = data; 443 fuzz_cur_input_len = size; 444 fuzz_dumped = 0; 445 (void) snprintf (fuzz_crash_path, 446 sizeof (fuzz_crash_path), 447 "%s/crash-%s-seed%llu-iter%llu.bin", 448 fuzz_crash_dir, 449 FUZZ_HARNESS_NAME, 450 (unsigned long long) fuzz_cur_seed, 451 (unsigned long long) fuzz_cur_iter); 452 (void) snprintf (fuzz_crash_msg, 453 sizeof (fuzz_crash_msg), 454 "harness=%s seed=%llu iteration=%llu source=%s", 455 FUZZ_HARNESS_NAME, 456 (unsigned long long) fuzz_cur_seed, 457 (unsigned long long) fuzz_cur_iter, 458 tag); 459 } 460 461 462 /* ------------------------------------------------------------------ */ 463 /* Generic byte-level mutator */ 464 /* ------------------------------------------------------------------ */ 465 466 static const uint8_t fuzz_interesting[] = { 467 0x00, 0x01, 0x07, 0x09, 0x0A, 0x0D, 0x20, 0x22, 0x25, 0x26, 0x27, 468 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x3A, 0x3B, 0x3D, 0x3F, 0x40, 469 0x5C, 0x7B, 0x7D, 0x7F, 0x80, 0xC0, 0xFE, 0xFF 470 }; 471 472 static const char *const fuzz_interesting_str[] = { 473 "\r\n", "\r\n\r\n", "\n", "%", "%%NONCE%%", ";", "=", "\"", "\\", 474 "chunked", "Transfer-Encoding: ", "Content-Length: ", "algorithm=", 475 "userhash=true", "Authorization: Digest ", "Authorization: Basic ", 476 "boundary=", "multipart/form-data", "0\r\n\r\n", "?", "&", 477 "aaaaaaaaaaaaaaaa" 478 }; 479 480 481 /** 482 * Apply a single random mutation to @a buf. 483 * 484 * @param rng the PRNG state 485 * @param[in,out] buf the buffer to mutate 486 * @param len current length 487 * @param cap capacity of @a buf 488 * @return new length 489 */ 490 static size_t 491 fuzz_mutate_once (struct fuzz_rng *rng, 492 uint8_t *buf, 493 size_t len, 494 size_t cap) 495 { 496 uint32_t op; 497 498 if (0 == len) 499 { 500 buf[0] = fuzz_byte (rng); 501 return 1; 502 } 503 op = fuzz_below (rng, 10); 504 switch (op) 505 { 506 case 0: /* bit flip */ 507 { 508 size_t p = fuzz_below (rng, (uint32_t) len); 509 buf[p] = (uint8_t) (buf[p] ^ (1u << fuzz_below (rng, 8))); 510 break; 511 } 512 case 1: /* random byte */ 513 buf[fuzz_below (rng, (uint32_t) len)] = fuzz_byte (rng); 514 break; 515 case 2: /* interesting byte */ 516 buf[fuzz_below (rng, (uint32_t) len)] = 517 fuzz_interesting[fuzz_below (rng, 518 (uint32_t) (sizeof (fuzz_interesting)))]; 519 break; 520 case 3: /* add/sub small value */ 521 { 522 size_t p = fuzz_below (rng, (uint32_t) len); 523 buf[p] = (uint8_t) (buf[p] + (int) fuzz_below (rng, 17) - 8); 524 break; 525 } 526 case 4: /* erase a run */ 527 { 528 size_t p = fuzz_below (rng, (uint32_t) len); 529 size_t n = 1 + fuzz_below (rng, (uint32_t) (len - p)); 530 memmove (buf + p, buf + p + n, len - p - n); 531 len -= n; 532 break; 533 } 534 case 5: /* insert repeated byte */ 535 { 536 size_t p = fuzz_below (rng, (uint32_t) len + 1); 537 size_t n = 1 + fuzz_below (rng, 32); 538 uint8_t v = fuzz_byte (rng); 539 if (len + n > cap) 540 n = cap - len; 541 if (0 == n) 542 break; 543 memmove (buf + p + n, buf + p, len - p); 544 memset (buf + p, v, n); 545 len += n; 546 break; 547 } 548 case 6: /* duplicate a chunk */ 549 { 550 size_t p = fuzz_below (rng, (uint32_t) len); 551 size_t n = 1 + fuzz_below (rng, (uint32_t) (len - p)); 552 size_t d = fuzz_below (rng, (uint32_t) len + 1); 553 if (len + n > cap) 554 n = cap - len; 555 if (0 == n) 556 break; 557 memmove (buf + d + n, buf + d, len - d); 558 memmove (buf + d, buf + ((p >= d) ? (p + n) : p), n); 559 len += n; 560 break; 561 } 562 case 7: /* insert an interesting token */ 563 { 564 const char *s = 565 fuzz_interesting_str[fuzz_below (rng, 566 (uint32_t) 567 (sizeof (fuzz_interesting_str) 568 / sizeof (fuzz_interesting_str[0])))]; 569 size_t n = strlen (s); 570 size_t p = fuzz_below (rng, (uint32_t) len + 1); 571 if (len + n > cap) 572 break; 573 memmove (buf + p + n, buf + p, len - p); 574 memcpy (buf + p, s, n); 575 len += n; 576 break; 577 } 578 case 8: /* swap two bytes */ 579 { 580 size_t a = fuzz_below (rng, (uint32_t) len); 581 size_t b = fuzz_below (rng, (uint32_t) len); 582 uint8_t t = buf[a]; 583 buf[a] = buf[b]; 584 buf[b] = t; 585 break; 586 } 587 default: /* truncate */ 588 len = 1 + fuzz_below (rng, (uint32_t) len); 589 break; 590 } 591 return len; 592 } 593 594 595 /* ------------------------------------------------------------------ */ 596 /* Standalone driver */ 597 /* ------------------------------------------------------------------ */ 598 599 static int 600 fuzz_run_file (const char *path) 601 { 602 FILE *f; 603 uint8_t *buf; 604 size_t n; 605 606 f = fopen (path, "rb"); 607 if (NULL == f) 608 { 609 fprintf (stderr, 610 "%s: cannot open '%s': %s\n", 611 FUZZ_HARNESS_NAME, 612 path, 613 strerror (errno)); 614 return 1; 615 } 616 buf = (uint8_t *) malloc (FUZZ_MAX_INPUT); 617 if (NULL == buf) 618 { 619 (void) fclose (f); 620 return 1; 621 } 622 n = fread (buf, 1, FUZZ_MAX_INPUT, f); 623 (void) fclose (f); 624 fuzz_pristine = 1; 625 fuzz_set_current (buf, n, path); 626 alarm (FUZZ_DEFAULT_TIMEOUT); 627 (void) LLVMFuzzerTestOneInput (buf, n); 628 alarm (0); 629 free (buf); 630 return 0; 631 } 632 633 634 static int 635 fuzz_run_corpus_dir (const char *dir) 636 { 637 DIR *d; 638 struct dirent *de; 639 char path[1024]; 640 int ret = 0; 641 unsigned int cnt = 0; 642 643 d = opendir (dir); 644 if (NULL == d) 645 { 646 fprintf (stderr, 647 "%s: cannot open corpus dir '%s': %s\n", 648 FUZZ_HARNESS_NAME, 649 dir, 650 strerror (errno)); 651 return 1; 652 } 653 while (NULL != (de = readdir (d))) 654 { 655 struct stat sb; 656 657 if ('.' == de->d_name[0]) 658 continue; 659 (void) snprintf (path, sizeof (path), "%s/%s", dir, de->d_name); 660 if ( (0 != stat (path, &sb)) || 661 (! S_ISREG (sb.st_mode)) ) 662 continue; 663 fuzz_cur_iter = cnt++; 664 ret |= fuzz_run_file (path); 665 } 666 (void) closedir (d); 667 printf ("%s: replayed %u corpus file(s) from %s\n", 668 FUZZ_HARNESS_NAME, cnt, dir); 669 return ret; 670 } 671 672 673 static void 674 fuzz_usage (const char *argv0) 675 { 676 printf ( 677 "Usage: %s [OPTIONS] [FILE...]\n" 678 "\n" 679 "In-process fuzzing harness '%s' for GNU libmicrohttpd.\n" 680 "\n" 681 " --iterations=N number of generate/mutate iterations (default %d)\n" 682 " --seed=N PRNG seed; runs are fully reproducible (default 1)\n" 683 " --corpus-dir=DIR replay every regular file in DIR and exit\n" 684 " --file=PATH replay a single input and exit (crash reproduction)\n" 685 " --crash-dir=DIR where to write reproducers (default 'crashes')\n" 686 " --timeout=SEC per-iteration watchdog (default %d, 0 disables)\n" 687 " --write-corpus=DIR write the built-in seed corpus to DIR and exit\n" 688 " --skip-seeds do not replay the built-in seed corpus first\n" 689 " --verbose enable MHD's error log inside the harness\n" 690 " --help this text\n" 691 "\n" 692 "Environment: MHD_FUZZ_ITERATIONS, MHD_FUZZ_SEED, MHD_FUZZ_TIMEOUT,\n" 693 " MHD_FUZZ_CRASH_DIR, MHD_FUZZ_VERBOSE\n" 694 "\n" 695 "Bare FILE arguments are equivalent to --file=FILE (libFuzzer-style).\n", 696 argv0, FUZZ_HARNESS_NAME, 697 (int) FUZZ_DEFAULT_ITERATIONS, (int) FUZZ_DEFAULT_TIMEOUT); 698 } 699 700 701 static int 702 fuzz_write_corpus (const char *dir) 703 { 704 size_t i; 705 size_t n = fuzz_seed_count (); 706 707 if ( (0 != mkdir (dir, 0755)) && 708 (EEXIST != errno) ) 709 { 710 fprintf (stderr, "%s: mkdir '%s': %s\n", 711 FUZZ_HARNESS_NAME, dir, strerror (errno)); 712 return 1; 713 } 714 for (i = 0; i < n; i++) 715 { 716 char path[1024]; 717 size_t len; 718 const uint8_t *s = fuzz_seed_get (i, &len); 719 FILE *f; 720 721 (void) snprintf (path, sizeof (path), "%s/%s-%02u.bin", 722 dir, FUZZ_HARNESS_NAME, (unsigned int) i); 723 f = fopen (path, "wb"); 724 if (NULL == f) 725 { 726 fprintf (stderr, "%s: fopen '%s': %s\n", 727 FUZZ_HARNESS_NAME, path, strerror (errno)); 728 return 1; 729 } 730 if (len != fwrite (s, 1, len, f)) 731 { 732 (void) fclose (f); 733 return 1; 734 } 735 (void) fclose (f); 736 } 737 printf ("%s: wrote %u seed(s) to %s\n", 738 FUZZ_HARNESS_NAME, (unsigned int) n, dir); 739 return 0; 740 } 741 742 743 int 744 main (int argc, char *const *argv) 745 { 746 uint64_t iterations = FUZZ_DEFAULT_ITERATIONS; 747 uint64_t seed = 1; 748 unsigned int timeout = FUZZ_DEFAULT_TIMEOUT; 749 const char *corpus_dir = NULL; 750 const char *single_file = NULL; 751 const char *write_corpus = NULL; 752 struct fuzz_rng rng; 753 uint8_t *buf; 754 uint64_t i; 755 int j; 756 const char *e; 757 int ret = 0; 758 759 e = getenv ("MHD_FUZZ_ITERATIONS"); 760 if (NULL != e) 761 iterations = strtoull (e, NULL, 10); 762 e = getenv ("MHD_FUZZ_SEED"); 763 if (NULL != e) 764 seed = strtoull (e, NULL, 10); 765 e = getenv ("MHD_FUZZ_TIMEOUT"); 766 if (NULL != e) 767 timeout = (unsigned int) strtoul (e, NULL, 10); 768 e = getenv ("MHD_FUZZ_CRASH_DIR"); 769 if (NULL != e) 770 fuzz_crash_dir = e; 771 e = getenv ("MHD_FUZZ_VERBOSE"); 772 if (NULL != e) 773 fuzz_verbose = (0 != atoi (e)); 774 e = getenv ("MHD_FUZZ_SKIP_SEEDS"); 775 if (NULL != e) 776 fuzz_skip_seeds = (0 != atoi (e)); 777 778 for (j = 1; j < argc; j++) 779 { 780 const char *a = argv[j]; 781 782 if (0 == strncmp (a, "--iterations=", 13)) 783 iterations = strtoull (a + 13, NULL, 10); 784 else if (0 == strncmp (a, "--seed=", 7)) 785 seed = strtoull (a + 7, NULL, 10); 786 else if (0 == strncmp (a, "--corpus-dir=", 13)) 787 corpus_dir = a + 13; 788 else if (0 == strncmp (a, "--file=", 7)) 789 single_file = a + 7; 790 else if (0 == strncmp (a, "--crash-dir=", 12)) 791 fuzz_crash_dir = a + 12; 792 else if (0 == strncmp (a, "--timeout=", 10)) 793 timeout = (unsigned int) strtoul (a + 10, NULL, 10); 794 else if (0 == strncmp (a, "--write-corpus=", 15)) 795 write_corpus = a + 15; 796 else if (0 == strcmp (a, "--skip-seeds")) 797 fuzz_skip_seeds = 1; 798 else if (0 == strcmp (a, "--verbose")) 799 fuzz_verbose = 1; 800 else if ( (0 == strcmp (a, "--help")) || 801 (0 == strcmp (a, "-h")) ) 802 { 803 fuzz_usage (argv[0]); 804 return 0; 805 } 806 else if ('-' == a[0]) 807 { 808 fprintf (stderr, "%s: unknown option '%s'\n", FUZZ_HARNESS_NAME, a); 809 fuzz_usage (argv[0]); 810 return 2; 811 } 812 else 813 single_file = a; 814 } 815 816 fuzz_cur_seed = seed; 817 fuzz_install_handlers (); 818 819 if (NULL != write_corpus) 820 return fuzz_write_corpus (write_corpus); 821 822 if (NULL != single_file) 823 { 824 printf ("%s: replaying %s\n", FUZZ_HARNESS_NAME, single_file); 825 ret = fuzz_run_file (single_file); 826 printf ("%s: replay finished without a finding\n", FUZZ_HARNESS_NAME); 827 return ret; 828 } 829 if (NULL != corpus_dir) 830 return fuzz_run_corpus_dir (corpus_dir); 831 832 buf = (uint8_t *) malloc (FUZZ_MAX_INPUT); 833 if (NULL == buf) 834 return 1; 835 fuzz_rng_seed (&rng, seed); 836 printf ("%s: seed=%llu iterations=%llu\n", 837 FUZZ_HARNESS_NAME, 838 (unsigned long long) seed, 839 (unsigned long long) iterations); 840 fflush (stdout); 841 842 for (i = 0; i < iterations; i++) 843 { 844 size_t len; 845 const char *tag; 846 uint32_t mode; 847 848 fuzz_cur_iter = i; 849 if ( (! fuzz_skip_seeds) && 850 (i < fuzz_seed_count ()) ) 851 { 852 size_t sl; 853 const uint8_t *s = fuzz_seed_get ((size_t) i, &sl); 854 855 if (sl > FUZZ_MAX_INPUT) 856 sl = FUZZ_MAX_INPUT; 857 memcpy (buf, s, sl); 858 len = sl; 859 fuzz_pristine = 1; 860 tag = "builtin-seed"; 861 } 862 else 863 { 864 mode = fuzz_below (&rng, 100); 865 if (mode < 55) 866 { 867 fuzz_pristine = 1; 868 len = fuzz_generate (&rng, buf, FUZZ_MAX_INPUT); 869 tag = "generated"; 870 } 871 else 872 { 873 unsigned int k; 874 unsigned int nmut; 875 876 if (mode < 85) 877 { 878 len = fuzz_generate (&rng, buf, FUZZ_MAX_INPUT); 879 tag = "generated+mutated"; 880 } 881 else 882 { 883 size_t sl; 884 const uint8_t *s; 885 886 if (0 == fuzz_seed_count ()) 887 { 888 len = fuzz_generate (&rng, buf, FUZZ_MAX_INPUT); 889 } 890 else 891 { 892 s = fuzz_seed_get (fuzz_below (&rng, 893 (uint32_t) fuzz_seed_count ()), 894 &sl); 895 if (sl > FUZZ_MAX_INPUT) 896 sl = FUZZ_MAX_INPUT; 897 memcpy (buf, s, sl); 898 len = sl; 899 } 900 tag = "seed+mutated"; 901 } 902 fuzz_pristine = 0; 903 nmut = 1 + fuzz_below (&rng, 8); 904 for (k = 0; k < nmut; k++) 905 len = fuzz_mutate_once (&rng, buf, len, FUZZ_MAX_INPUT); 906 } 907 } 908 fuzz_set_current (buf, len, tag); 909 if (0 != timeout) 910 alarm (timeout); 911 (void) LLVMFuzzerTestOneInput (buf, len); 912 if (0 != timeout) 913 alarm (0); 914 } 915 free (buf); 916 printf ("%s: %llu iterations completed, no findings\n", 917 FUZZ_HARNESS_NAME, 918 (unsigned long long) iterations); 919 return ret; 920 } 921 922 923 #endif /* ! FUZZ_NO_MAIN */ 924 925 #endif /* MHD_FUZZ_COMMON_H */