libmicrohttpd

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

README (10219B)


      1 Seed corpus for the src/fuzz harnesses
      2 ======================================
      3 
      4 `<harness>-NN.bin` is the built-in seed corpus of that harness, dumped
      5 to disk.  Regenerate at any time with
      6 
      7     ./fuzz_<harness> --write-corpus=<this directory>
      8 
      9 or, from the build tree,
     10 
     11     make -C src/fuzz refresh-corpus
     12 
     13 Replay everything (this is what a CI regression run should do):
     14 
     15     make -C src/fuzz check-corpus
     16     # or, per harness:
     17     ./fuzz_request --corpus-dir=src/fuzz/corpus
     18 
     19 Files belonging to another harness are simply uninteresting inputs for
     20 the harness that reads them, so pointing every harness at the whole
     21 directory is fine and gives some extra cross-pollination.
     22 
     23 The set is kept minimal: every seed here adds coverage that no other
     24 seed provides.  After changing a seed table, re-check that with
     25 
     26     <target> -merge=1 <empty dir> <this directory>
     27 
     28 from an OSS-Fuzz style build (contrib/oss-fuzz/build.sh); a seed the
     29 merge does not copy across is redundant and should be deleted from the
     30 table rather than left in the corpus.  Note that fuzz_request is
     31 mildly non-deterministic -- MHD timestamps its nonces and its
     32 connection timeouts -- so a single merge decision worth ~0.1% of the
     33 regions is noise; only drop a seed that several independent merges
     34 agree on.
     35 
     36 
     37 fuzz_request seeds
     38 ------------------
     39 
     40   00  content-length-body              body oracle, Content-Length
     41   01  chunked-with-extensions          body oracle, chunk extensions
     42                                        -> chunk-extension CRLF bug
     43   02  chunked-split                    body oracle, chunk boundary split
     44                                        across two send() calls
     45   03  small-pool-trailing-query-arg    128..512 byte connection pool, no
     46                                        header lines, "?novalue"
     47                                        -> read-buffer shift-back bug
     48   04  small-pool-trailing-query-arg-2  same with "?a=1&b"
     49   05  digest-unknown-algorithm         algorithm=BOGUS
     50                                        -> MHD_DIGEST_AUTH_ALGO3_INVALID
     51                                           MHD_PANIC()
     52   06  digest-overlong-response         401 challenge, then a replay on a
     53                                        fresh connection with the
     54                                        harvested nonce and a 128 hex
     55                                        digit response=
     56                                        -> stack buffer overflow
     57   07  digest-userhash                  userhash=true with a 128 char
     58                                        username
     59   08  basic-auth                       Authorization: Basic
     60   09  multipart-post                   chunked multipart/form-data
     61   10  urlencoded-post                  application/x-www-form-urlencoded
     62   11  folded-header                    obs-fold continuation line
     63   12  pipelined                        two requests in one segment
     64 
     65 Seeds 00-12 leave the API-selection block (bytes 4-9, see section 2.2.1
     66 of ../README) zero and only drive the request parser.  Seeds 13-39 each
     67 switch on one area of it; without them those entry points are reachable
     68 only by guessing six configuration bytes.
     69 
     70   13  ext-callback-chunked             MHD_SIZE_UNKNOWN content reader,
     71                                        response headers and a trailer
     72   14  ext-callback-error               known-length reader that fails
     73                                        part way through
     74   15  ext-fd-response                  MHD_create_response_from_fd()
     75   16  ext-fd-at-offset-response        ..._from_fd_at_offset()
     76   17  ext-pipe-response                ..._from_pipe()
     77   18  ext-iovec-response               ..._from_iovec()
     78   19  ext-empty-response-hdrapi        ..._empty() plus get/del header
     79                                        and MHD_set_response_options()
     80   20  ext-digest-check-digest3         pre-computed userdigest path
     81   21  ext-digest-v1-wrappers           MHD_digest_auth_check()
     82   22  ext-digest-request-info          MHD_digest_auth_get_request_info3()
     83   23  ext-external-event-loop          MHD_get_fdset2()/run_from_select2()
     84                                        plus the timeout accessors
     85   24  ext-external-event-loop-v1       the v1 fdset/run_from_select pair
     86   25  ext-connection-api               every introspection accessor
     87   26  ext-suspend-resume               suspend, resume from the pump loop
     88   27  ext-suspend-two-connections      two connections parked at once
     89   28  ext-upgrade                      101 + MHD_upgrade_action()
     90   29  ext-buffer-persistent            buffer/MHD_RESPMEM_PERSISTENT
     91   30  ext-buffer-free-callback         ..._with_free_callback()
     92   31  ext-from-data                    MHD_create_response_from_data()
     93   32  ext-fd64-response                ..._from_fd64()
     94   33  ext-basic-auth-v1                MHD_basic_auth_get_username_password()
     95   34  ext-basic-auth-challenge         MHD_queue_basic_auth_fail_response()
     96   35  ext-basic-auth-challenge-utf8    ..._response3() with UTF-8 charset
     97   36  ext-digest-check-digest-v1       MHD_digest_auth_check_digest()
     98   37  ext-digest-check-digest2         ..._digest2()  (never with
     99                                        MHD_DIGEST_ALG_AUTO, see K8)
    100   38  ext-digest-get-username-v1       MHD_digest_auth_get_username()
    101   39  ext-digest-get-username3         ..._username3()
    102 
    103 fuzz_str seeds
    104 --------------
    105 
    106   Cover MHD_hex_to_bin (including the 128 character input that
    107   digestauth.c used to allow into a 32 byte buffer), MHD_bin_to_hex[_z],
    108   the percent-decoders (strict, lenient, in place), MHD_str_unquote,
    109   MHD_str_quote, MHD_base64_to_bin_n, MHD_str[x]_to_uint64_n_ and the
    110   token helpers.
    111 
    112 fuzz_auth_header seeds
    113 ----------------------
    114 
    115   Well-formed and broken Digest parameter lists (unknown algorithm,
    116   extended `username*` notation, unterminated quoted strings, empty
    117   parameter list, over-long values) plus Basic `token68` variants, and
    118   -- with bit 0x02 of byte 0 -- the connection-less digest helpers at
    119   several algorithms and several deliberately undersized output
    120   buffers.
    121 
    122 fuzz_postprocessor seeds
    123 ------------------------
    124 
    125   urlencoded bodies with broken percent escapes, multipart bodies with
    126   ordinary, degenerate ("-") and quote-containing boundaries, a
    127   multipart Content-Type without a boundary, and a non-POST
    128   Content-Type.
    129 
    130 
    131 known-findings/
    132 ---------------
    133 
    134 Reproducers for the issues that this suite found in MHD itself; see
    135 section 6 of ../README, which records the status of each.  All of them
    136 are fixed on master, so all of them replay clean, and `check-corpus`
    137 replays this directory too.
    138 
    139 A file is named `K<n>-<harness>-<what>.bin` for the harness that found
    140 it; the ones without a harness in the name predate that convention and
    141 are all fuzz_request inputs.  `contrib/oss-fuzz/make_seed_corpus.sh`
    142 routes each into the seed corpus of its own harness on that basis.
    143 
    144 They are kept as a separate, explicitly named set rather than being
    145 folded into the main corpus because the files there are *generated*: a
    146 hand-written input would be clobbered or renumbered by the next
    147 `refresh-corpus`.  Replay one with
    148 
    149     ./fuzz_request --file=src/fuzz/corpus/known-findings/K1-digest-empty-realm.bin
    150 
    151   K1-digest-empty-realm.bin          digestauth.c is_param_equal()
    152                                      mhd_assert (0 != param->value.len)
    153   K2-chunkext-no-space.bin           connection.c handle_recv_no_space()
    154   K3-chunkext-stop-with-error.bin    connection.c
    155                                      transmit_error_response_len()
    156   K4a-wsp-first-header.bin           connection.c get_req_header(),
    157                                      CLIENT_DISCIPLINE_LVL <= -1
    158   K4b-empty-header-name.bin          connection.c get_req_header(),
    159                                      CLIENT_DISCIPLINE_LVL <= -2
    160   K5-bare-cr-keep.bin                connection.c get_req_header(),
    161                                      CLIENT_DISCIPLINE_LVL == -3
    162   K6-nonce-length-collision.bin      digestauth.c check_nonce_nc()
    163                                      mhd_assert (0 == nn->nonce[noncelen])
    164   K7-upgrade-after-must-close.bin    connection.c build_header_response()
    165                                      mhd_assert (upgrade -> MUST_UPGRADE);
    166                                      a single request carrying both
    167                                      Content-Length and chunked
    168                                      Transfer-Encoding, then an upgrade
    169   K9-fuzz_postprocessor-             postprocessor.c post_process_multipart()
    170     nested-boundary-leak.bin         leaked the previous nested boundary
    171                                      on every extra nested
    172                                      multipart/mixed part
    173   K14-fuzz_eventloop-                daemon.c call_handlers() asserted a
    174     force-close-not-closed.bin       post-condition that
    175                                      MHD_connection_handle_read() does
    176                                      not guarantee when the read buffer
    177                                      is full
    178                                      *** still open, see ../../../patches ***
    179                                      Only fires on an --enable-asserts
    180                                      *ASan* build: the redzones change
    181                                      the pool arithmetic that reaches it.
    182 
    183 These files are *not* regenerated by `--write-corpus`; they are edited by
    184 hand.  When the input format changes they have to be migrated, and the
    185 migration has to be checked -- replay each one with `--verbose` before
    186 and after and confirm the daemon, handler, body and challenge counts are
    187 unchanged.  The last such change made the API-selection block
    188 unconditional, which is six zero bytes inserted at offset 4 for K1-K6.
    189 
    190 K8 has no reproducer here: its trigger is an argument the application
    191 chooses, not network input, so the harness never performs it.  See
    192 section 6 of ../README for how to reach it by hand.
    193 
    194 A reproducer added while its finding is still open will make
    195 `make check-corpus` fail, which is intended -- that is what a regression
    196 test for an unfixed bug does.  `contrib/oss-fuzz/make_seed_corpus.sh`
    197 skips such a file so that ClusterFuzz does not spend every run
    198 rediscovering it; the rule it uses is whether `patches/$ID.diff` exists.