fuzz_common.h (29696B)
1 /* 2 This file is part of libextractor. 3 Copyright (C) 2026 Christian Grothoff 4 5 libextractor is free software; you can redistribute it and/or modify 6 it under the terms of the GNU General Public License as published 7 by the Free Software Foundation; either version 3, or (at your 8 option) any later version. 9 10 libextractor is distributed in the hope that it will be useful, but 11 WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 General Public License for more details. 14 15 You should have received a copy of the GNU General Public License 16 along with libextractor; see the file COPYING. If not, write to the 17 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 18 Boston, MA 02110-1301, USA. 19 */ 20 /** 21 * @file fuzz/fuzz_common.h 22 * @brief shared, header-only fuzzing driver for the libextractor 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(). Anything the fuzz *target* 42 * needs must stay outside the @c FUZZ_NO_MAIN block; see 43 * BUILD-INTEGRATION.md section 6. 44 */ 45 #ifndef LE_FUZZ_COMMON_H 46 #define LE_FUZZ_COMMON_H 1 47 48 #include <stdint.h> 49 #include <stddef.h> 50 #include <stdio.h> 51 #include <stdlib.h> 52 #include <string.h> 53 #include <unistd.h> 54 #include <fcntl.h> 55 #include <signal.h> 56 #include <errno.h> 57 #include <sys/stat.h> 58 #include <sys/types.h> 59 #include <dirent.h> 60 61 #ifndef FUZZ_HARNESS_NAME 62 #define FUZZ_HARNESS_NAME "fuzz" 63 #endif 64 65 /** 66 * Not every harness uses every helper; silence -Wunused-function. 67 */ 68 #define FUZZ_UNUSED __attribute__ ((unused)) 69 70 /** 71 * Hard upper bound on the size of a single fuzz input. libextractor 72 * parses container formats whose interesting structures (a ZIP central 73 * directory, an OLE2 FAT, a RIFF chunk tree) live well past the first 74 * few kilobytes, so this is considerably larger than a protocol fuzzer 75 * would need. 76 */ 77 #ifndef FUZZ_MAX_INPUT 78 #define FUZZ_MAX_INPUT (256 * 1024) 79 #endif 80 81 /** 82 * Default number of iterations of the built-in driver. Kept small so 83 * that "make check" stays in the "couple of seconds" range; raise with 84 * --iterations=N or the LE_FUZZ_ITERATIONS environment variable. 85 */ 86 #ifndef FUZZ_DEFAULT_ITERATIONS 87 #define FUZZ_DEFAULT_ITERATIONS 3000 88 #endif 89 90 /** 91 * Default per-iteration watchdog, in seconds. 92 */ 93 #ifndef FUZZ_DEFAULT_TIMEOUT 94 #define FUZZ_DEFAULT_TIMEOUT 20 95 #endif 96 97 98 /* ------------------------------------------------------------------ */ 99 /* Interface to be implemented by each harness */ 100 /* ------------------------------------------------------------------ */ 101 102 /** 103 * The fuzz target. Signature is the libFuzzer one on purpose, so that 104 * the very same harness can be linked with libFuzzer or AFL++. 105 */ 106 int 107 LLVMFuzzerTestOneInput (const uint8_t *data, 108 size_t size); 109 110 struct fuzz_rng; 111 112 /** 113 * Structure-aware generator used by the built-in standalone driver. 114 * Must write at most @a cap bytes to @a buf and return the number of 115 * bytes written. Purely random bytes almost never form a file that 116 * gets past a plugin's magic-number check, so this is what actually 117 * makes the gcc-only driver useful. 118 */ 119 FUZZ_UNUSED static size_t 120 fuzz_generate (struct fuzz_rng *rng, 121 uint8_t *buf, 122 size_t cap); 123 124 /** 125 * @return number of entries in the built-in seed corpus 126 */ 127 FUZZ_UNUSED static size_t 128 fuzz_seed_count (void); 129 130 /** 131 * @param idx index of the seed to retrieve 132 * @param[out] len set to the length of the seed 133 * @return pointer to the seed bytes 134 */ 135 FUZZ_UNUSED static const uint8_t * 136 fuzz_seed_get (size_t idx, 137 size_t *len); 138 139 140 /* ------------------------------------------------------------------ */ 141 /* Deterministic PRNG */ 142 /* ------------------------------------------------------------------ */ 143 144 struct fuzz_rng 145 { 146 uint64_t s[4]; 147 }; 148 149 150 FUZZ_UNUSED static uint64_t 151 fuzz_splitmix64 (uint64_t *x) 152 { 153 uint64_t z; 154 155 *x += UINT64_C (0x9E3779B97F4A7C15); 156 z = *x; 157 z = (z ^ (z >> 30)) * UINT64_C (0xBF58476D1CE4E5B9); 158 z = (z ^ (z >> 27)) * UINT64_C (0x94D049BB133111EB); 159 return z ^ (z >> 31); 160 } 161 162 163 FUZZ_UNUSED static void 164 fuzz_rng_seed (struct fuzz_rng *r, 165 uint64_t seed) 166 { 167 uint64_t x = seed; 168 unsigned int i; 169 170 for (i = 0; i < 4; i++) 171 r->s[i] = fuzz_splitmix64 (&x); 172 } 173 174 175 FUZZ_UNUSED static uint64_t 176 fuzz_rot64 (uint64_t x, 177 unsigned int k) 178 { 179 return (x << k) | (x >> (64 - k)); 180 } 181 182 183 /** 184 * xoshiro256** -- small, fast, deterministic and identical on every 185 * platform, which is what we need for reproducible fuzzing runs. 186 */ 187 FUZZ_UNUSED static uint64_t 188 fuzz_next (struct fuzz_rng *r) 189 { 190 const uint64_t res = fuzz_rot64 (r->s[1] * 5, 7) * 9; 191 const uint64_t t = r->s[1] << 17; 192 193 r->s[2] ^= r->s[0]; 194 r->s[3] ^= r->s[1]; 195 r->s[1] ^= r->s[2]; 196 r->s[0] ^= r->s[3]; 197 r->s[2] ^= t; 198 r->s[3] = fuzz_rot64 (r->s[3], 45); 199 return res; 200 } 201 202 203 /** 204 * @return uniformly distributed value in [0, n), 0 if @a n is 0 205 */ 206 FUZZ_UNUSED static uint32_t 207 fuzz_below (struct fuzz_rng *r, 208 uint32_t n) 209 { 210 if (0 == n) 211 return 0; 212 return (uint32_t) (fuzz_next (r) % n); 213 } 214 215 216 FUZZ_UNUSED static uint8_t 217 fuzz_byte (struct fuzz_rng *r) 218 { 219 return (uint8_t) (fuzz_next (r) & 0xFF); 220 } 221 222 223 /** 224 * @return true with a probability of 1/@a n 225 */ 226 FUZZ_UNUSED static int 227 fuzz_chance (struct fuzz_rng *r, 228 uint32_t n) 229 { 230 return 0 == fuzz_below (r, n); 231 } 232 233 234 /** 235 * Append @a n little-endian bytes of @a v to @a buf. 236 * 237 * @param buf buffer to append to 238 * @param[in,out] len current length, updated 239 * @param cap capacity of @a buf 240 * @param v value to append 241 * @param n number of bytes of @a v to append 242 */ 243 FUZZ_UNUSED static void 244 fuzz_put_le (uint8_t *buf, 245 size_t *len, 246 size_t cap, 247 uint64_t v, 248 unsigned int n) 249 { 250 unsigned int i; 251 252 for (i = 0; i < n; i++) 253 { 254 if (*len >= cap) 255 return; 256 buf[(*len)++] = (uint8_t) (v >> (8 * i)); 257 } 258 } 259 260 261 /** 262 * Append @a n big-endian bytes of @a v to @a buf. 263 * 264 * @param buf buffer to append to 265 * @param[in,out] len current length, updated 266 * @param cap capacity of @a buf 267 * @param v value to append 268 * @param n number of bytes of @a v to append 269 */ 270 FUZZ_UNUSED static void 271 fuzz_put_be (uint8_t *buf, 272 size_t *len, 273 size_t cap, 274 uint64_t v, 275 unsigned int n) 276 { 277 unsigned int i; 278 279 for (i = 0; i < n; i++) 280 { 281 if (*len >= cap) 282 return; 283 buf[(*len)++] = (uint8_t) (v >> (8 * (n - 1 - i))); 284 } 285 } 286 287 288 /** 289 * Append @a n bytes from @a src to @a buf, truncating at @a cap. 290 */ 291 FUZZ_UNUSED static void 292 fuzz_put_mem (uint8_t *buf, 293 size_t *len, 294 size_t cap, 295 const void *src, 296 size_t n) 297 { 298 if (*len + n > cap) 299 n = (*len > cap) ? 0 : (cap - *len); 300 memcpy (buf + *len, src, n); 301 *len += n; 302 } 303 304 305 /** 306 * Append the NUL-terminated string @a s (without its NUL) to @a buf. 307 */ 308 FUZZ_UNUSED static void 309 fuzz_put_str (uint8_t *buf, 310 size_t *len, 311 size_t cap, 312 const char *s) 313 { 314 fuzz_put_mem (buf, len, cap, s, strlen (s)); 315 } 316 317 318 /* ------------------------------------------------------------------ */ 319 /* Global driver state */ 320 /* ------------------------------------------------------------------ */ 321 322 /** 323 * Non-zero if the input of the current iteration comes from a trusted 324 * source (the built-in generator, the built-in seed corpus, --file or 325 * --corpus-dir) and has NOT been mutated afterwards. Harnesses use 326 * this to enable "ground truth" oracles. Random mutations invalidate 327 * such declarations, hence the flag. 328 */ 329 FUZZ_UNUSED static int fuzz_pristine; 330 331 /** 332 * Non-zero to let the harness be chatty about what it is doing. 333 */ 334 FUZZ_UNUSED static int fuzz_verbose; 335 336 /** 337 * Non-zero to skip the replay of the built-in seed corpus at the start 338 * of a run (useful when a seed is known to trigger an already-reported 339 * finding and one wants to look for others). 340 */ 341 FUZZ_UNUSED static int fuzz_skip_seeds; 342 343 /* Only read by the built-in driver; under -DFUZZ_NO_MAIN (the libFuzzer 344 and AFL++ builds, see contrib/oss-fuzz/build.sh) they are written but 345 never read, which is not a defect. */ 346 FUZZ_UNUSED static uint64_t fuzz_cur_seed; 347 FUZZ_UNUSED static uint64_t fuzz_cur_iter; 348 static const uint8_t *fuzz_cur_input; 349 static size_t fuzz_cur_input_len; 350 static const char *fuzz_crash_dir = "crashes"; 351 352 /* Pre-rendered, so that the death/signal handlers stay 353 async-signal-safe (no snprintf, no malloc). */ 354 static char fuzz_crash_path[512]; 355 static char fuzz_crash_msg[512]; 356 static volatile sig_atomic_t fuzz_dumped; 357 358 359 FUZZ_UNUSED static void 360 fuzz_write_all (int fd, 361 const void *buf, 362 size_t len) 363 { 364 const char *p = (const char *) buf; 365 366 while (0 != len) 367 { 368 ssize_t w = write (fd, p, len); 369 370 if (0 >= w) 371 break; 372 p += w; 373 len -= (size_t) w; 374 } 375 } 376 377 378 FUZZ_UNUSED static void 379 fuzz_msg (const char *s) 380 { 381 fuzz_write_all (STDERR_FILENO, s, strlen (s)); 382 } 383 384 385 /** 386 * Dump the input of the currently running iteration so that the 387 * failure can be replayed with --file=... Async-signal-safe. 388 */ 389 FUZZ_UNUSED static void 390 fuzz_dump_current (void) 391 { 392 int fd; 393 394 if (fuzz_dumped) 395 return; 396 fuzz_dumped = 1; 397 if ( (NULL == fuzz_cur_input) || 398 ('\0' == fuzz_crash_path[0]) ) 399 return; 400 (void) mkdir (fuzz_crash_dir, 0755); 401 fd = open (fuzz_crash_path, 402 O_WRONLY | O_CREAT | O_TRUNC, 403 0644); 404 if (0 > fd) 405 { 406 fuzz_msg ("\n*** FUZZ: failed to write crash file ***\n"); 407 return; 408 } 409 fuzz_write_all (fd, fuzz_cur_input, fuzz_cur_input_len); 410 (void) close (fd); 411 fuzz_msg ("\n*** FUZZ: reproducer written to "); 412 fuzz_msg (fuzz_crash_path); 413 fuzz_msg (" ***\n*** FUZZ: "); 414 fuzz_msg (fuzz_crash_msg); 415 fuzz_msg (" ***\n"); 416 } 417 418 419 FUZZ_UNUSED static void 420 fuzz_death_callback (void) 421 { 422 fuzz_dump_current (); 423 } 424 425 426 FUZZ_UNUSED static void 427 fuzz_sig_handler (int sig) 428 { 429 fuzz_dump_current (); 430 if (SIGALRM == sig) 431 { 432 fuzz_msg ("*** FUZZ: HANG detected (watchdog fired) ***\n"); 433 _exit (99); 434 } 435 /* restore default handler and re-raise so that the usual 436 ASAN/abort diagnostics are produced */ 437 signal (sig, SIG_DFL); 438 raise (sig); 439 } 440 441 442 /** 443 * Report a logical (non-memory-safety) finding: dump the reproducer 444 * and abort so that the failure is impossible to overlook. 445 * 446 * @param what human readable description of the contract violation 447 */ 448 FUZZ_UNUSED static void 449 fuzz_report_finding (const char *what) 450 { 451 size_t l = strlen (what); 452 453 if (l >= sizeof (fuzz_crash_msg)) 454 l = sizeof (fuzz_crash_msg) - 1; 455 memcpy (fuzz_crash_msg, what, l); 456 fuzz_crash_msg[l] = '\0'; 457 fuzz_msg ("\n*** FUZZ FINDING: "); 458 fuzz_msg (fuzz_crash_msg); 459 fuzz_msg (" ***\n"); 460 fuzz_dump_current (); 461 abort (); 462 } 463 464 465 /* Weak declaration: resolved when built with ASAN (gcc or clang), 466 NULL otherwise. ASAN calls this right before it terminates the 467 process, which is the only reliable hook when abort_on_error=0. */ 468 extern void 469 __sanitizer_set_death_callback (void (*cb)(void)) __attribute__ ((weak)); 470 471 472 /** 473 * Ignore SIGPIPE. Idempotent, so it is safe to call on every execution. 474 * 475 * This deliberately lives OUTSIDE the #ifndef FUZZ_NO_MAIN block below, 476 * because it is needed by the fuzz *target*, not merely by the built-in 477 * driver: fuzz_extract runs plugins in-process but the core library 478 * still writes to pipes in some code paths, and fuzz_ipc writes into a 479 * socketpair whose peer end it closes on purpose. None of the external 480 * engines ignores SIGPIPE for us -- libFuzzer has no -handle_sigpipe 481 * flag at all -- so without this an OSS-Fuzz build simply stops fuzzing 482 * after a few dozen executions, with no report. 483 */ 484 FUZZ_UNUSED static void 485 fuzz_ignore_sigpipe (void) 486 { 487 static int sigpipe_ignored; 488 489 if (sigpipe_ignored) 490 return; 491 sigpipe_ignored = 1; 492 (void) signal (SIGPIPE, SIG_IGN); 493 } 494 495 496 /** 497 * Read an unsigned integer from the environment. 498 * 499 * @param name variable to read 500 * @param dflt value to return if unset or unparsable 501 * @return the configured value 502 */ 503 FUZZ_UNUSED static unsigned long 504 fuzz_env_ulong (const char *name, 505 unsigned long dflt) 506 { 507 const char *e = getenv (name); 508 char *end; 509 unsigned long v; 510 511 if (NULL == e) 512 return dflt; 513 errno = 0; 514 v = strtoul (e, &end, 0); 515 if ( (end == e) || 516 (0 != errno) ) 517 return dflt; 518 return v; 519 } 520 521 522 #ifndef FUZZ_NO_MAIN 523 524 static void 525 fuzz_install_handlers (void) 526 { 527 struct sigaction sa; 528 529 if (NULL != __sanitizer_set_death_callback) 530 __sanitizer_set_death_callback (&fuzz_death_callback); 531 memset (&sa, 0, sizeof (sa)); 532 sa.sa_handler = &fuzz_sig_handler; 533 sigemptyset (&sa.sa_mask); 534 sa.sa_flags = 0; 535 (void) sigaction (SIGABRT, &sa, NULL); 536 (void) sigaction (SIGSEGV, &sa, NULL); 537 (void) sigaction (SIGBUS, &sa, NULL); 538 (void) sigaction (SIGILL, &sa, NULL); 539 (void) sigaction (SIGFPE, &sa, NULL); 540 (void) sigaction (SIGALRM, &sa, NULL); 541 fuzz_ignore_sigpipe (); 542 } 543 544 545 /** 546 * Remember which input we are about to feed to the target, and 547 * pre-render the name of the file it would be dumped to. 548 */ 549 static void 550 fuzz_set_current (const uint8_t *data, 551 size_t size, 552 const char *tag) 553 { 554 fuzz_cur_input = data; 555 fuzz_cur_input_len = size; 556 fuzz_dumped = 0; 557 (void) snprintf (fuzz_crash_path, 558 sizeof (fuzz_crash_path), 559 "%s/crash-%s-seed%llu-iter%llu.bin", 560 fuzz_crash_dir, 561 FUZZ_HARNESS_NAME, 562 (unsigned long long) fuzz_cur_seed, 563 (unsigned long long) fuzz_cur_iter); 564 (void) snprintf (fuzz_crash_msg, 565 sizeof (fuzz_crash_msg), 566 "harness=%s seed=%llu iteration=%llu source=%s", 567 FUZZ_HARNESS_NAME, 568 (unsigned long long) fuzz_cur_seed, 569 (unsigned long long) fuzz_cur_iter, 570 tag); 571 } 572 573 574 /* ------------------------------------------------------------------ */ 575 /* Generic byte-level mutator */ 576 /* ------------------------------------------------------------------ */ 577 578 static const uint8_t fuzz_interesting[] = { 579 0x00, 0x01, 0x02, 0x04, 0x07, 0x08, 0x09, 0x0A, 0x0D, 0x10, 0x1A, 580 0x20, 0x22, 0x25, 0x27, 0x2C, 0x2E, 0x2F, 0x30, 0x3A, 0x3B, 0x3D, 581 0x5C, 0x7B, 0x7D, 0x7F, 0x80, 0xC0, 0xFE, 0xFF 582 }; 583 584 /** 585 * Byte strings that are structurally meaningful to at least one of the 586 * formats libextractor parses: container magic numbers, chunk tags and 587 * the length fields that guard them. 588 */ 589 static const char *const fuzz_interesting_str[] = { 590 "RIFF", "WAVE", "AVI ", "LIST", "INFO", "fmt ", "data", 591 "PK\x03\x04", "PK\x01\x02", "PK\x05\x06", 592 "\x89PNG\r\n\x1a\n", "IHDR", "tEXt", "zTXt", "iTXt", "IEND", 593 "GIF89a", "GIF87a", "\xff\xd8\xff\xe0", "\xff\xd8\xff\xe1", "Exif", 594 "\x7f" "ELF", "%!PS-Adobe-", "%%Title:", "%%Creator:", 595 "{\\rtf1", "\\info", "\\title", "\\*\\", "\\u", "\\'", 596 "NESM\x1a", "NSFE", "PSID", "RSID", "SCRM", "IMPM", 597 "Extended Module: ", "MThd", "MTrk", "fLaC", "OggS", 598 "\x1f\x8b\x08", "BZh9", "!<arch>\n", "debian-binary", 599 "moov", "mvhd", "ftyp", "mdat", "cmov", 600 "\xd0\xcf\x11\xe0\xa1\xb1\x1a\xe1", 601 "\x00\x05\x16\x07\x00\x02\x00\x00", 602 "\xf7\x02", "\xf7\x03", "\xed\xab\xee\xdb", 603 "mimetypeapplication/vnd.oasis.opendocument.", 604 "meta.xml", "content.xml", "docProps/core.xml", "word/document.xml", 605 ".TH ", ".SH ", "\r\n", "\n", "\xff\xff\xff\xff", "\x00\x00\x00\x00" 606 }; 607 608 609 /** 610 * Apply a single random mutation to @a buf. 611 * 612 * @param rng the PRNG state 613 * @param[in,out] buf the buffer to mutate 614 * @param len current length 615 * @param cap capacity of @a buf 616 * @return new length 617 */ 618 static size_t 619 fuzz_mutate_once (struct fuzz_rng *rng, 620 uint8_t *buf, 621 size_t len, 622 size_t cap) 623 { 624 uint32_t op; 625 626 if (0 == len) 627 { 628 buf[0] = fuzz_byte (rng); 629 return 1; 630 } 631 op = fuzz_below (rng, 11); 632 switch (op) 633 { 634 case 0: /* bit flip */ 635 { 636 size_t p = fuzz_below (rng, (uint32_t) len); 637 638 buf[p] = (uint8_t) (buf[p] ^ (1u << fuzz_below (rng, 8))); 639 break; 640 } 641 case 1: /* random byte */ 642 buf[fuzz_below (rng, (uint32_t) len)] = fuzz_byte (rng); 643 break; 644 case 2: /* interesting byte */ 645 buf[fuzz_below (rng, (uint32_t) len)] = 646 fuzz_interesting[fuzz_below (rng, 647 (uint32_t) (sizeof (fuzz_interesting)))]; 648 break; 649 case 3: /* add/sub small value */ 650 { 651 size_t p = fuzz_below (rng, (uint32_t) len); 652 653 buf[p] = (uint8_t) (buf[p] + (int) fuzz_below (rng, 17) - 8); 654 break; 655 } 656 case 4: /* erase a run */ 657 { 658 size_t p = fuzz_below (rng, (uint32_t) len); 659 size_t n = 1 + fuzz_below (rng, (uint32_t) (len - p)); 660 661 memmove (buf + p, buf + p + n, len - p - n); 662 len -= n; 663 break; 664 } 665 case 5: /* insert repeated byte */ 666 { 667 size_t p = fuzz_below (rng, (uint32_t) len + 1); 668 size_t n = 1 + fuzz_below (rng, 64); 669 uint8_t v = fuzz_byte (rng); 670 671 if (len + n > cap) 672 n = cap - len; 673 if (0 == n) 674 break; 675 memmove (buf + p + n, buf + p, len - p); 676 memset (buf + p, v, n); 677 len += n; 678 break; 679 } 680 case 6: /* duplicate a chunk */ 681 { 682 size_t p = fuzz_below (rng, (uint32_t) len); 683 size_t n = 1 + fuzz_below (rng, (uint32_t) (len - p)); 684 size_t d = fuzz_below (rng, (uint32_t) len + 1); 685 686 if (len + n > cap) 687 n = cap - len; 688 if (0 == n) 689 break; 690 memmove (buf + d + n, buf + d, len - d); 691 memmove (buf + d, buf + ((p >= d) ? (p + n) : p), n); 692 len += n; 693 break; 694 } 695 case 7: /* insert an interesting token */ 696 { 697 const char *s = 698 fuzz_interesting_str[fuzz_below (rng, 699 (uint32_t) 700 (sizeof (fuzz_interesting_str) 701 / sizeof (fuzz_interesting_str[0])))]; 702 size_t n = strlen (s); 703 size_t p = fuzz_below (rng, (uint32_t) len + 1); 704 705 if (len + n > cap) 706 break; 707 memmove (buf + p + n, buf + p, len - p); 708 memcpy (buf + p, s, n); 709 len += n; 710 break; 711 } 712 case 8: /* swap two bytes */ 713 { 714 size_t a = fuzz_below (rng, (uint32_t) len); 715 size_t b = fuzz_below (rng, (uint32_t) len); 716 uint8_t t = buf[a]; 717 718 buf[a] = buf[b]; 719 buf[b] = t; 720 break; 721 } 722 case 9: /* overwrite a 16/32 bit field with an 723 extreme value; length fields are where 724 the parser bugs are */ 725 { 726 static const uint32_t vals[] = { 727 0, 1, 2, 0x7F, 0x80, 0xFF, 0x100, 0x7FFF, 0x8000, 0xFFFF, 728 0x10000, 0x7FFFFFFF, 0x80000000, 0xFFFFFFFF 729 }; 730 size_t p = fuzz_below (rng, (uint32_t) len); 731 unsigned int n = (0 == fuzz_below (rng, 2)) ? 2 : 4; 732 uint32_t v = vals[fuzz_below (rng, 733 (uint32_t) (sizeof (vals) 734 / sizeof (vals[0])))]; 735 unsigned int k; 736 737 if (p + n > len) 738 break; 739 /* Keep both loops braced. uncrustify 0.78 spins forever on an 740 unbraced if/else whose body is an unbraced for whose statement 741 carries a trailing comment, which hangs the pre-commit hook. */ 742 if (0 == fuzz_below (rng, 2)) 743 { 744 /* little endian */ 745 for (k = 0; k < n; k++) 746 buf[p + k] = (uint8_t) (v >> (8 * k)); 747 } 748 else 749 { 750 /* big endian */ 751 for (k = 0; k < n; k++) 752 buf[p + k] = (uint8_t) (v >> (8 * (n - 1 - k))); 753 } 754 break; 755 } 756 default: /* truncate */ 757 len = 1 + fuzz_below (rng, (uint32_t) len); 758 break; 759 } 760 return len; 761 } 762 763 764 /* ------------------------------------------------------------------ */ 765 /* Standalone driver */ 766 /* ------------------------------------------------------------------ */ 767 768 static int 769 fuzz_run_file (const char *path) 770 { 771 FILE *f; 772 uint8_t *buf; 773 size_t n; 774 775 f = fopen (path, "rb"); 776 if (NULL == f) 777 { 778 fprintf (stderr, 779 "%s: cannot open '%s': %s\n", 780 FUZZ_HARNESS_NAME, 781 path, 782 strerror (errno)); 783 return 1; 784 } 785 buf = (uint8_t *) malloc (FUZZ_MAX_INPUT); 786 if (NULL == buf) 787 { 788 (void) fclose (f); 789 return 1; 790 } 791 n = fread (buf, 1, FUZZ_MAX_INPUT, f); 792 (void) fclose (f); 793 fuzz_pristine = 1; 794 fuzz_set_current (buf, n, path); 795 alarm (FUZZ_DEFAULT_TIMEOUT); 796 (void) LLVMFuzzerTestOneInput (buf, n); 797 alarm (0); 798 fuzz_cur_input = NULL; /* nothing to dump for an at-exit report */ 799 free (buf); 800 return 0; 801 } 802 803 804 static int 805 fuzz_run_corpus_dir (const char *dir) 806 { 807 DIR *d; 808 struct dirent *de; 809 char path[1024]; 810 int ret = 0; 811 unsigned int cnt = 0; 812 813 d = opendir (dir); 814 if (NULL == d) 815 { 816 fprintf (stderr, 817 "%s: cannot open corpus dir '%s': %s\n", 818 FUZZ_HARNESS_NAME, 819 dir, 820 strerror (errno)); 821 return 1; 822 } 823 while (NULL != (de = readdir (d))) 824 { 825 struct stat sb; 826 827 if ('.' == de->d_name[0]) 828 continue; 829 (void) snprintf (path, sizeof (path), "%s/%s", dir, de->d_name); 830 if ( (0 != stat (path, &sb)) || 831 (! S_ISREG (sb.st_mode)) ) 832 continue; 833 fuzz_cur_iter = cnt++; 834 ret |= fuzz_run_file (path); 835 } 836 (void) closedir (d); 837 printf ("%s: replayed %u corpus file(s) from %s\n", 838 FUZZ_HARNESS_NAME, cnt, dir); 839 return ret; 840 } 841 842 843 static void 844 fuzz_usage (const char *argv0) 845 { 846 printf ( 847 "Usage: %s [OPTIONS] [FILE...]\n" 848 "\n" 849 "In-process fuzzing harness '%s' for GNU libextractor.\n" 850 "\n" 851 " --iterations=N number of generate/mutate iterations (default %d)\n" 852 " --seed=N PRNG seed; runs are fully reproducible (default 1)\n" 853 " --corpus-dir=DIR replay every regular file in DIR and exit\n" 854 " --file=PATH replay a single input and exit (crash reproduction)\n" 855 " --crash-dir=DIR where to write reproducers (default 'crashes')\n" 856 " --timeout=SEC per-iteration watchdog (default %d, 0 disables)\n" 857 " --write-corpus=DIR write the built-in seed corpus to DIR and exit\n" 858 " --skip-seeds do not replay the built-in seed corpus first\n" 859 " --verbose be chatty about what the harness does\n" 860 " --help this text\n" 861 "\n" 862 "Environment: LE_FUZZ_ITERATIONS, LE_FUZZ_SEED, LE_FUZZ_TIMEOUT,\n" 863 " LE_FUZZ_CRASH_DIR, LE_FUZZ_VERBOSE, LE_FUZZ_SKIP_SEEDS\n" 864 "\n" 865 "Bare FILE arguments are equivalent to --file=FILE (libFuzzer-style).\n", 866 argv0, FUZZ_HARNESS_NAME, 867 (int) FUZZ_DEFAULT_ITERATIONS, (int) FUZZ_DEFAULT_TIMEOUT); 868 } 869 870 871 static int 872 fuzz_write_corpus (const char *dir) 873 { 874 size_t i; 875 size_t n = fuzz_seed_count (); 876 877 if ( (0 != mkdir (dir, 0755)) && 878 (EEXIST != errno) ) 879 { 880 fprintf (stderr, "%s: mkdir '%s': %s\n", 881 FUZZ_HARNESS_NAME, dir, strerror (errno)); 882 return 1; 883 } 884 for (i = 0; i < n; i++) 885 { 886 char path[1024]; 887 size_t len; 888 const uint8_t *s = fuzz_seed_get (i, &len); 889 FILE *f; 890 891 (void) snprintf (path, sizeof (path), "%s/%s-%03u.bin", 892 dir, FUZZ_HARNESS_NAME, (unsigned int) i); 893 f = fopen (path, "wb"); 894 if (NULL == f) 895 { 896 fprintf (stderr, "%s: fopen '%s': %s\n", 897 FUZZ_HARNESS_NAME, path, strerror (errno)); 898 return 1; 899 } 900 if ( (0 != len) && 901 (len != fwrite (s, 1, len, f)) ) 902 { 903 (void) fclose (f); 904 return 1; 905 } 906 (void) fclose (f); 907 } 908 printf ("%s: wrote %u seed(s) to %s\n", 909 FUZZ_HARNESS_NAME, (unsigned int) n, dir); 910 return 0; 911 } 912 913 914 int 915 main (int argc, char *const *argv) 916 { 917 uint64_t iterations = FUZZ_DEFAULT_ITERATIONS; 918 uint64_t seed = 1; 919 unsigned int timeout = FUZZ_DEFAULT_TIMEOUT; 920 const char *corpus_dir = NULL; 921 const char *single_file = NULL; 922 const char *write_corpus = NULL; 923 struct fuzz_rng rng; 924 uint8_t *buf; 925 uint64_t i; 926 int j; 927 const char *e; 928 int ret = 0; 929 930 iterations = fuzz_env_ulong ("LE_FUZZ_ITERATIONS", iterations); 931 seed = fuzz_env_ulong ("LE_FUZZ_SEED", seed); 932 timeout = (unsigned int) fuzz_env_ulong ("LE_FUZZ_TIMEOUT", timeout); 933 e = getenv ("LE_FUZZ_CRASH_DIR"); 934 if (NULL != e) 935 fuzz_crash_dir = e; 936 fuzz_verbose = (0 != fuzz_env_ulong ("LE_FUZZ_VERBOSE", 0)); 937 fuzz_skip_seeds = (0 != fuzz_env_ulong ("LE_FUZZ_SKIP_SEEDS", 0)); 938 939 for (j = 1; j < argc; j++) 940 { 941 const char *a = argv[j]; 942 943 if (0 == strncmp (a, "--iterations=", 13)) 944 iterations = strtoull (a + 13, NULL, 10); 945 else if (0 == strncmp (a, "--seed=", 7)) 946 seed = strtoull (a + 7, NULL, 10); 947 else if (0 == strncmp (a, "--corpus-dir=", 13)) 948 corpus_dir = a + 13; 949 else if (0 == strncmp (a, "--file=", 7)) 950 single_file = a + 7; 951 else if (0 == strncmp (a, "--crash-dir=", 12)) 952 fuzz_crash_dir = a + 12; 953 else if (0 == strncmp (a, "--timeout=", 10)) 954 timeout = (unsigned int) strtoul (a + 10, NULL, 10); 955 else if (0 == strncmp (a, "--write-corpus=", 15)) 956 write_corpus = a + 15; 957 else if (0 == strcmp (a, "--skip-seeds")) 958 fuzz_skip_seeds = 1; 959 else if (0 == strcmp (a, "--verbose")) 960 fuzz_verbose = 1; 961 else if ( (0 == strcmp (a, "--help")) || 962 (0 == strcmp (a, "-h")) ) 963 { 964 fuzz_usage (argv[0]); 965 return 0; 966 } 967 else if ('-' == a[0]) 968 { 969 fprintf (stderr, "%s: unknown option '%s'\n", FUZZ_HARNESS_NAME, a); 970 fuzz_usage (argv[0]); 971 return 2; 972 } 973 else 974 single_file = a; 975 } 976 977 fuzz_cur_seed = seed; 978 fuzz_install_handlers (); 979 980 if (NULL != write_corpus) 981 return fuzz_write_corpus (write_corpus); 982 983 if (NULL != single_file) 984 { 985 printf ("%s: replaying %s\n", FUZZ_HARNESS_NAME, single_file); 986 ret = fuzz_run_file (single_file); 987 printf ("%s: replay finished without a finding\n", FUZZ_HARNESS_NAME); 988 return ret; 989 } 990 if (NULL != corpus_dir) 991 return fuzz_run_corpus_dir (corpus_dir); 992 993 buf = (uint8_t *) malloc (FUZZ_MAX_INPUT); 994 if (NULL == buf) 995 return 1; 996 fuzz_rng_seed (&rng, seed); 997 printf ("%s: seed=%llu iterations=%llu\n", 998 FUZZ_HARNESS_NAME, 999 (unsigned long long) seed, 1000 (unsigned long long) iterations); 1001 fflush (stdout); 1002 1003 for (i = 0; i < iterations; i++) 1004 { 1005 size_t len; 1006 const char *tag; 1007 uint32_t mode; 1008 1009 fuzz_cur_iter = i; 1010 if ( (! fuzz_skip_seeds) && 1011 (i < fuzz_seed_count ()) ) 1012 { 1013 size_t sl; 1014 const uint8_t *s = fuzz_seed_get ((size_t) i, &sl); 1015 1016 if (sl > FUZZ_MAX_INPUT) 1017 sl = FUZZ_MAX_INPUT; 1018 memcpy (buf, s, sl); 1019 len = sl; 1020 fuzz_pristine = 1; 1021 tag = "builtin-seed"; 1022 } 1023 else 1024 { 1025 mode = fuzz_below (&rng, 100); 1026 if (mode < 45) 1027 { 1028 fuzz_pristine = 1; 1029 len = fuzz_generate (&rng, buf, FUZZ_MAX_INPUT); 1030 tag = "generated"; 1031 } 1032 else 1033 { 1034 unsigned int k; 1035 unsigned int nmut; 1036 1037 if (mode < 80) 1038 { 1039 len = fuzz_generate (&rng, buf, FUZZ_MAX_INPUT); 1040 tag = "generated+mutated"; 1041 } 1042 else 1043 { 1044 size_t sl; 1045 const uint8_t *s; 1046 1047 if (0 == fuzz_seed_count ()) 1048 { 1049 len = fuzz_generate (&rng, buf, FUZZ_MAX_INPUT); 1050 } 1051 else 1052 { 1053 s = fuzz_seed_get (fuzz_below (&rng, 1054 (uint32_t) fuzz_seed_count ()), 1055 &sl); 1056 if (sl > FUZZ_MAX_INPUT) 1057 sl = FUZZ_MAX_INPUT; 1058 memcpy (buf, s, sl); 1059 len = sl; 1060 } 1061 tag = "seed+mutated"; 1062 } 1063 fuzz_pristine = 0; 1064 nmut = 1 + fuzz_below (&rng, 8); 1065 for (k = 0; k < nmut; k++) 1066 len = fuzz_mutate_once (&rng, buf, len, FUZZ_MAX_INPUT); 1067 } 1068 } 1069 fuzz_set_current (buf, len, tag); 1070 if (0 != timeout) 1071 alarm (timeout); 1072 (void) LLVMFuzzerTestOneInput (buf, len); 1073 if (0 != timeout) 1074 alarm (0); 1075 } 1076 fuzz_cur_input = NULL; /* nothing to dump for an at-exit report */ 1077 free (buf); 1078 printf ("%s: %llu iterations completed, no findings\n", 1079 FUZZ_HARNESS_NAME, 1080 (unsigned long long) iterations); 1081 return ret; 1082 } 1083 1084 1085 #endif /* ! FUZZ_NO_MAIN */ 1086 1087 #endif /* LE_FUZZ_COMMON_H */