fuzz_common.h (29558B)
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 free (buf); 799 return 0; 800 } 801 802 803 static int 804 fuzz_run_corpus_dir (const char *dir) 805 { 806 DIR *d; 807 struct dirent *de; 808 char path[1024]; 809 int ret = 0; 810 unsigned int cnt = 0; 811 812 d = opendir (dir); 813 if (NULL == d) 814 { 815 fprintf (stderr, 816 "%s: cannot open corpus dir '%s': %s\n", 817 FUZZ_HARNESS_NAME, 818 dir, 819 strerror (errno)); 820 return 1; 821 } 822 while (NULL != (de = readdir (d))) 823 { 824 struct stat sb; 825 826 if ('.' == de->d_name[0]) 827 continue; 828 (void) snprintf (path, sizeof (path), "%s/%s", dir, de->d_name); 829 if ( (0 != stat (path, &sb)) || 830 (! S_ISREG (sb.st_mode)) ) 831 continue; 832 fuzz_cur_iter = cnt++; 833 ret |= fuzz_run_file (path); 834 } 835 (void) closedir (d); 836 printf ("%s: replayed %u corpus file(s) from %s\n", 837 FUZZ_HARNESS_NAME, cnt, dir); 838 return ret; 839 } 840 841 842 static void 843 fuzz_usage (const char *argv0) 844 { 845 printf ( 846 "Usage: %s [OPTIONS] [FILE...]\n" 847 "\n" 848 "In-process fuzzing harness '%s' for GNU libextractor.\n" 849 "\n" 850 " --iterations=N number of generate/mutate iterations (default %d)\n" 851 " --seed=N PRNG seed; runs are fully reproducible (default 1)\n" 852 " --corpus-dir=DIR replay every regular file in DIR and exit\n" 853 " --file=PATH replay a single input and exit (crash reproduction)\n" 854 " --crash-dir=DIR where to write reproducers (default 'crashes')\n" 855 " --timeout=SEC per-iteration watchdog (default %d, 0 disables)\n" 856 " --write-corpus=DIR write the built-in seed corpus to DIR and exit\n" 857 " --skip-seeds do not replay the built-in seed corpus first\n" 858 " --verbose be chatty about what the harness does\n" 859 " --help this text\n" 860 "\n" 861 "Environment: LE_FUZZ_ITERATIONS, LE_FUZZ_SEED, LE_FUZZ_TIMEOUT,\n" 862 " LE_FUZZ_CRASH_DIR, LE_FUZZ_VERBOSE, LE_FUZZ_SKIP_SEEDS\n" 863 "\n" 864 "Bare FILE arguments are equivalent to --file=FILE (libFuzzer-style).\n", 865 argv0, FUZZ_HARNESS_NAME, 866 (int) FUZZ_DEFAULT_ITERATIONS, (int) FUZZ_DEFAULT_TIMEOUT); 867 } 868 869 870 static int 871 fuzz_write_corpus (const char *dir) 872 { 873 size_t i; 874 size_t n = fuzz_seed_count (); 875 876 if ( (0 != mkdir (dir, 0755)) && 877 (EEXIST != errno) ) 878 { 879 fprintf (stderr, "%s: mkdir '%s': %s\n", 880 FUZZ_HARNESS_NAME, dir, strerror (errno)); 881 return 1; 882 } 883 for (i = 0; i < n; i++) 884 { 885 char path[1024]; 886 size_t len; 887 const uint8_t *s = fuzz_seed_get (i, &len); 888 FILE *f; 889 890 (void) snprintf (path, sizeof (path), "%s/%s-%03u.bin", 891 dir, FUZZ_HARNESS_NAME, (unsigned int) i); 892 f = fopen (path, "wb"); 893 if (NULL == f) 894 { 895 fprintf (stderr, "%s: fopen '%s': %s\n", 896 FUZZ_HARNESS_NAME, path, strerror (errno)); 897 return 1; 898 } 899 if ( (0 != len) && 900 (len != fwrite (s, 1, len, f)) ) 901 { 902 (void) fclose (f); 903 return 1; 904 } 905 (void) fclose (f); 906 } 907 printf ("%s: wrote %u seed(s) to %s\n", 908 FUZZ_HARNESS_NAME, (unsigned int) n, dir); 909 return 0; 910 } 911 912 913 int 914 main (int argc, char *const *argv) 915 { 916 uint64_t iterations = FUZZ_DEFAULT_ITERATIONS; 917 uint64_t seed = 1; 918 unsigned int timeout = FUZZ_DEFAULT_TIMEOUT; 919 const char *corpus_dir = NULL; 920 const char *single_file = NULL; 921 const char *write_corpus = NULL; 922 struct fuzz_rng rng; 923 uint8_t *buf; 924 uint64_t i; 925 int j; 926 const char *e; 927 int ret = 0; 928 929 iterations = fuzz_env_ulong ("LE_FUZZ_ITERATIONS", iterations); 930 seed = fuzz_env_ulong ("LE_FUZZ_SEED", seed); 931 timeout = (unsigned int) fuzz_env_ulong ("LE_FUZZ_TIMEOUT", timeout); 932 e = getenv ("LE_FUZZ_CRASH_DIR"); 933 if (NULL != e) 934 fuzz_crash_dir = e; 935 fuzz_verbose = (0 != fuzz_env_ulong ("LE_FUZZ_VERBOSE", 0)); 936 fuzz_skip_seeds = (0 != fuzz_env_ulong ("LE_FUZZ_SKIP_SEEDS", 0)); 937 938 for (j = 1; j < argc; j++) 939 { 940 const char *a = argv[j]; 941 942 if (0 == strncmp (a, "--iterations=", 13)) 943 iterations = strtoull (a + 13, NULL, 10); 944 else if (0 == strncmp (a, "--seed=", 7)) 945 seed = strtoull (a + 7, NULL, 10); 946 else if (0 == strncmp (a, "--corpus-dir=", 13)) 947 corpus_dir = a + 13; 948 else if (0 == strncmp (a, "--file=", 7)) 949 single_file = a + 7; 950 else if (0 == strncmp (a, "--crash-dir=", 12)) 951 fuzz_crash_dir = a + 12; 952 else if (0 == strncmp (a, "--timeout=", 10)) 953 timeout = (unsigned int) strtoul (a + 10, NULL, 10); 954 else if (0 == strncmp (a, "--write-corpus=", 15)) 955 write_corpus = a + 15; 956 else if (0 == strcmp (a, "--skip-seeds")) 957 fuzz_skip_seeds = 1; 958 else if (0 == strcmp (a, "--verbose")) 959 fuzz_verbose = 1; 960 else if ( (0 == strcmp (a, "--help")) || 961 (0 == strcmp (a, "-h")) ) 962 { 963 fuzz_usage (argv[0]); 964 return 0; 965 } 966 else if ('-' == a[0]) 967 { 968 fprintf (stderr, "%s: unknown option '%s'\n", FUZZ_HARNESS_NAME, a); 969 fuzz_usage (argv[0]); 970 return 2; 971 } 972 else 973 single_file = a; 974 } 975 976 fuzz_cur_seed = seed; 977 fuzz_install_handlers (); 978 979 if (NULL != write_corpus) 980 return fuzz_write_corpus (write_corpus); 981 982 if (NULL != single_file) 983 { 984 printf ("%s: replaying %s\n", FUZZ_HARNESS_NAME, single_file); 985 ret = fuzz_run_file (single_file); 986 printf ("%s: replay finished without a finding\n", FUZZ_HARNESS_NAME); 987 return ret; 988 } 989 if (NULL != corpus_dir) 990 return fuzz_run_corpus_dir (corpus_dir); 991 992 buf = (uint8_t *) malloc (FUZZ_MAX_INPUT); 993 if (NULL == buf) 994 return 1; 995 fuzz_rng_seed (&rng, seed); 996 printf ("%s: seed=%llu iterations=%llu\n", 997 FUZZ_HARNESS_NAME, 998 (unsigned long long) seed, 999 (unsigned long long) iterations); 1000 fflush (stdout); 1001 1002 for (i = 0; i < iterations; i++) 1003 { 1004 size_t len; 1005 const char *tag; 1006 uint32_t mode; 1007 1008 fuzz_cur_iter = i; 1009 if ( (! fuzz_skip_seeds) && 1010 (i < fuzz_seed_count ()) ) 1011 { 1012 size_t sl; 1013 const uint8_t *s = fuzz_seed_get ((size_t) i, &sl); 1014 1015 if (sl > FUZZ_MAX_INPUT) 1016 sl = FUZZ_MAX_INPUT; 1017 memcpy (buf, s, sl); 1018 len = sl; 1019 fuzz_pristine = 1; 1020 tag = "builtin-seed"; 1021 } 1022 else 1023 { 1024 mode = fuzz_below (&rng, 100); 1025 if (mode < 45) 1026 { 1027 fuzz_pristine = 1; 1028 len = fuzz_generate (&rng, buf, FUZZ_MAX_INPUT); 1029 tag = "generated"; 1030 } 1031 else 1032 { 1033 unsigned int k; 1034 unsigned int nmut; 1035 1036 if (mode < 80) 1037 { 1038 len = fuzz_generate (&rng, buf, FUZZ_MAX_INPUT); 1039 tag = "generated+mutated"; 1040 } 1041 else 1042 { 1043 size_t sl; 1044 const uint8_t *s; 1045 1046 if (0 == fuzz_seed_count ()) 1047 { 1048 len = fuzz_generate (&rng, buf, FUZZ_MAX_INPUT); 1049 } 1050 else 1051 { 1052 s = fuzz_seed_get (fuzz_below (&rng, 1053 (uint32_t) fuzz_seed_count ()), 1054 &sl); 1055 if (sl > FUZZ_MAX_INPUT) 1056 sl = FUZZ_MAX_INPUT; 1057 memcpy (buf, s, sl); 1058 len = sl; 1059 } 1060 tag = "seed+mutated"; 1061 } 1062 fuzz_pristine = 0; 1063 nmut = 1 + fuzz_below (&rng, 8); 1064 for (k = 0; k < nmut; k++) 1065 len = fuzz_mutate_once (&rng, buf, len, FUZZ_MAX_INPUT); 1066 } 1067 } 1068 fuzz_set_current (buf, len, tag); 1069 if (0 != timeout) 1070 alarm (timeout); 1071 (void) LLVMFuzzerTestOneInput (buf, len); 1072 if (0 != timeout) 1073 alarm (0); 1074 } 1075 free (buf); 1076 printf ("%s: %llu iterations completed, no findings\n", 1077 FUZZ_HARNESS_NAME, 1078 (unsigned long long) iterations); 1079 return ret; 1080 } 1081 1082 1083 #endif /* ! FUZZ_NO_MAIN */ 1084 1085 #endif /* LE_FUZZ_COMMON_H */