libmicrohttpd

HTTP/1.x server C library (MHD 1.x, stable)
Log | Files | Refs | Submodules | README | LICENSE

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 */