Seed corpus for the src/fuzz harnesses
======================================

`<harness>-NN.bin` is the built-in seed corpus of that harness, dumped
to disk.  Regenerate at any time with

    ./fuzz_<harness> --write-corpus=<this directory>

or, from the build tree,

    make -C src/fuzz refresh-corpus

Replay everything (this is what a CI regression run should do):

    make -C src/fuzz check-corpus
    # or, per harness:
    ./fuzz_request --corpus-dir=src/fuzz/corpus

Files belonging to another harness are simply uninteresting inputs for
the harness that reads them, so pointing every harness at the whole
directory is fine and gives some extra cross-pollination.

The set is kept minimal: every seed here adds coverage that no other
seed provides.  After changing a seed table, re-check that with

    <target> -merge=1 <empty dir> <this directory>

from an OSS-Fuzz style build (contrib/oss-fuzz/build.sh); a seed the
merge does not copy across is redundant and should be deleted from the
table rather than left in the corpus.  Note that fuzz_request is
mildly non-deterministic -- MHD timestamps its nonces and its
connection timeouts -- so a single merge decision worth ~0.1% of the
regions is noise; only drop a seed that several independent merges
agree on.


fuzz_request seeds
------------------

  00  content-length-body              body oracle, Content-Length
  01  chunked-with-extensions          body oracle, chunk extensions
                                       -> chunk-extension CRLF bug
  02  chunked-split                    body oracle, chunk boundary split
                                       across two send() calls
  03  small-pool-trailing-query-arg    128..512 byte connection pool, no
                                       header lines, "?novalue"
                                       -> read-buffer shift-back bug
  04  small-pool-trailing-query-arg-2  same with "?a=1&b"
  05  digest-unknown-algorithm         algorithm=BOGUS
                                       -> MHD_DIGEST_AUTH_ALGO3_INVALID
                                          MHD_PANIC()
  06  digest-overlong-response         401 challenge, then a replay on a
                                       fresh connection with the
                                       harvested nonce and a 128 hex
                                       digit response=
                                       -> stack buffer overflow
  07  digest-userhash                  userhash=true with a 128 char
                                       username
  08  basic-auth                       Authorization: Basic
  09  multipart-post                   chunked multipart/form-data
  10  urlencoded-post                  application/x-www-form-urlencoded
  11  folded-header                    obs-fold continuation line
  12  pipelined                        two requests in one segment

Seeds 00-12 leave the API-selection block (bytes 4-9, see section 2.2.1
of ../README) zero and only drive the request parser.  Seeds 13-39 each
switch on one area of it; without them those entry points are reachable
only by guessing six configuration bytes.

  13  ext-callback-chunked             MHD_SIZE_UNKNOWN content reader,
                                       response headers and a trailer
  14  ext-callback-error               known-length reader that fails
                                       part way through
  15  ext-fd-response                  MHD_create_response_from_fd()
  16  ext-fd-at-offset-response        ..._from_fd_at_offset()
  17  ext-pipe-response                ..._from_pipe()
  18  ext-iovec-response               ..._from_iovec()
  19  ext-empty-response-hdrapi        ..._empty() plus get/del header
                                       and MHD_set_response_options()
  20  ext-digest-check-digest3         pre-computed userdigest path
  21  ext-digest-v1-wrappers           MHD_digest_auth_check()
  22  ext-digest-request-info          MHD_digest_auth_get_request_info3()
  23  ext-external-event-loop          MHD_get_fdset2()/run_from_select2()
                                       plus the timeout accessors
  24  ext-external-event-loop-v1       the v1 fdset/run_from_select pair
  25  ext-connection-api               every introspection accessor
  26  ext-suspend-resume               suspend, resume from the pump loop
  27  ext-suspend-two-connections      two connections parked at once
  28  ext-upgrade                      101 + MHD_upgrade_action()
  29  ext-buffer-persistent            buffer/MHD_RESPMEM_PERSISTENT
  30  ext-buffer-free-callback         ..._with_free_callback()
  31  ext-from-data                    MHD_create_response_from_data()
  32  ext-fd64-response                ..._from_fd64()
  33  ext-basic-auth-v1                MHD_basic_auth_get_username_password()
  34  ext-basic-auth-challenge         MHD_queue_basic_auth_fail_response()
  35  ext-basic-auth-challenge-utf8    ..._response3() with UTF-8 charset
  36  ext-digest-check-digest-v1       MHD_digest_auth_check_digest()
  37  ext-digest-check-digest2         ..._digest2()  (never with
                                       MHD_DIGEST_ALG_AUTO, see K8)
  38  ext-digest-get-username-v1       MHD_digest_auth_get_username()
  39  ext-digest-get-username3         ..._username3()

fuzz_str seeds
--------------

  Cover MHD_hex_to_bin (including the 128 character input that
  digestauth.c used to allow into a 32 byte buffer), MHD_bin_to_hex[_z],
  the percent-decoders (strict, lenient, in place), MHD_str_unquote,
  MHD_str_quote, MHD_base64_to_bin_n, MHD_str[x]_to_uint64_n_ and the
  token helpers.

fuzz_auth_header seeds
----------------------

  Well-formed and broken Digest parameter lists (unknown algorithm,
  extended `username*` notation, unterminated quoted strings, empty
  parameter list, over-long values) plus Basic `token68` variants, and
  -- with bit 0x02 of byte 0 -- the connection-less digest helpers at
  several algorithms and several deliberately undersized output
  buffers.

fuzz_postprocessor seeds
------------------------

  urlencoded bodies with broken percent escapes, multipart bodies with
  ordinary, degenerate ("-") and quote-containing boundaries, a
  multipart Content-Type without a boundary, and a non-POST
  Content-Type.


known-findings/
---------------

Reproducers for the issues that this suite found in MHD itself; see
section 6 of ../README, which records the status of each.  All of them
are fixed on master, so all of them replay clean, and `check-corpus`
replays this directory too.

A file is named `K<n>-<harness>-<what>.bin` for the harness that found
it; the ones without a harness in the name predate that convention and
are all fuzz_request inputs.  `contrib/oss-fuzz/make_seed_corpus.sh`
routes each into the seed corpus of its own harness on that basis.

They are kept as a separate, explicitly named set rather than being
folded into the main corpus because the files there are *generated*: a
hand-written input would be clobbered or renumbered by the next
`refresh-corpus`.  Replay one with

    ./fuzz_request --file=src/fuzz/corpus/known-findings/K1-digest-empty-realm.bin

  K1-digest-empty-realm.bin          digestauth.c is_param_equal()
                                     mhd_assert (0 != param->value.len)
  K2-chunkext-no-space.bin           connection.c handle_recv_no_space()
  K3-chunkext-stop-with-error.bin    connection.c
                                     transmit_error_response_len()
  K4a-wsp-first-header.bin           connection.c get_req_header(),
                                     CLIENT_DISCIPLINE_LVL <= -1
  K4b-empty-header-name.bin          connection.c get_req_header(),
                                     CLIENT_DISCIPLINE_LVL <= -2
  K5-bare-cr-keep.bin                connection.c get_req_header(),
                                     CLIENT_DISCIPLINE_LVL == -3
  K6-nonce-length-collision.bin      digestauth.c check_nonce_nc()
                                     mhd_assert (0 == nn->nonce[noncelen])
  K7-upgrade-after-must-close.bin    connection.c build_header_response()
                                     mhd_assert (upgrade -> MUST_UPGRADE);
                                     a single request carrying both
                                     Content-Length and chunked
                                     Transfer-Encoding, then an upgrade
  K9-fuzz_postprocessor-             postprocessor.c post_process_multipart()
    nested-boundary-leak.bin         leaked the previous nested boundary
                                     on every extra nested
                                     multipart/mixed part
  K14-fuzz_eventloop-                daemon.c call_handlers() asserted a
    force-close-not-closed.bin       post-condition that
                                     MHD_connection_handle_read() does
                                     not guarantee when the read buffer
                                     is full
                                     *** still open, see ../../../patches ***
                                     Only fires on an --enable-asserts
                                     *ASan* build: the redzones change
                                     the pool arithmetic that reaches it.

These files are *not* regenerated by `--write-corpus`; they are edited by
hand.  When the input format changes they have to be migrated, and the
migration has to be checked -- replay each one with `--verbose` before
and after and confirm the daemon, handler, body and challenge counts are
unchanged.  The last such change made the API-selection block
unconditional, which is six zero bytes inserted at offset 4 for K1-K6.

K8 has no reproducer here: its trigger is an argument the application
chooses, not network input, so the harness never performs it.  See
section 6 of ../README for how to reach it by hand.

A reproducer added while its finding is still open will make
`make check-corpus` fail, which is intended -- that is what a regression
test for an unfixed bug does.  `contrib/oss-fuzz/make_seed_corpus.sh`
skips such a file so that ClusterFuzz does not spend every run
rediscovering it; the rule it uses is whether `patches/$ID.diff` exists.
