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.