fuzz_options.c (76668B)
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_options.c 22 * @brief In-process fuzzer for MHD's daemon option handling and for the 23 * daemon start-up / shutdown paths. 24 * @author Christian Grothoff 25 * 26 * fuzz_request.c fuzzes what MHD does with the *bytes on the wire*, but 27 * it always starts the daemon in one single shape: external polling, no 28 * listen socket, five fixed options. Everything in daemon.c that is 29 * reached through any other configuration -- the flag validation in 30 * MHD_start_daemon(), parse_options_va(), the internal polling thread, 31 * the thread pool, epoll/poll/select, the listen socket, the per-IP and 32 * per-daemon connection limits, quiesce -- is therefore dead code as far 33 * as the fuzzing suite is concerned. This harness closes that gap: the 34 * input bytes pick a *combination* of MHD_FLAG bits and an MHD_OPTION 35 * array, the daemon is started with them, one short HTTP request is 36 * driven through it over a socketpair, and the daemon is stopped again. 37 * 38 * The request-driving machinery is the one from fuzz_request.c: 39 * MHD_USE_NO_LISTEN_SOCKET plus MHD_add_connection() on an AF_UNIX 40 * socketpair(), with a fake 127.0.0.1 peer address so that the per-IP 41 * accounting sees something sane, and MHD_set_panic_func() installed as 42 * a tripwire. What is new here is that the *daemon* is a variable. 43 * 44 * Two shapes of daemon exist, and the harness drives them differently: 45 * 46 * - "external polling" (no MHD_USE_INTERNAL_POLLING_THREAD and no 47 * MHD_USE_THREAD_PER_CONNECTION). Single threaded and fully 48 * deterministic: the segments are fed in one at a time and the 49 * daemon is pumped with MHD_run() / MHD_get_fdset2() + select() + 50 * MHD_run_from_select2() / MHD_run_wait() between them. This is the 51 * majority of the iterations and the only shape in which the harness 52 * suspends a connection. 53 * 54 * - "internal thread" (an internal polling thread, a thread pool, or 55 * thread-per-connection). MHD_add_connection() is explicitly 56 * supported in these modes -- it is how an application with its own 57 * accept() loop hands sockets over -- but the harness must not also 58 * poll the daemon, and it cannot know when the worker is done. So 59 * the whole request is written at once, the write side is shut down 60 * (which makes MHD finish the request immediately rather than 61 * waiting for more data), and the answer is awaited with a short 62 * bounded poll() before the daemon is stopped. MHD_USE_ITC is 63 * forced on in this shape so that the worker picks the new 64 * connection up at once instead of waiting for its poll timeout. 65 * No connection is ever suspended here. 66 * 67 * Input format (see README section 2.2 for the fuzz_request one, which 68 * the segment stream below is deliberately identical to): 69 * 70 * byte 0 daemon shape: index into mode_tbl[] (event loop and 71 * threading; the entries are the combinations MHD documents 72 * as valid, so that most iterations get a live daemon) 73 * byte 1 MHD_FLAG bits, group A (see flags_from_input()) 74 * byte 2 MHD_FLAG bits, group B; 0xC0 in the top two bits switches 75 * on the "raw" escape, which feeds bytes 1, 2 and 15 76 * straight into the flags word. That is what fuzzes 77 * MHD_start_daemon()'s own combination checks; MHD answering 78 * NULL is a normal outcome and simply ends the iteration. 79 * byte 3 option presence mask A (sizes and limits) 80 * byte 4 option presence mask B (discipline, insanity, fd_setsize) 81 * byte 5 option presence mask C (the callbacks and the pointers) 82 * byte 6 value selector: connection memory limit / increment 83 * byte 7 value selector: connection limit / per-IP limit 84 * byte 8 value selector: connection timeout / pool size / stack 85 * byte 9 value selector: nonce-nc size / digest random / bind type 86 * byte 10 value selector: discipline / strict / insanity / bzero URI 87 * byte 11 value selector: backlog / fd_setsize / reuse / fastopen 88 * byte 12 driver behaviour: event loop variant, quiesce, 89 * introspection, suspend mode, number of connections 90 * byte 13 handler behaviour (which response, MHD_NO, per-connection 91 * option) and the accept path: whether an accept policy 92 * callback is installed, what it answers, and whether the 93 * iteration additionally connect()s to the daemon's real 94 * listening socket 95 * byte 14 option presence mask D (the digest and TLS-adjacent 96 * options, MHD_OPTION_LISTEN_SOCKET, and the deliberately 97 * invalid option number) 98 * byte 15 spare entropy; also supplies flag bits 14+ in raw mode 99 * byte 16. a sequence of send segments, each introduced by a little 100 * endian 16 bit header (op << 14) | length 101 * op 0 send the payload on the current connection 102 * op 1 send the payload, then pump extra rounds 103 * op 2 send the payload, then pump extra rounds 104 * op 3 close the current connection, open a fresh one on 105 * the same daemon, then send 106 * 107 * The sixteen configuration bytes are mandatory; a shorter input is 108 * rejected. An input of exactly sixteen bytes is meaningful and is not 109 * a degenerate case: it starts and stops a daemon with one connection 110 * and no traffic at all, which is precisely the start-up/shutdown path 111 * this harness is about. 112 * 113 * Two rules the harness obeys, both of them application contract rather 114 * than anything worth fuzzing: 115 * 116 * - every daemon that starts is stopped, and no connection is left 117 * suspended when that happens, because MHD_stop_daemon() answers a 118 * suspended connection with MHD_PANIC(). Suspended connections are 119 * tracked in pending_resume[] and flushed during teardown, exactly 120 * as in fuzz_request.c; 121 * - the socket that MHD_quiesce_daemon() returns belongs to the 122 * caller. It is closed after MHD_stop_daemon() (not before: with 123 * internal threads a worker may still be using it), otherwise the 124 * harness runs out of file descriptors within a few thousand 125 * iterations. 126 * 127 * Environment: 128 * 129 * MHD_FUZZ_QUIESCE_EPOLL_RACE=1 let the harness call 130 * MHD_quiesce_daemon() on an epoll daemon that runs its own 131 * thread(s). Off by default because that combination reaches an 132 * open MHD defect; see quiesce_epoll_race_allowed() below. 133 */ 134 135 #define FUZZ_HARNESS_NAME "fuzz_options" 136 #include "fuzz_common.h" 137 138 #include <microhttpd.h> 139 #include <sys/socket.h> 140 #include <netinet/in.h> 141 #include <sys/select.h> 142 #include <poll.h> 143 #include <limits.h> 144 145 /* MHD_OPTION_STRICT_FOR_CLIENT is superseded by 146 MHD_OPTION_CLIENT_DISCIPLINE_LVL but is still shipped API, and its 147 mapping onto the discipline level is exactly the kind of thing this 148 harness is for. */ 149 #if defined(__GNUC__) || defined(__clang__) 150 #pragma GCC diagnostic ignored "-Wdeprecated-declarations" 151 #endif 152 153 /** Number of mandatory configuration bytes at the start of the input. */ 154 #define CFG_BYTES 16 155 156 #define MAX_SEGMENTS 48 157 #define MAX_CONNECTIONS 6 158 #define MAX_OPTS 40 159 #define RESP_DRAIN_BUF 4096 160 #define GEN_BUF_SIZE 4096 161 162 /** 163 * Milliseconds the internal-thread shape waits for the daemon to answer. 164 * The write side of the socketpair is shut down before the wait, so the 165 * daemon never sits on an incomplete request: either it answers (the 166 * common case, tens of microseconds) or it closes the connection, and 167 * both wake poll() up immediately. The timeout is therefore only paid 168 * by inputs that make MHD drop the connection without a reply. 169 */ 170 #define THREAD_WAIT_MS 2 171 172 /** Rounds of MHD_run() after each segment in the external shape. */ 173 #define PUMP_ROUNDS 3 174 /** Rounds of MHD_run() after a segment sent with op 1 or 2. */ 175 #define PUMP_ROUNDS_LONG 10 176 177 178 /* ------------------------------------------------------------------ */ 179 /* Per-iteration configuration */ 180 /* ------------------------------------------------------------------ */ 181 182 struct fuzz_cfg 183 { 184 /** Daemon runs its own thread(s); the harness must not call MHD_run(). */ 185 int uses_threads; 186 /** A real listening socket was requested (no MHD_USE_NO_LISTEN_SOCKET). */ 187 int listen_sock; 188 /** MHD_USE_ERROR_LOG was requested, so an external logger is mandatory. */ 189 int err_log; 190 /** 0 none, 1 suspend+resume in the handler, 2 resume from the pump. */ 191 int suspend_mode; 192 /** 0 MHD_run(), 1 MHD_get_fdset2()+select()+MHD_run_from_select2(), 193 2 MHD_run_wait(0), 3 the v1 MHD_get_fdset()/MHD_run_from_select(). */ 194 unsigned int loop_mode; 195 /** Call MHD_quiesce_daemon() before stopping. */ 196 int quiesce; 197 /** Query MHD_get_daemon_info() / MHD_get_timeout*() while pumping. */ 198 int daemon_info; 199 /** Call MHD_set_connection_option() from the handler. */ 200 int conn_option; 201 /** Which response constructor the handler uses. */ 202 unsigned int resp_kind; 203 /** Handler answers MHD_NO instead of queueing a response. */ 204 int handler_no; 205 /** Upper bound on the number of connections the iteration opens. */ 206 unsigned int nconn_max; 207 /** Really connect() to the daemon's listening socket. */ 208 int real_connect; 209 /** Install an accept policy callback, and what it should answer. */ 210 int accept_policy; 211 int accept_policy_deny; 212 }; 213 214 static struct fuzz_cfg cfg; 215 216 /** 217 * Connections suspended by suspend mode 2, which the pump loop still has 218 * to resume. Cleared by completed_cb() so that a connection MHD has 219 * finished with is never resumed afterwards. A connection that is still 220 * suspended when MHD_stop_daemon() runs makes MHD answer with 221 * MHD_PANIC ("MHD_stop_daemon() called while we have suspended 222 * connections"), which would look exactly like an MHD bug. 223 */ 224 static struct MHD_Connection *pending_resume[MAX_CONNECTIONS]; 225 226 /** 227 * Set once the iteration only wants to drain the daemon. The handler 228 * then stops parking new connections, which is what makes the flush loop 229 * in the teardown provably terminate. 230 */ 231 static int tearing_down; 232 233 /** Statistics, printed at exit with --verbose. */ 234 static unsigned long stat_daemons; 235 static unsigned long stat_daemons_failed; 236 static unsigned long stat_threaded; 237 static unsigned long stat_handler_calls; 238 static unsigned long stat_conns_added; 239 static unsigned long stat_conns_refused; 240 static unsigned long stat_real_conns; 241 static int stats_registered; 242 243 244 static void 245 print_stats (void) 246 { 247 if (! fuzz_verbose) 248 return; 249 fprintf (stderr, 250 "%s: daemons=%lu (failed to start=%lu, threaded=%lu) " 251 "connections=%lu (refused=%lu, accepted from a real socket=%lu) " 252 "handler calls=%lu\n", 253 FUZZ_HARNESS_NAME, 254 stat_daemons, stat_daemons_failed, stat_threaded, 255 stat_conns_added, stat_conns_refused, stat_real_conns, 256 stat_handler_calls); 257 } 258 259 260 /* ------------------------------------------------------------------ */ 261 /* Flags */ 262 /* ------------------------------------------------------------------ */ 263 264 /** 265 * The event-loop / threading core of the flags word. Every entry is a 266 * combination MHD documents as valid, so that the ordinary path through 267 * this harness gets a live daemon and actually exercises the option 268 * handling; the deliberately invalid combinations are reached through 269 * the raw escape of byte 2 instead. 270 * 271 * The distribution is on purpose: entries 0..17 are the external polling 272 * modes, 18..23 the ones with internal threads. An iteration with 273 * internal threads costs a thread creation, a join and a short wait for 274 * the answer, i.e. roughly an order of magnitude more than an external 275 * one, so they are a quarter of the iterations rather than a half. 276 */ 277 static const unsigned int mode_tbl[] = { 278 0, 0, 0, 0, 0, 0, /* external, select() */ 279 0, 0, 0, 0, 0, 0, 280 MHD_USE_EPOLL, MHD_USE_EPOLL, /* external, epoll */ 281 MHD_USE_EPOLL, MHD_USE_EPOLL, 282 MHD_USE_AUTO, MHD_USE_AUTO, /* external, MHD picks */ 283 MHD_USE_INTERNAL_POLLING_THREAD, 284 MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_POLL, 285 MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_EPOLL, 286 MHD_USE_THREAD_PER_CONNECTION | MHD_USE_INTERNAL_POLLING_THREAD, 287 MHD_USE_THREAD_PER_CONNECTION | MHD_USE_INTERNAL_POLLING_THREAD 288 | MHD_USE_POLL, 289 MHD_USE_AUTO | MHD_USE_INTERNAL_POLLING_THREAD 290 }; 291 292 #define MODE_COUNT (sizeof (mode_tbl) / sizeof (mode_tbl[0])) 293 294 /** 295 * Flag bits the raw escape is allowed to set. MHD_USE_TLS is masked 296 * out: without a certificate the daemon merely fails to start, so it 297 * would only waste iterations, and fuzzing GnuTLS is not what this 298 * harness is for. Everything else, including the bits above 299 * MHD_USE_NO_THREAD_SAFETY that no MHD_FLAG uses, is fair game. 300 */ 301 #define RAW_FLAG_MASK (~((unsigned int) MHD_USE_TLS)) 302 303 /** 304 * The bit that MHD_suspend_connection() actually tests. 305 * 306 * MHD_ALLOW_SUSPEND_RESUME is a *compound* value (8192 | MHD_USE_ITC), 307 * so "0 != (flags & MHD_ALLOW_SUSPEND_RESUME)" is already true for a 308 * daemon that merely asked for MHD_USE_ITC -- and suspending such a 309 * connection is answered with 310 * MHD_PANIC ("Cannot suspend connections without enabling 311 * MHD_ALLOW_SUSPEND_RESUME"), which is the harness violating the API 312 * rather than a finding. internal.h calls the single bit 313 * MHD_TEST_ALLOW_SUSPEND_RESUME; this is the same value, expressed 314 * without reaching into a private header. 315 */ 316 #define SUSPEND_BIT \ 317 (((unsigned int) MHD_ALLOW_SUSPEND_RESUME) \ 318 & ~((unsigned int) MHD_USE_ITC)) 319 320 321 /** 322 * Build the flags word from bytes 0, 1, 2 and 15 of the input. 323 * 324 * @param data the input 325 * @return the flags to pass to MHD_start_daemon() 326 */ 327 static unsigned int 328 flags_from_input (const uint8_t *data) 329 { 330 unsigned int flags; 331 332 if (0xC0 == (data[2] & 0xC0)) 333 { 334 /* Raw escape: the flags word comes straight off the input. This is 335 what exercises MHD_start_daemon()'s combination checks (POLL with 336 EPOLL, EPOLL with thread-per-connection, AUTO with either, ...), 337 every one of which answers NULL. */ 338 flags = ((unsigned int) data[1]) 339 | (((unsigned int) (data[2] & 0x3F)) << 8) 340 | (((unsigned int) data[15]) << 14); 341 flags &= RAW_FLAG_MASK; 342 cfg.listen_sock = (0 == (flags & MHD_USE_NO_LISTEN_SOCKET)); 343 cfg.err_log = (0 != (flags & MHD_USE_ERROR_LOG)); 344 return flags; 345 } 346 347 flags = mode_tbl[data[0] % MODE_COUNT]; 348 349 if (0 != (data[1] & 0x01)) 350 flags |= MHD_USE_PEDANTIC_CHECKS; 351 if (0 != (data[1] & 0x02)) 352 flags |= MHD_USE_SUPPRESS_DATE_NO_CLOCK; 353 if (0 != (data[1] & 0x04)) 354 flags |= MHD_ALLOW_SUSPEND_RESUME; 355 if ( (0 != (data[1] & 0x08)) && 356 (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_UPGRADE)) ) 357 flags |= MHD_ALLOW_UPGRADE; 358 if (0 != (data[1] & 0x10)) 359 flags |= MHD_USE_TURBO; 360 if (0 != (data[1] & 0x20)) 361 flags |= MHD_USE_ITC; 362 if (0 != (data[1] & 0x40)) 363 cfg.err_log = 1; 364 if (0 != (data[1] & 0x80)) 365 flags |= MHD_USE_POST_HANDSHAKE_AUTH_SUPPORT; /* ignored without TLS */ 366 367 if (0 != (data[2] & 0x01)) 368 cfg.listen_sock = 1; 369 if (0 != (data[2] & 0x02)) 370 flags |= MHD_USE_IPv6; 371 if (0 != (data[2] & 0x04)) 372 flags |= MHD_USE_DUAL_STACK; 373 if (0 != (data[2] & 0x08)) 374 flags |= MHD_USE_TCP_FASTOPEN; 375 if (0 != (data[2] & 0x10)) 376 flags |= MHD_USE_NO_THREAD_SAFETY; 377 if (0 != (data[2] & 0x20)) 378 flags |= MHD_USE_INSECURE_TLS_EARLY_DATA; /* ignored without TLS */ 379 380 /* MHD_USE_NO_THREAD_SAFETY together with any internal thread is 381 documented as unsupported and answered with NULL. Drop it here 382 rather than burning the iteration; the raw escape above still 383 reaches that check. */ 384 if (0 != (flags & (MHD_USE_INTERNAL_POLLING_THREAD 385 | MHD_USE_THREAD_PER_CONNECTION))) 386 flags &= ~((unsigned int) MHD_USE_NO_THREAD_SAFETY); 387 return flags; 388 } 389 390 391 /** 392 * @return non-zero if @a flags make MHD use epoll 393 * 394 * MHD_USE_AUTO resolves to epoll for everything except 395 * thread-per-connection, which it resolves to poll. 396 */ 397 static int 398 uses_epoll (unsigned int flags) 399 { 400 if (0 != (flags & MHD_USE_EPOLL)) 401 return 1; 402 return (0 != (flags & MHD_USE_AUTO)) && 403 (0 == (flags & MHD_USE_THREAD_PER_CONNECTION)); 404 } 405 406 407 /** 408 * Is the harness allowed to call MHD_quiesce_daemon() on an epoll 409 * daemon that runs its own thread(s)? 410 * 411 * It is not, by default, because that combination reaches an *open* MHD 412 * defect and would make "make check" fail at random: 413 * 414 * MHD_quiesce_daemon() sets daemon->was_quiesced and then removes the 415 * listen FD from the epoll set itself (daemon.c:6208), tolerating 416 * ENOENT with the comment "can happen due to race with MHD_epoll()". 417 * MHD_epoll() removes the very same FD in two places. The first 418 * (daemon.c:5556) mirrors that and tolerates ENOENT. The second 419 * (daemon.c:5593), the "at the connection limit, disable listen 420 * socket" branch, also fires when daemon->was_quiesced is set -- and 421 * it does not tolerate anything: 422 * 423 * if (0 != epoll_ctl (daemon->epoll_fd, EPOLL_CTL_DEL, ls, NULL)) 424 * MHD_PANIC (_ ("Failed to remove listen FD from epoll set.")); 425 * 426 * The worker can read daemon->was_quiesced as already true and 427 * daemon->listen_socket_in_epoll as still true -- neither is atomic 428 * and neither is under a lock -- and then lose the race for the 429 * removal, so epoll_ctl() answers ENOENT and MHD aborts the process. 430 * Confirmed with errno == ENOENT, was_quiesced == 1, connections == 0. 431 * The same unguarded MHD_PANIC() sits in the worker-pool loop of 432 * MHD_quiesce_daemon() itself (daemon.c:6189). 433 * 434 * Set MHD_FUZZ_QUIESCE_EPOLL_RACE=1 to reach it. Quiescing is left 435 * enabled everywhere else, so the poll()/select() side of 436 * MHD_quiesce_daemon(), including its worker-pool loop, keeps being 437 * exercised. 438 */ 439 static int 440 quiesce_epoll_race_allowed (void) 441 { 442 static int val = -1; 443 444 if (0 > val) 445 { 446 const char *e = getenv ("MHD_FUZZ_QUIESCE_EPOLL_RACE"); 447 448 val = ((NULL != e) && (0 != atoi (e))) ? 1 : 0; 449 } 450 return val; 451 } 452 453 454 /* ------------------------------------------------------------------ */ 455 /* Option values */ 456 /* ------------------------------------------------------------------ */ 457 458 static const size_t mem_limit_tbl[] = { 459 0 /* MHD default */, 128, 192, 256, 384, 512, 1024, 1400, 460 1500, 2048, 4096, 8192, 32768, 64, 0, 0 461 }; 462 463 static const size_t mem_increment_tbl[] = { 464 0 /* MHD default */, 1, 16, 64, 128, 256, 1024, 4096, 465 1500, 32768, 0, 0, 0, 0, 0, 0 466 }; 467 468 static const unsigned int conn_limit_tbl[] = { 469 0, 1, 2, 3, 8, 64, 1024, 100000 470 }; 471 472 static const unsigned int per_ip_limit_tbl[] = { 473 0, 1, 2, 3, 4, 8, 64, 1000 474 }; 475 476 static const unsigned int timeout_tbl[] = { 477 0, 1, 2, 5, 60, 3600, 86400, UINT_MAX 478 }; 479 480 static const unsigned int pool_size_tbl[] = { 2, 3, 4, 2 }; 481 482 /* Small stacks make pthread_create() fail outright under ASAN, which 483 only produces a NULL daemon; keep the values plausible. */ 484 static const size_t stack_size_tbl[] = { 485 0, 1024u * 1024u, 2048u * 1024u, 8192u * 1024u 486 }; 487 488 /* Bounded on purpose: the nonce-nc array is nonce_nc_size * 489 sizeof(struct MHD_NonceNc) (about 130 bytes per slot) and is 490 allocated and freed once per iteration. */ 491 static const unsigned int nonce_nc_tbl[] = { 0, 1, 4, 32 }; 492 493 static const int discipline_tbl[] = { -3, -2, -1, 0, 1, 2, 3, -4 }; 494 495 static const int strict_tbl[] = { -1, 0, 1, 2 }; 496 497 static const unsigned int backlog_tbl[] = { 0, 1, 5, 511 }; 498 499 static const unsigned int fastopen_tbl[] = { 0, 1, 5, 10 }; 500 501 /* Anything but the platform's own FD_SETSIZE is rejected unless MHD was 502 built with HAS_FD_SETSIZE_OVERRIDABLE, so the odd values are a small 503 minority: they cost a whole iteration each. */ 504 static const int fd_setsize_tbl[] = { 505 (int) FD_SETSIZE, (int) FD_SETSIZE, (int) FD_SETSIZE, (int) FD_SETSIZE, 506 (int) FD_SETSIZE, (int) FD_SETSIZE, 64, 0 507 }; 508 509 /** Fixed entropy, so that a digest nonce is reproducible across runs. */ 510 static const char digest_rnd[32] = 511 "\x01\x23\x45\x67\x89\xab\xcd\xef\x01\x23\x45\x67\x89\xab\xcd\xef" 512 "\xfe\xdc\xba\x98\x76\x54\x32\x10\xfe\xdc\xba\x98\x76\x54\x32\x10"; 513 514 /* Bind addresses for MHD_OPTION_SOCK_ADDR / MHD_OPTION_SOCK_ADDR_LEN. 515 Filled in by prepare_sock_addrs() because htons()/htonl() are not 516 constant expressions. */ 517 static struct sockaddr_in bind4; 518 #ifdef AF_INET6 519 static struct sockaddr_in6 bind6; 520 #endif 521 static int bind_addrs_ready; 522 523 524 static void 525 prepare_sock_addrs (void) 526 { 527 if (bind_addrs_ready) 528 return; 529 bind_addrs_ready = 1; 530 memset (&bind4, 0, sizeof (bind4)); 531 bind4.sin_family = AF_INET; 532 bind4.sin_port = htons (0); /* ephemeral */ 533 bind4.sin_addr.s_addr = htonl (INADDR_LOOPBACK); 534 #ifdef AF_INET6 535 memset (&bind6, 0, sizeof (bind6)); 536 bind6.sin6_family = AF_INET6; 537 bind6.sin6_port = htons (0); 538 bind6.sin6_addr = in6addr_loopback; 539 #endif 540 } 541 542 543 /* ------------------------------------------------------------------ */ 544 /* The daemon callbacks */ 545 /* ------------------------------------------------------------------ */ 546 547 /** 548 * MHD_OPTION_EXTERNAL_LOGGER. Swallows the message unless --verbose is 549 * in effect. This is what makes MHD_USE_ERROR_LOG affordable: with the 550 * flag set, MHD executes several hundred MHD_DLOG() call sites in 551 * daemon.c that are otherwise dead, and without a logger of our own all 552 * of that would land on stderr. 553 */ 554 static void 555 logger_cb (void *cls, 556 const char *fmt, 557 va_list ap) 558 { 559 (void) cls; 560 if (! fuzz_verbose) 561 return; 562 (void) vfprintf (stderr, fmt, ap); 563 } 564 565 566 /** 567 * MHD_OPTION_URI_LOG_CALLBACK. The return value becomes the initial 568 * `*req_cls` of the request, so it has to stay NULL: the access handler 569 * uses "req_cls is still NULL" to recognise its first invocation, and a 570 * non-NULL value here would also have to be freed somewhere, which is 571 * not possible when MHD_OPTION_NOTIFY_COMPLETED is not set. 572 */ 573 static void * 574 uri_log_cb (void *cls, 575 const char *uri, 576 struct MHD_Connection *con) 577 { 578 volatile size_t sink; 579 580 (void) cls; 581 (void) con; 582 sink = (NULL != uri) ? strlen (uri) : 0; 583 (void) sink; 584 return NULL; 585 } 586 587 588 /** 589 * MHD_OPTION_UNESCAPE_CALLBACK. Replaces MHD's own unescaping, so it 590 * must honour the contract: unescape @a s in place and return the new 591 * length. Leaving the string alone is a legal implementation and keeps 592 * the harness out of the business of re-testing mhd_str.c, which 593 * fuzz_str does. 594 */ 595 static size_t 596 unescape_cb (void *cls, 597 struct MHD_Connection *conn, 598 char *s) 599 { 600 (void) cls; 601 (void) conn; 602 return strlen (s); 603 } 604 605 606 static void 607 completed_cb (void *cls, 608 struct MHD_Connection *connection, 609 void **req_cls, 610 enum MHD_RequestTerminationCode toe) 611 { 612 (void) cls; 613 (void) toe; 614 /* MHD is done with this connection, so the deferred resume of suspend 615 mode 2 must not fire for it any more. */ 616 if (! cfg.uses_threads) 617 { 618 unsigned int i; 619 620 for (i = 0; i < MAX_CONNECTIONS; i++) 621 if (pending_resume[i] == connection) 622 pending_resume[i] = NULL; 623 } 624 *req_cls = NULL; 625 } 626 627 628 static void 629 notify_connection_cb (void *cls, 630 struct MHD_Connection *connection, 631 void **socket_context, 632 enum MHD_ConnectionNotificationCode toe) 633 { 634 (void) cls; 635 (void) connection; 636 if (MHD_CONNECTION_NOTIFY_STARTED == toe) 637 *socket_context = NULL; 638 } 639 640 641 static void 642 panic_cb (void *cls, 643 const char *file, 644 unsigned int line, 645 const char *reason) 646 { 647 char msg[512]; 648 649 (void) cls; 650 (void) snprintf (msg, sizeof (msg), 651 "MHD_PANIC() reached at %s:%u: %s", 652 (NULL != file) ? file : "?", 653 line, 654 (NULL != reason) ? reason : "?"); 655 fuzz_report_finding (msg); 656 } 657 658 659 /* ------------------------------------------------------------------ */ 660 /* Suspend bookkeeping */ 661 /* ------------------------------------------------------------------ */ 662 663 static void 664 pending_resume_add (struct MHD_Connection *c) 665 { 666 unsigned int i; 667 668 for (i = 0; i < MAX_CONNECTIONS; i++) 669 { 670 if (NULL == pending_resume[i]) 671 { 672 pending_resume[i] = c; 673 return; 674 } 675 } 676 } 677 678 679 /** 680 * Resume everything still parked. 681 * 682 * @return non-zero if at least one connection was resumed, so that the 683 * caller knows the daemon needs another round to act on it 684 */ 685 static int 686 pending_resume_flush (void) 687 { 688 unsigned int i; 689 int any = 0; 690 691 for (i = 0; i < MAX_CONNECTIONS; i++) 692 { 693 struct MHD_Connection *c = pending_resume[i]; 694 695 if (NULL == c) 696 continue; 697 /* Clear first: MHD_resume_connection() can make MHD run the handler, 698 which may suspend the very same connection again. */ 699 pending_resume[i] = NULL; 700 MHD_resume_connection (c); 701 any = 1; 702 } 703 return any; 704 } 705 706 707 /** 708 * Suspend the connection if the input asks for it. Only ever called in 709 * the external-polling shape: resuming from the harness thread while an 710 * internal worker owns the connection is legal but untimed, and this 711 * harness has no way to tell whether the resume happened before or after 712 * the worker looked at the connection. 713 */ 714 static void 715 suspend_maybe (struct MHD_Connection *connection) 716 { 717 if ( (0 == cfg.suspend_mode) || 718 cfg.uses_threads || 719 tearing_down) 720 return; 721 MHD_suspend_connection (connection); 722 if (1 == cfg.suspend_mode) 723 { 724 MHD_resume_connection (connection); 725 return; 726 } 727 pending_resume_add (connection); 728 } 729 730 731 /* ------------------------------------------------------------------ */ 732 /* The access handler */ 733 /* ------------------------------------------------------------------ */ 734 735 static const char resp_body[] = "hello"; 736 737 static enum MHD_Result 738 ahc (void *cls, 739 struct MHD_Connection *connection, 740 const char *url, 741 const char *method, 742 const char *version, 743 const char *upload_data, 744 size_t *upload_data_size, 745 void **req_cls) 746 { 747 /* The address of this object is the "request already started" marker. 748 Nothing is allocated per request on purpose: this harness runs 749 millions of iterations in one process and the daemon shapes with 750 internal threads cannot guarantee that MHD_OPTION_NOTIFY_COMPLETED 751 is even set, so anything malloc()ed here could leak. */ 752 static int req_marker; 753 struct MHD_Response *resp; 754 enum MHD_Result ret; 755 unsigned int code; 756 volatile size_t sink = 0; 757 758 (void) cls; 759 (void) upload_data; 760 761 if (&req_marker != *req_cls) 762 { 763 *req_cls = &req_marker; 764 return MHD_YES; 765 } 766 stat_handler_calls++; 767 768 /* Touch the parsed request line the way a real application would. */ 769 if (NULL != url) 770 sink += strlen (url); 771 if (NULL != method) 772 sink += strlen (method); 773 if (NULL != version) 774 sink += strlen (version); 775 (void) sink; 776 777 if (0 != *upload_data_size) 778 { 779 *upload_data_size = 0; 780 return MHD_YES; 781 } 782 783 if (cfg.conn_option) 784 (void) MHD_set_connection_option (connection, 785 MHD_CONNECTION_OPTION_TIMEOUT, 786 (unsigned int) 30); 787 if (cfg.handler_no) 788 return MHD_NO; /* never suspend on this path: MHD_NO terminates the 789 connection and terminating a suspended one trips 790 mhd_assert (! connection->suspended) */ 791 792 code = MHD_HTTP_OK; 793 switch (cfg.resp_kind) 794 { 795 case 1: 796 resp = MHD_create_response_empty (MHD_RF_NONE); 797 code = MHD_HTTP_NO_CONTENT; 798 break; 799 case 2: 800 resp = MHD_create_response_from_buffer (sizeof (resp_body) - 1, 801 (void *) (intptr_t) resp_body, 802 MHD_RESPMEM_MUST_COPY); 803 code = MHD_HTTP_FORBIDDEN; 804 break; 805 case 3: 806 resp = MHD_create_response_from_buffer_static (0, ""); 807 code = MHD_HTTP_INTERNAL_SERVER_ERROR; 808 break; 809 default: 810 resp = MHD_create_response_from_buffer_static (sizeof (resp_body) - 1, 811 resp_body); 812 break; 813 } 814 if (NULL == resp) 815 return MHD_NO; 816 ret = MHD_queue_response (connection, code, resp); 817 MHD_destroy_response (resp); 818 if (MHD_YES == ret) 819 suspend_maybe (connection); 820 return ret; 821 } 822 823 824 /* ------------------------------------------------------------------ */ 825 /* Driving the daemon */ 826 /* ------------------------------------------------------------------ */ 827 828 /** 829 * Read and discard whatever the daemon has produced so far. The harness 830 * has no response oracle -- fuzz_request owns that job -- but the data 831 * has to be taken off the socket so that MHD's writes keep succeeding. 832 */ 833 static void 834 drain (int sock) 835 { 836 char tmp[RESP_DRAIN_BUF]; 837 838 if (0 > sock) 839 return; 840 while (0 < recv (sock, tmp, sizeof (tmp), MSG_DONTWAIT)) 841 /* nothing */; 842 } 843 844 845 /** 846 * Advance an externally polled daemon by one cycle, through whichever of 847 * the four event-loop entry points byte 12 selected. 848 * 849 * The select() timeout is always zero: the harness is single threaded 850 * and everything the daemon could be waiting for has already been 851 * written into the socketpair, so blocking would only burn wall clock. 852 */ 853 static void 854 run_once (struct MHD_Daemon *d) 855 { 856 fd_set rs; 857 fd_set ws; 858 fd_set es; 859 MHD_socket max_fd = MHD_INVALID_SOCKET; 860 struct timeval tv; 861 862 if (cfg.daemon_info) 863 { 864 MHD_UNSIGNED_LONG_LONG tl = 0; 865 volatile int64_t sink; 866 867 (void) MHD_get_timeout (d, &tl); 868 sink = MHD_get_timeout64s (d); 869 sink += (int64_t) MHD_get_timeout_i (d); 870 (void) sink; 871 } 872 switch (cfg.loop_mode) 873 { 874 case 1: 875 case 3: 876 FD_ZERO (&rs); 877 FD_ZERO (&ws); 878 FD_ZERO (&es); 879 /* MHD_get_fdset and MHD_run_from_select are also macros forwarding 880 to the *2 variants, so the names have to be parenthesised for the 881 v1 entry points to be reached at all. */ 882 if (1 == cfg.loop_mode) 883 { 884 if (MHD_YES != MHD_get_fdset2 (d, &rs, &ws, &es, &max_fd, 885 (unsigned int) FD_SETSIZE)) 886 { 887 (void) MHD_run (d); 888 return; 889 } 890 } 891 else 892 { 893 if (MHD_YES != (MHD_get_fdset) (d, &rs, &ws, &es, &max_fd)) 894 { 895 (void) MHD_run (d); 896 return; 897 } 898 } 899 tv.tv_sec = 0; 900 tv.tv_usec = 0; 901 if (MHD_INVALID_SOCKET != max_fd) 902 (void) select ((int) max_fd + 1, &rs, &ws, &es, &tv); 903 if (1 == cfg.loop_mode) 904 (void) MHD_run_from_select2 (d, &rs, &ws, &es, (unsigned int) FD_SETSIZE); 905 else 906 (void) (MHD_run_from_select) (d, &rs, &ws, &es); 907 break; 908 case 2: 909 (void) MHD_run_wait (d, 0); 910 break; 911 default: 912 (void) MHD_run (d); 913 break; 914 } 915 } 916 917 918 static void 919 pump (struct MHD_Daemon *d, 920 int sock, 921 unsigned int rounds) 922 { 923 unsigned int i; 924 925 if (cfg.uses_threads) 926 return; 927 for (i = 0; i < rounds; i++) 928 { 929 /* Deferred resume of suspend mode 2. Each slot is cleared before 930 its connection is resumed, which stays correct even when the 931 resume makes MHD complete (and forget) the connection. */ 932 (void) pending_resume_flush (); 933 run_once (d); 934 drain (sock); 935 } 936 } 937 938 939 /** 940 * Wait, briefly and with a hard bound, for a daemon with internal 941 * threads to answer. The caller has already shut the write side down, 942 * so MHD sees end-of-stream and either answers or closes; both wake 943 * poll() immediately. An input that makes MHD do neither pays the full 944 * timeout, which is why it is only a couple of milliseconds. 945 */ 946 static void 947 wait_threaded (int sock) 948 { 949 unsigned int round; 950 951 if (0 > sock) 952 return; 953 for (round = 0; round < 4; round++) 954 { 955 struct pollfd p; 956 char tmp[RESP_DRAIN_BUF]; 957 ssize_t n; 958 959 p.fd = sock; 960 p.events = POLLIN; 961 p.revents = 0; 962 if (0 >= poll (&p, 1, THREAD_WAIT_MS)) 963 return; 964 if (0 == (p.revents & POLLIN)) 965 return; /* POLLHUP/POLLERR only: peer is gone */ 966 n = recv (sock, tmp, sizeof (tmp), MSG_DONTWAIT); 967 if (0 >= n) 968 return; 969 } 970 } 971 972 973 static void 974 send_all (struct MHD_Daemon *d, 975 int sock, 976 const uint8_t *data, 977 size_t len) 978 { 979 size_t off = 0; 980 unsigned int stall = 0; 981 982 if (0 > sock) 983 return; 984 while ( (off < len) && 985 (stall < 64) ) 986 { 987 ssize_t s = send (sock, data + off, len - off, MSG_DONTWAIT); 988 989 if (0 < s) 990 { 991 off += (size_t) s; 992 stall = 0; 993 continue; 994 } 995 stall++; 996 if (cfg.uses_threads) 997 { 998 struct pollfd p; 999 1000 p.fd = sock; 1001 p.events = POLLOUT; 1002 p.revents = 0; 1003 if (0 >= poll (&p, 1, THREAD_WAIT_MS)) 1004 break; 1005 } 1006 else 1007 { 1008 pump (d, sock, 2); 1009 } 1010 if ( (0 > s) && 1011 (EAGAIN != errno) && 1012 (EWOULDBLOCK != errno) && 1013 (EINTR != errno) ) 1014 break; 1015 } 1016 } 1017 1018 1019 /** 1020 * Hand a fresh socketpair to the daemon. 1021 * 1022 * @param d the daemon 1023 * @param[out] sock set to the harness side of the pair 1024 * @return 0 on success, -1 if MHD refused the connection (which is a 1025 * perfectly ordinary outcome: MHD_OPTION_CONNECTION_LIMIT and 1026 * MHD_OPTION_PER_IP_CONNECTION_LIMIT are part of what is fuzzed) 1027 */ 1028 static int 1029 new_connection (struct MHD_Daemon *d, 1030 int *sock) 1031 { 1032 int sv[2]; 1033 struct sockaddr_in sa; 1034 1035 *sock = -1; 1036 if (0 != socketpair (AF_UNIX, SOCK_STREAM, 0, sv)) 1037 return -1; 1038 memset (&sa, 0, sizeof (sa)); 1039 sa.sin_family = AF_INET; 1040 sa.sin_port = htons (44444); 1041 sa.sin_addr.s_addr = htonl (INADDR_LOOPBACK); 1042 if (MHD_YES != MHD_add_connection (d, 1043 (MHD_socket) sv[1], 1044 (const struct sockaddr *) &sa, 1045 (socklen_t) sizeof (sa))) 1046 { 1047 /* MHD has already closed sv[1] in that case */ 1048 (void) close (sv[0]); 1049 stat_conns_refused++; 1050 return -1; 1051 } 1052 stat_conns_added++; 1053 *sock = sv[0]; 1054 return 0; 1055 } 1056 1057 1058 static void 1059 close_connection (struct MHD_Daemon *d, 1060 int *sock) 1061 { 1062 if (0 > *sock) 1063 return; 1064 (void) shutdown (*sock, SHUT_WR); 1065 if (cfg.uses_threads) 1066 wait_threaded (*sock); 1067 else 1068 pump (d, *sock, 4); 1069 (void) close (*sock); 1070 *sock = -1; 1071 } 1072 1073 1074 /* ------------------------------------------------------------------ */ 1075 /* Building the option array */ 1076 /* ------------------------------------------------------------------ */ 1077 1078 #define PICK(tbl, idx) ((tbl)[(idx) % (sizeof (tbl) / sizeof ((tbl)[0]))]) 1079 1080 static void 1081 add_opt (struct MHD_OptionItem *opts, 1082 unsigned int *nopt, 1083 enum MHD_OPTION option, 1084 intptr_t value, 1085 void *ptr_value) 1086 { 1087 if (*nopt + 1 >= MAX_OPTS) 1088 return; 1089 opts[*nopt].option = option; 1090 opts[*nopt].value = value; 1091 opts[*nopt].ptr_value = ptr_value; 1092 (*nopt)++; 1093 } 1094 1095 1096 /** 1097 * Translate the presence masks (bytes 3, 4, 14) and the value selectors 1098 * (bytes 6..11, 15) into an MHD_OptionItem array. 1099 * 1100 * Only the options that can be expressed through MHD_OPTION_ARRAY are 1101 * built here. The five callback options take two pointers, and putting 1102 * a function pointer into the array's `intptr_t value` member is not 1103 * strictly conforming C, so those go through the varargs of 1104 * MHD_start_daemon() instead (see start_daemon_variant()). 1105 */ 1106 static unsigned int 1107 build_options (const uint8_t *data, 1108 struct MHD_OptionItem *opts, 1109 unsigned int flags) 1110 { 1111 unsigned int nopt = 0; 1112 const uint8_t mask_a = data[3]; 1113 const uint8_t mask_b = data[4]; 1114 const uint8_t mask_d = data[14]; 1115 /* A thread pool needs an internal polling thread and is incompatible 1116 with thread-per-connection. */ 1117 const int pool_ok = 1118 (0 != (flags & MHD_USE_INTERNAL_POLLING_THREAD)) && 1119 (0 == (flags & MHD_USE_THREAD_PER_CONNECTION)); 1120 /* Offer options in configurations where MHD rejects them. One input 1121 in eight: the rejection branches are worth reaching, but each one 1122 costs the whole rest of the iteration, because the daemon does not 1123 start at all. */ 1124 const int misfit = (0xE0 == (data[15] & 0xE0)); 1125 1126 if (0 != (mask_a & 0x01)) 1127 add_opt (opts, &nopt, MHD_OPTION_CONNECTION_MEMORY_LIMIT, 1128 (intptr_t) PICK (mem_limit_tbl, data[6] & 0x0F), NULL); 1129 if (0 != (mask_a & 0x02)) 1130 add_opt (opts, &nopt, MHD_OPTION_CONNECTION_MEMORY_INCREMENT, 1131 (intptr_t) PICK (mem_increment_tbl, (data[6] >> 4) & 0x0F), NULL); 1132 if (0 != (mask_a & 0x04)) 1133 add_opt (opts, &nopt, MHD_OPTION_CONNECTION_LIMIT, 1134 (intptr_t) PICK (conn_limit_tbl, data[7] & 0x07), NULL); 1135 if (0 != (mask_a & 0x08)) 1136 add_opt (opts, &nopt, MHD_OPTION_PER_IP_CONNECTION_LIMIT, 1137 (intptr_t) PICK (per_ip_limit_tbl, (data[7] >> 4) & 0x07), NULL); 1138 if (0 != (mask_a & 0x10)) 1139 add_opt (opts, &nopt, MHD_OPTION_CONNECTION_TIMEOUT, 1140 (intptr_t) PICK (timeout_tbl, data[8] & 0x07), NULL); 1141 /* MHD_OPTION_THREAD_POOL_SIZE is rejected outright without an 1142 internal polling thread, and again when combined with 1143 MHD_USE_THREAD_PER_CONNECTION. Both branches are worth reaching, 1144 but a daemon that fails to start exercises nothing else, so the 1145 mismatched combinations are gated behind an extra bit instead of 1146 being half of all the inputs that name the option. */ 1147 if ( (0 != (mask_a & 0x20)) && 1148 (pool_ok || misfit) ) 1149 add_opt (opts, &nopt, MHD_OPTION_THREAD_POOL_SIZE, 1150 (intptr_t) PICK (pool_size_tbl, (data[8] >> 3) & 0x03), NULL); 1151 if (0 != (mask_a & 0x40)) 1152 add_opt (opts, &nopt, MHD_OPTION_THREAD_STACK_SIZE, 1153 (intptr_t) PICK (stack_size_tbl, (data[8] >> 5) & 0x03), NULL); 1154 if (0 != (mask_a & 0x80)) 1155 add_opt (opts, &nopt, MHD_OPTION_LISTENING_ADDRESS_REUSE, 1156 (intptr_t) (unsigned int) (data[11] & 0x01), NULL); 1157 1158 if (0 != (mask_b & 0x01)) 1159 add_opt (opts, &nopt, MHD_OPTION_LISTEN_BACKLOG_SIZE, 1160 (intptr_t) PICK (backlog_tbl, (data[11] >> 1) & 0x03), NULL); 1161 if (0 != (mask_b & 0x02)) 1162 add_opt (opts, &nopt, MHD_OPTION_NONCE_NC_SIZE, 1163 (intptr_t) PICK (nonce_nc_tbl, data[9] & 0x03), NULL); 1164 if (0 != (mask_b & 0x04)) 1165 add_opt (opts, &nopt, MHD_OPTION_SERVER_INSANITY, 1166 (intptr_t) (unsigned int) ((data[10] >> 6) & 0x03), NULL); 1167 if (0 != (mask_b & 0x08)) 1168 add_opt (opts, &nopt, MHD_OPTION_STRICT_FOR_CLIENT, 1169 (intptr_t) PICK (strict_tbl, (data[10] >> 4) & 0x03), NULL); 1170 if (0 != (mask_b & 0x10)) 1171 add_opt (opts, &nopt, MHD_OPTION_CLIENT_DISCIPLINE_LVL, 1172 (intptr_t) PICK (discipline_tbl, data[10] & 0x07), NULL); 1173 if (0 != (mask_b & 0x20)) 1174 add_opt (opts, &nopt, MHD_OPTION_SIGPIPE_HANDLED_BY_APP, 1175 /* Truthful: LLVMFuzzerTestOneInput() ignores SIGPIPE 1176 process-wide before anything else happens. */ 1177 (intptr_t) 1, NULL); 1178 if (0 != (mask_b & 0x40)) 1179 add_opt (opts, &nopt, MHD_OPTION_APP_FD_SETSIZE, 1180 (intptr_t) PICK (fd_setsize_tbl, (data[11] >> 3) & 0x07), NULL); 1181 if (0 != (mask_b & 0x80)) 1182 add_opt (opts, &nopt, MHD_OPTION_ALLOW_BIN_ZERO_IN_URI_PATH, 1183 (intptr_t) (int) ((data[11] >> 5) & 0x03), NULL); 1184 1185 if (0 != (mask_d & 0x01)) 1186 add_opt (opts, &nopt, MHD_OPTION_DIGEST_AUTH_NONCE_BIND_TYPE, 1187 (intptr_t) (unsigned int) ((data[9] >> 2) & 0x0F), NULL); 1188 if (0 != (mask_d & 0x02)) 1189 add_opt (opts, &nopt, MHD_OPTION_DIGEST_AUTH_DEFAULT_NONCE_TIMEOUT, 1190 (intptr_t) (unsigned int) (data[15] & 0x7F), NULL); 1191 if (0 != (mask_d & 0x04)) 1192 add_opt (opts, &nopt, MHD_OPTION_DIGEST_AUTH_DEFAULT_MAX_NC, 1193 (intptr_t) (uint32_t) data[15], NULL); 1194 if (0 != (mask_d & 0x08)) 1195 add_opt (opts, &nopt, MHD_OPTION_TCP_FASTOPEN_QUEUE_SIZE, 1196 (intptr_t) PICK (fastopen_tbl, (data[11] >> 6) & 0x03), NULL); 1197 if (0 != (mask_d & 0x10)) 1198 add_opt (opts, &nopt, MHD_OPTION_TLS_NO_ALPN, 1199 (intptr_t) (int) (data[15] & 0x01), NULL); 1200 if (0 != (mask_d & 0x20)) 1201 /* MHD_INVALID_SOCKET means "use the socket MHD creates itself"; a 1202 real descriptor is deliberately not offered, because MHD takes 1203 ownership of it and the harness would have to track that. */ 1204 add_opt (opts, &nopt, MHD_OPTION_LISTEN_SOCKET, 1205 (intptr_t) MHD_INVALID_SOCKET, NULL); 1206 1207 /* MHD_OPTION_DIGEST_AUTH_RANDOM keeps the caller's buffer, the _COPY 1208 variant makes MHD malloc() a copy that MHD_stop_daemon() has to free 1209 again -- a leak this harness would notice immediately. */ 1210 if (0 != (data[5] & 0x20)) 1211 add_opt (opts, &nopt, 1212 (0 != (data[5] & 0x40)) 1213 ? MHD_OPTION_DIGEST_AUTH_RANDOM_COPY 1214 : MHD_OPTION_DIGEST_AUTH_RANDOM, 1215 (intptr_t) sizeof (digest_rnd), 1216 (void *) (intptr_t) digest_rnd); 1217 1218 /* A bind address is rejected together with MHD_USE_NO_LISTEN_SOCKET; 1219 same reasoning as for the thread pool above. */ 1220 if ( (0 != (data[5] & 0x80)) && 1221 (cfg.listen_sock || misfit) ) 1222 { 1223 prepare_sock_addrs (); 1224 /* The address family has to match MHD_USE_IPv6, otherwise MHD 1225 rejects the daemon; that check is reached through the raw flag 1226 escape rather than by feeding a mismatched address on purpose. */ 1227 #ifdef AF_INET6 1228 if (0 != (flags & MHD_USE_IPv6)) 1229 { 1230 if (0 != (data[15] & 0x02)) 1231 add_opt (opts, &nopt, MHD_OPTION_SOCK_ADDR_LEN, 1232 (intptr_t) (socklen_t) sizeof (bind6), &bind6); 1233 else 1234 add_opt (opts, &nopt, MHD_OPTION_SOCK_ADDR, 0, &bind6); 1235 } 1236 else 1237 #endif 1238 if (0 != (data[15] & 0x02)) 1239 add_opt (opts, &nopt, MHD_OPTION_SOCK_ADDR_LEN, 1240 (intptr_t) (socklen_t) sizeof (bind4), &bind4); 1241 else 1242 add_opt (opts, &nopt, MHD_OPTION_SOCK_ADDR, 0, &bind4); 1243 } 1244 1245 /* An option number MHD does not know at all. parse_options_va() 1246 answers MHD_NO for it and MHD_start_daemon() returns NULL, which is 1247 the branch this reaches; keep it last so that everything above has 1248 already been parsed. */ 1249 if (0 != (mask_d & 0x40)) 1250 add_opt (opts, &nopt, (enum MHD_OPTION) (200 + (data[15] & 0x0F)), 1251 0, NULL); 1252 1253 opts[nopt].option = MHD_OPTION_END; 1254 opts[nopt].value = 0; 1255 opts[nopt].ptr_value = NULL; 1256 return nopt; 1257 } 1258 1259 1260 /* The three callback options whose "not set" state is representable as a 1261 NULL function pointer; MHD checks all three for NULL before calling 1262 them, so passing NULL is exactly equivalent to omitting the option. */ 1263 typedef void *(*fuzz_uri_log_cb)(void *, const char *, 1264 struct MHD_Connection *); 1265 1266 #define VARARG_CALLBACKS \ 1267 MHD_OPTION_NOTIFY_COMPLETED, cb_completed, NULL, \ 1268 MHD_OPTION_NOTIFY_CONNECTION, cb_notify, NULL, \ 1269 MHD_OPTION_URI_LOG_CALLBACK, cb_uri_log, NULL 1270 1271 /* The accept policy callback, defined with the accept path further 1272 down. */ 1273 static enum MHD_Result 1274 apc_cb (void *cls, 1275 const struct sockaddr *addr, 1276 socklen_t addrlen); 1277 1278 1279 /** 1280 * Start the daemon. 1281 * 1282 * MHD_OPTION_EXTERNAL_LOGGER and MHD_OPTION_UNESCAPE_CALLBACK cannot be 1283 * "passed as NULL": MHD calls both unconditionally, so their absence has 1284 * to be expressed by leaving the option out of the varargs, which is why 1285 * there are four spellings of the same call. The logger comes first on 1286 * purpose -- MHD_OPTION_EXTERNAL_LOGGER only catches the messages 1287 * emitted after it has been parsed, and parsing the option array emits 1288 * several. 1289 */ 1290 static struct MHD_Daemon * 1291 start_daemon_variant (unsigned int flags, 1292 struct MHD_OptionItem *opts, 1293 unsigned int cbsel) 1294 { 1295 MHD_AcceptPolicyCallback apc = cfg.accept_policy ? &apc_cb : NULL; 1296 MHD_RequestCompletedCallback cb_completed = 1297 (0 != (cbsel & 0x01)) ? &completed_cb : NULL; 1298 MHD_NotifyConnectionCallback cb_notify = 1299 (0 != (cbsel & 0x02)) ? ¬ify_connection_cb : NULL; 1300 fuzz_uri_log_cb cb_uri_log = 1301 (0 != (cbsel & 0x04)) ? &uri_log_cb : NULL; 1302 1303 switch ((cbsel >> 3) & 0x03) 1304 { 1305 case 1: 1306 return MHD_start_daemon (flags, 0, apc, NULL, &ahc, NULL, 1307 MHD_OPTION_ARRAY, opts, 1308 VARARG_CALLBACKS, 1309 MHD_OPTION_UNESCAPE_CALLBACK, &unescape_cb, NULL, 1310 MHD_OPTION_END); 1311 case 2: 1312 return MHD_start_daemon (flags, 0, apc, NULL, &ahc, NULL, 1313 MHD_OPTION_EXTERNAL_LOGGER, &logger_cb, NULL, 1314 MHD_OPTION_ARRAY, opts, 1315 VARARG_CALLBACKS, 1316 MHD_OPTION_END); 1317 case 3: 1318 return MHD_start_daemon (flags, 0, apc, NULL, &ahc, NULL, 1319 MHD_OPTION_EXTERNAL_LOGGER, &logger_cb, NULL, 1320 MHD_OPTION_ARRAY, opts, 1321 VARARG_CALLBACKS, 1322 MHD_OPTION_UNESCAPE_CALLBACK, &unescape_cb, NULL, 1323 MHD_OPTION_END); 1324 default: 1325 return MHD_start_daemon (flags, 0, apc, NULL, &ahc, NULL, 1326 MHD_OPTION_ARRAY, opts, 1327 VARARG_CALLBACKS, 1328 MHD_OPTION_END); 1329 } 1330 } 1331 1332 1333 /** 1334 * The accept policy callback. MHD_accept_connection() calls it with the 1335 * peer address of every socket it accepts, and answering MHD_NO makes 1336 * new_connection_prepare_() close the socket and drop the IP-limit entry 1337 * before any connection object exists -- a path nothing else here 1338 * reaches. (It does *not* reach new_connection_close_(): that one is 1339 * only called from close_all_connections(), for connections queued by 1340 * MHD_add_connection() and never started. See the byte 3 bit 4 1341 * scenario in fuzz_eventloop.c.) 1342 */ 1343 static enum MHD_Result 1344 apc_cb (void *cls, 1345 const struct sockaddr *addr, 1346 socklen_t addrlen) 1347 { 1348 volatile size_t sink; 1349 1350 (void) cls; 1351 sink = (size_t) addrlen + ((NULL != addr) ? (size_t) addr->sa_family : 0u); 1352 (void) sink; 1353 return cfg.accept_policy_deny ? MHD_NO : MHD_YES; 1354 } 1355 1356 1357 /** 1358 * Really connect to the daemon's listening socket. 1359 * 1360 * MHD_add_connection() bypasses accept(), so without this the whole 1361 * accept path -- MHD_accept_connection(), the accept policy callback, 1362 * the listen-socket branches of the three event loops -- is unreachable. 1363 * The client end is given SO_LINGER {1, 0} so that close() sends a RST 1364 * and neither side ends up in TIME_WAIT: at fuzzing rates the ephemeral 1365 * port range would otherwise be exhausted within a couple of minutes. 1366 * 1367 * @param d the daemon, which must have a listening socket 1368 * @param flags the flags it was started with, to pick the address family 1369 * @return the connected socket, or -1 1370 */ 1371 static int 1372 connect_real (struct MHD_Daemon *d, 1373 unsigned int flags) 1374 { 1375 const union MHD_DaemonInfo *di; 1376 struct linger lg; 1377 int s; 1378 uint16_t port; 1379 1380 di = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT); 1381 if ( (NULL == di) || 1382 (0 == di->port) ) 1383 return -1; 1384 port = (uint16_t) di->port; 1385 prepare_sock_addrs (); 1386 #ifdef AF_INET6 1387 if (0 != (flags & MHD_USE_IPv6)) 1388 { 1389 struct sockaddr_in6 to = bind6; 1390 1391 to.sin6_port = htons (port); 1392 s = socket (AF_INET6, SOCK_STREAM, 0); 1393 if (0 > s) 1394 return -1; 1395 if (0 != connect (s, (const struct sockaddr *) &to, sizeof (to))) 1396 { 1397 (void) close (s); 1398 return -1; 1399 } 1400 } 1401 else 1402 #endif 1403 { 1404 struct sockaddr_in to = bind4; 1405 1406 to.sin_port = htons (port); 1407 s = socket (AF_INET, SOCK_STREAM, 0); 1408 if (0 > s) 1409 return -1; 1410 if (0 != connect (s, (const struct sockaddr *) &to, sizeof (to))) 1411 { 1412 (void) close (s); 1413 return -1; 1414 } 1415 } 1416 lg.l_onoff = 1; 1417 lg.l_linger = 0; 1418 (void) setsockopt (s, SOL_SOCKET, SO_LINGER, &lg, sizeof (lg)); 1419 stat_real_conns++; 1420 return s; 1421 } 1422 1423 1424 /** 1425 * Ask MHD_is_feature_supported() about one feature. The function is a 1426 * large switch in daemon.c that no other harness touches; the index 1427 * comes off the input so that invalid values reach its default branch 1428 * as well. 1429 */ 1430 static void 1431 query_feature (uint8_t sel) 1432 { 1433 volatile int sink; 1434 1435 sink = (int) MHD_is_feature_supported ((enum MHD_FEATURE) (sel % 40u)); 1436 (void) sink; 1437 } 1438 1439 1440 /** 1441 * Read everything MHD_get_daemon_info() offers. All of it lives in 1442 * daemon.c and none of it is reachable from the other harnesses. 1443 */ 1444 static void 1445 query_daemon_info (struct MHD_Daemon *d) 1446 { 1447 volatile unsigned int sink = 0; 1448 const union MHD_DaemonInfo *di; 1449 1450 sink += (unsigned int) (NULL != MHD_get_version ()); 1451 sink += MHD_get_version_bin (); 1452 1453 di = MHD_get_daemon_info (d, MHD_DAEMON_INFO_CURRENT_CONNECTIONS); 1454 if (NULL != di) 1455 sink += di->num_connections; 1456 di = MHD_get_daemon_info (d, MHD_DAEMON_INFO_FLAGS); 1457 if (NULL != di) 1458 sink += (unsigned int) di->flags; 1459 di = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT); 1460 if (NULL != di) 1461 sink += di->port; 1462 di = MHD_get_daemon_info (d, MHD_DAEMON_INFO_LISTEN_FD); 1463 if (NULL != di) 1464 sink += (unsigned int) (di->listen_fd + 1); 1465 di = MHD_get_daemon_info (d, MHD_DAEMON_INFO_EPOLL_FD); 1466 if (NULL != di) 1467 sink += (unsigned int) (di->listen_fd + 1); 1468 (void) sink; 1469 } 1470 1471 1472 /* ------------------------------------------------------------------ */ 1473 /* The fuzz target */ 1474 /* ------------------------------------------------------------------ */ 1475 1476 int 1477 LLVMFuzzerTestOneInput (const uint8_t *data, 1478 size_t size) 1479 { 1480 struct MHD_Daemon *d; 1481 struct MHD_OptionItem opts[MAX_OPTS]; 1482 unsigned int flags; 1483 int sock = -1; 1484 int rsock = -1; 1485 size_t pos; 1486 unsigned int nseg = 0; 1487 unsigned int nconn = 1; 1488 unsigned int cbsel; 1489 MHD_socket quiesced = MHD_INVALID_SOCKET; 1490 1491 /* Must happen before the first write() into the socketpair, and must 1492 not be left to the built-in driver: fuzz_install_handlers() is 1493 compiled out under -DFUZZ_NO_MAIN, which is exactly the build every 1494 external fuzzing engine uses. The call is idempotent. See 1495 fuzz_ignore_sigpipe() in fuzz_common.h for what happens without it. */ 1496 fuzz_ignore_sigpipe (); 1497 1498 /* The sixteen configuration bytes are mandatory. An input of exactly 1499 that length is fine and starts a daemon with no traffic. */ 1500 if (size < CFG_BYTES) 1501 return 0; 1502 1503 memset (&cfg, 0, sizeof (cfg)); 1504 memset (pending_resume, 0, sizeof (pending_resume)); 1505 tearing_down = 0; 1506 1507 flags = flags_from_input (data); 1508 if (cfg.listen_sock) 1509 flags &= ~((unsigned int) MHD_USE_NO_LISTEN_SOCKET); 1510 else 1511 flags |= MHD_USE_NO_LISTEN_SOCKET; 1512 if (cfg.err_log) 1513 flags |= MHD_USE_ERROR_LOG; 1514 else 1515 flags &= ~((unsigned int) MHD_USE_ERROR_LOG); 1516 1517 cfg.uses_threads = (0 != (flags & (MHD_USE_INTERNAL_POLLING_THREAD 1518 | MHD_USE_THREAD_PER_CONNECTION))); 1519 if (cfg.uses_threads && 1520 (0 == (flags & MHD_USE_NO_THREAD_SAFETY))) 1521 { 1522 /* Without the inter-thread communication channel a worker only 1523 notices a connection handed to it by MHD_add_connection() when its 1524 own poll times out, so every threaded iteration would pay the full 1525 wait. MHD forces ITC on anyway whenever there is no listen 1526 socket; this extends that to the daemons that have one. */ 1527 flags |= MHD_USE_ITC; 1528 } 1529 1530 cfg.loop_mode = (unsigned int) (data[12] & 0x03); 1531 cfg.quiesce = (0 != (data[12] & 0x04)); 1532 if (cfg.quiesce && 1533 cfg.uses_threads && 1534 uses_epoll (flags) && 1535 (! quiesce_epoll_race_allowed ())) 1536 cfg.quiesce = 0; 1537 cfg.daemon_info = (0 != (data[12] & 0x08)); 1538 cfg.suspend_mode = (0 != (flags & SUSPEND_BIT)) 1539 ? (int) ((data[12] >> 4) & 0x03) 1540 : 0; 1541 if (3 == cfg.suspend_mode) 1542 cfg.suspend_mode = 1; 1543 cfg.nconn_max = 1u + (unsigned int) ((data[12] >> 6) & 0x03); 1544 if (cfg.nconn_max > MAX_CONNECTIONS) 1545 cfg.nconn_max = MAX_CONNECTIONS; 1546 1547 cfg.resp_kind = (unsigned int) (data[13] & 0x03); 1548 cfg.handler_no = (0 != (data[13] & 0x04)); 1549 cfg.conn_option = (0 != (data[13] & 0x08)); 1550 cfg.accept_policy = (0 != (data[13] & 0x10)); 1551 cfg.accept_policy_deny = (0 != (data[13] & 0x20)); 1552 /* One iteration in four of those that have a listening socket at all; 1553 a real connect()/accept() pair is much more expensive than 1554 MHD_add_connection() on a socketpair. */ 1555 cfg.real_connect = cfg.listen_sock && (0xC0 == (data[13] & 0xC0)); 1556 1557 (void) build_options (data, opts, flags); 1558 1559 cbsel = (unsigned int) data[5]; 1560 /* MHD_USE_ERROR_LOG without MHD_OPTION_EXTERNAL_LOGGER sends every 1561 message MHD produces to stderr through MHD_default_logger_, which 1562 at fuzzing rates is tens of megabytes of noise. The flag is far too 1563 valuable to drop -- with it set, several hundred MHD_DLOG() call 1564 sites in daemon.c become live -- so the logger is forced on instead 1565 (bit 4 of the callback selector). */ 1566 if (cfg.err_log) 1567 cbsel |= 0x10u; 1568 1569 MHD_set_panic_func (&panic_cb, NULL); 1570 d = start_daemon_variant (flags, opts, cbsel); 1571 if (NULL == d) 1572 { 1573 /* Entirely normal: an unsupported flag combination, an option MHD 1574 rejects, a bind() failure, ... */ 1575 stat_daemons_failed++; 1576 return 0; 1577 } 1578 stat_daemons++; 1579 if (cfg.uses_threads) 1580 stat_threaded++; 1581 if (! stats_registered) 1582 { 1583 stats_registered = 1; 1584 (void) atexit (&print_stats); 1585 } 1586 1587 if (cfg.daemon_info) 1588 query_daemon_info (d); 1589 query_feature (data[15]); 1590 1591 if (cfg.real_connect) 1592 { 1593 rsock = connect_real (d, flags); 1594 if (0 <= rsock) 1595 { 1596 static const char req[] = "GET / HTTP/1.1\r\nHost: x\r\n\r\n"; 1597 1598 (void) send (rsock, req, sizeof (req) - 1, MSG_DONTWAIT); 1599 if (cfg.uses_threads) 1600 wait_threaded (rsock); 1601 else 1602 pump (d, rsock, PUMP_ROUNDS_LONG); 1603 } 1604 } 1605 1606 (void) new_connection (d, &sock); 1607 1608 pos = CFG_BYTES; 1609 while ( (pos + 2 <= size) && 1610 (nseg < MAX_SEGMENTS) ) 1611 { 1612 unsigned int hdr = (unsigned int) data[pos] 1613 | ((unsigned int) data[pos + 1] << 8); 1614 unsigned int op = hdr >> 14; 1615 size_t slen = (size_t) (hdr & 0x3FFF); 1616 1617 pos += 2; 1618 nseg++; 1619 if (slen > size - pos) 1620 slen = size - pos; 1621 1622 if ( (3 == op) && 1623 (nconn < cfg.nconn_max) ) 1624 { 1625 close_connection (d, &sock); 1626 (void) new_connection (d, &sock); 1627 nconn++; 1628 } 1629 if (0 != slen) 1630 send_all (d, sock, data + pos, slen); 1631 pos += slen; 1632 if (cfg.uses_threads) 1633 continue; /* the threaded shape drains at close */ 1634 pump (d, sock, (0 == op) ? PUMP_ROUNDS : PUMP_ROUNDS_LONG); 1635 } 1636 1637 close_connection (d, &sock); 1638 pump (d, sock, 4); 1639 if (0 <= rsock) 1640 { 1641 (void) shutdown (rsock, SHUT_WR); 1642 if (cfg.uses_threads) 1643 wait_threaded (rsock); 1644 else 1645 pump (d, rsock, 4); 1646 (void) close (rsock); 1647 rsock = -1; 1648 } 1649 1650 /* A connection left suspended makes MHD_stop_daemon() MHD_PANIC(), and 1651 MHD_resume_connection() alone is not enough: it only raises a flag, 1652 and the connection is taken off the daemon's suspended list by 1653 MHD_run(). So flush and run until nothing is parked any more. 1654 tearing_down keeps the handler from parking anything new, which is 1655 what bounds this loop; the cap is only a backstop. */ 1656 tearing_down = 1; 1657 if (! cfg.uses_threads) 1658 { 1659 unsigned int i; 1660 1661 for (i = 0; i < MAX_CONNECTIONS + 2u; i++) 1662 { 1663 if (! pending_resume_flush ()) 1664 break; 1665 (void) MHD_run (d); 1666 } 1667 } 1668 1669 if (cfg.quiesce) 1670 { 1671 /* The returned socket belongs to the caller from here on, and with 1672 internal threads it must not be closed before MHD_stop_daemon() 1673 has joined them. */ 1674 quiesced = MHD_quiesce_daemon (d); 1675 } 1676 MHD_stop_daemon (d); 1677 if (MHD_INVALID_SOCKET != quiesced) 1678 (void) close ((int) quiesced); 1679 return 0; 1680 } 1681 1682 1683 /* ------------------------------------------------------------------ */ 1684 /* Structure-aware generator */ 1685 /* ------------------------------------------------------------------ */ 1686 1687 /* Defined unconditionally, exactly like the rest of the harness API: 1688 only main() may live behind #ifndef FUZZ_NO_MAIN, and fuzz_common.h 1689 already declares these three with FUZZ_UNUSED so that the libFuzzer 1690 and AFL++ builds, which never call them, compile without a warning. */ 1691 1692 struct sbuf 1693 { 1694 uint8_t *p; 1695 size_t len; 1696 size_t cap; 1697 }; 1698 1699 1700 static void 1701 sb_raw (struct sbuf *b, 1702 const void *s, 1703 size_t n) 1704 { 1705 if (b->len + n > b->cap) 1706 n = b->cap - b->len; 1707 memcpy (b->p + b->len, s, n); 1708 b->len += n; 1709 } 1710 1711 1712 static void 1713 sb_str (struct sbuf *b, 1714 const char *s) 1715 { 1716 sb_raw (b, s, strlen (s)); 1717 } 1718 1719 1720 static void 1721 sb_u64 (struct sbuf *b, 1722 uint64_t v, 1723 int hex) 1724 { 1725 char tmp[32]; 1726 1727 (void) snprintf (tmp, sizeof (tmp), 1728 hex ? "%llx" : "%llu", 1729 (unsigned long long) v); 1730 sb_str (b, tmp); 1731 } 1732 1733 1734 static const char *const gen_methods[] = { 1735 "GET", "POST", "HEAD", "PUT", "OPTIONS", "DELETE", "TRACE", "CONNECT", 1736 "PATCH", "get", "BREW", "" 1737 }; 1738 1739 static const char *const gen_targets[] = { 1740 "/", "/a", "/a/b/c", "*", "http://x/a", "/%41%42", "/a?b=c&d", 1741 "/very/long/path/that/does/not/fit/into/a/small/connection/memory/pool", 1742 "/a?novalue", "//", "/.%2e/", "/\x01" 1743 }; 1744 1745 static const char *const gen_versions[] = { 1746 "HTTP/1.1", "HTTP/1.0", "HTTP/1.2", "HTTP/0.9", "HTTP/1", "" 1747 }; 1748 1749 static const char *const gen_hdr_names[] = { 1750 "Host", "Connection", "Accept", "User-Agent", "Cookie", "Expect", 1751 "Content-Type", "X-Fuzz", "Upgrade", "Accept-Encoding", "Range", 1752 "If-Modified-Since" 1753 }; 1754 1755 static const char *const gen_hdr_values[] = { 1756 "x", "keep-alive", "close", "*/*", "a=b; c=d", "100-continue", 1757 "text/plain", "1", "fuzz-protocol", "gzip", "bytes=0-1", "chunked" 1758 }; 1759 1760 1761 /** 1762 * Emit one HTTP request into @a b. 1763 * 1764 * The shapes are deliberately unambitious -- the request parser is 1765 * fuzz_request's subject, not this harness's. What matters here is that 1766 * the bytes form something MHD will actually take through its state 1767 * machine under whatever daemon configuration the configuration bytes 1768 * describe, so that the option handling is exercised against a live 1769 * connection rather than against a connection MHD drops on the first 1770 * byte. 1771 */ 1772 static void 1773 gen_request (struct fuzz_rng *rng, 1774 struct sbuf *b, 1775 unsigned int shape) 1776 { 1777 unsigned int nhdr; 1778 unsigned int i; 1779 1780 switch (shape % 10) 1781 { 1782 case 9: 1783 /* Not HTTP at all: MHD has to reject it, which is its own path. */ 1784 for (i = 0; i < 24; i++) 1785 { 1786 uint8_t c = fuzz_byte (rng); 1787 1788 sb_raw (b, &c, 1); 1789 } 1790 return; 1791 case 6: 1792 /* Two pipelined requests on one connection. */ 1793 sb_str (b, "GET /a HTTP/1.1\r\nHost: x\r\n\r\n"); 1794 sb_str (b, "GET /b HTTP/1.1\r\nHost: x\r\nConnection: close\r\n\r\n"); 1795 return; 1796 default: 1797 break; 1798 } 1799 1800 sb_str (b, gen_methods[fuzz_below (rng, (uint32_t) (sizeof (gen_methods) 1801 / sizeof (gen_methods[0] 1802 )))]); 1803 sb_str (b, " "); 1804 sb_str (b, gen_targets[fuzz_below (rng, (uint32_t) (sizeof (gen_targets) 1805 / sizeof (gen_targets[0] 1806 )))]); 1807 sb_str (b, " "); 1808 sb_str (b, gen_versions[fuzz_below (rng, 1809 (uint32_t) (sizeof (gen_versions) 1810 / sizeof (gen_versions[0]))) 1811 ]); 1812 sb_str (b, "\r\n"); 1813 1814 nhdr = (5 == (shape % 10)) ? (8 + fuzz_below (rng, 24)) : fuzz_below (rng, 5); 1815 for (i = 0; i < nhdr; i++) 1816 { 1817 sb_str (b, gen_hdr_names[fuzz_below (rng, 1818 (uint32_t) (sizeof (gen_hdr_names) 1819 / sizeof (gen_hdr_names[0] 1820 )))]); 1821 sb_str (b, ": "); 1822 sb_str (b, gen_hdr_values[fuzz_below (rng, 1823 (uint32_t) (sizeof (gen_hdr_values) 1824 / sizeof (gen_hdr_values[ 1825 0])))]); 1826 sb_str (b, "\r\n"); 1827 } 1828 1829 switch (shape % 10) 1830 { 1831 case 2: /* Content-Length body */ 1832 { 1833 uint32_t n = fuzz_below (rng, 64); 1834 1835 sb_str (b, "Content-Length: "); 1836 sb_u64 (b, n, 0); 1837 sb_str (b, "\r\n\r\n"); 1838 for (i = 0; i < n; i++) 1839 sb_str (b, "A"); 1840 break; 1841 } 1842 case 3: /* chunked body */ 1843 { 1844 unsigned int nch = 1 + fuzz_below (rng, 3); 1845 1846 sb_str (b, "Transfer-Encoding: chunked\r\n\r\n"); 1847 for (i = 0; i < nch; i++) 1848 { 1849 uint32_t n = 1 + fuzz_below (rng, 16); 1850 uint32_t k; 1851 1852 sb_u64 (b, n, 1); 1853 sb_str (b, "\r\n"); 1854 for (k = 0; k < n; k++) 1855 sb_str (b, "B"); 1856 sb_str (b, "\r\n"); 1857 } 1858 sb_str (b, "0\r\n\r\n"); 1859 break; 1860 } 1861 case 4: /* expect 100-continue */ 1862 sb_str (b, "Expect: 100-continue\r\nContent-Length: 4\r\n\r\nabcd"); 1863 break; 1864 case 7: /* upgrade request */ 1865 sb_str (b, "Connection: Upgrade\r\nUpgrade: fuzz-protocol\r\n\r\n"); 1866 break; 1867 case 8: /* truncated: MHD keeps waiting for more */ 1868 sb_str (b, "X-Trunc: "); 1869 break; 1870 default: 1871 sb_str (b, "\r\n"); 1872 break; 1873 } 1874 } 1875 1876 1877 /** 1878 * Serialise @a body into the segment stream understood by 1879 * LLVMFuzzerTestOneInput(). 1880 */ 1881 static void 1882 emit_segments (struct fuzz_rng *rng, 1883 struct sbuf *out, 1884 const uint8_t *body, 1885 size_t body_len, 1886 int new_conn_first) 1887 { 1888 size_t off = 0; 1889 int first = 1; 1890 1891 while (off < body_len) 1892 { 1893 size_t chunk; 1894 unsigned int op; 1895 uint8_t hdr[2]; 1896 unsigned int hv; 1897 1898 switch (fuzz_below (rng, 5)) 1899 { 1900 case 0: 1901 chunk = 1; 1902 break; 1903 case 1: 1904 chunk = 2 + fuzz_below (rng, 8); 1905 break; 1906 default: 1907 chunk = body_len - off; 1908 break; 1909 } 1910 if (chunk > body_len - off) 1911 chunk = body_len - off; 1912 if (chunk > 0x3FFF) 1913 chunk = 0x3FFF; 1914 op = (first && new_conn_first) ? 3u : (fuzz_chance (rng, 5) ? 2u : 0u); 1915 hv = (op << 14) | (unsigned int) chunk; 1916 hdr[0] = (uint8_t) (hv & 0xFF); 1917 hdr[1] = (uint8_t) (hv >> 8); 1918 if (out->len + 2 + chunk > out->cap) 1919 return; 1920 sb_raw (out, hdr, 2); 1921 sb_raw (out, body + off, chunk); 1922 off += chunk; 1923 first = 0; 1924 } 1925 } 1926 1927 1928 /** 1929 * The generator. 1930 * 1931 * Purely random configuration bytes are already useful for this harness 1932 * -- unlike an HTTP request, an option array has no grammar to get wrong 1933 * -- but two things still need help. Byte 0 is biased into the range of 1934 * mode_tbl[] so that most iterations get a daemon that actually starts, 1935 * and the tail of the input has to look like HTTP, or MHD closes every 1936 * connection before any of the configured behaviour has a chance to 1937 * matter. 1938 */ 1939 static size_t 1940 fuzz_generate (struct fuzz_rng *rng, 1941 uint8_t *buf, 1942 size_t cap) 1943 { 1944 struct sbuf out; 1945 uint8_t cfg_bytes[CFG_BYTES]; 1946 uint8_t req[GEN_BUF_SIZE]; 1947 struct sbuf rb; 1948 unsigned int nreq; 1949 unsigned int i; 1950 1951 out.p = buf; 1952 out.len = 0; 1953 out.cap = cap; 1954 1955 for (i = 0; i < CFG_BYTES; i++) 1956 cfg_bytes[i] = fuzz_byte (rng); 1957 1958 /* Byte 0 selects the daemon shape; keep it inside the table so that 1959 the value is not folded by the modulo in an uneven way. */ 1960 cfg_bytes[0] = (uint8_t) fuzz_below (rng, (uint32_t) MODE_COUNT); 1961 /* The raw flag escape is 1 in 4 of the byte-2 values, which is far too 1962 often: those daemons mostly fail to start and never reach the option 1963 handling. Clear the escape unless it is explicitly rolled. */ 1964 if (! fuzz_chance (rng, 10)) 1965 cfg_bytes[2] &= (uint8_t) ~0xC0u; 1966 else 1967 cfg_bytes[2] |= (uint8_t) 0xC0u; 1968 /* Likewise the deliberately invalid option number: useful, but every 1969 input carrying it ends in MHD_start_daemon() == NULL. */ 1970 if (! fuzz_chance (rng, 20)) 1971 cfg_bytes[14] &= (uint8_t) ~0x40u; 1972 1973 sb_raw (&out, cfg_bytes, CFG_BYTES); 1974 1975 nreq = 1u + (fuzz_chance (rng, 4) ? 1u : 0u); 1976 for (i = 0; i < nreq; i++) 1977 { 1978 rb.p = req; 1979 rb.len = 0; 1980 rb.cap = sizeof (req); 1981 gen_request (rng, &rb, fuzz_below (rng, 10)); 1982 emit_segments (rng, &out, req, rb.len, 1983 (0 != i) || fuzz_chance (rng, 6)); 1984 } 1985 return out.len; 1986 } 1987 1988 1989 /* ------------------------------------------------------------------ */ 1990 /* Built-in seed corpus */ 1991 /* ------------------------------------------------------------------ */ 1992 1993 /** 1994 * A seed is sixteen configuration bytes plus one request, rendered into 1995 * the wire format at run time so that segment lengths never have to be 1996 * spelled out by hand. 1997 * 1998 * Every seed turns on exactly one area, so that a corpus minimiser keeps 1999 * them distinguishable, and the configuration bytes of a seed that does 2000 * not care about an area are zero -- which is the plainest setting: 2001 * external polling with select(), no listen socket, no options at all, 2002 * one connection, MHD_run() as the event loop. 2003 */ 2004 struct seed_def 2005 { 2006 const char *name; 2007 unsigned char cfg[CFG_BYTES]; 2008 const char *req; 2009 }; 2010 2011 /* Byte positions inside seed_def::cfg, for readability. */ 2012 #define C_MODE 0 2013 #define C_FLAGA 1 2014 #define C_FLAGB 2 2015 #define C_OPTA 3 2016 #define C_OPTB 4 2017 #define C_OPTC 5 2018 #define C_VAL_MEM 6 2019 #define C_VAL_CONN 7 2020 #define C_VAL_THR 8 2021 #define C_VAL_DAUTH 9 2022 #define C_VAL_DISC 10 2023 #define C_VAL_SOCK 11 2024 #define C_DRIVE 12 2025 #define C_HANDLER 13 2026 #define C_OPTD 14 2027 #define C_SPARE 15 2028 2029 #define REQ_PLAIN "GET /a HTTP/1.1\r\nHost: x\r\n\r\n" 2030 #define REQ_CLOSE "GET /a HTTP/1.1\r\nHost: x\r\nConnection: close\r\n\r\n" 2031 #define REQ_POST \ 2032 "POST /a HTTP/1.1\r\nHost: x\r\nContent-Length: 4\r\n\r\nabcd" 2033 #define REQ_CHUNKED \ 2034 "POST /a HTTP/1.1\r\nHost: x\r\nTransfer-Encoding: chunked\r\n" \ 2035 "\r\n3\r\nabc\r\n0\r\n\r\n" 2036 2037 static const struct seed_def seeds[] = { 2038 /* ---- the event loops, all external ---- */ 2039 { "plain-external-run", 2040 { 0 }, REQ_PLAIN }, 2041 { "external-fdset2", 2042 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01 | 0x08, 0, 0, 0 }, 2043 REQ_PLAIN }, 2044 { "external-run-wait", 2045 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x02, 0, 0, 0 }, 2046 REQ_PLAIN }, 2047 { "external-fdset-v1", 2048 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x03, 0, 0, 0 }, 2049 REQ_PLAIN }, 2050 { "external-epoll", 2051 { 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 2052 REQ_PLAIN }, 2053 { "external-auto", 2054 { 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 2055 REQ_PLAIN }, 2056 2057 /* ---- the daemon shapes that run their own threads ---- */ 2058 { "internal-thread-select", 2059 { 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 2060 REQ_CLOSE }, 2061 { "internal-thread-poll", 2062 { 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 2063 REQ_CLOSE }, 2064 { "internal-thread-epoll", 2065 { 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 2066 REQ_CLOSE }, 2067 { "thread-per-connection", 2068 { 21, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 2069 REQ_CLOSE }, 2070 { "thread-per-connection-poll", 2071 { 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 2072 REQ_CLOSE }, 2073 /* A thread pool needs MHD_OPTION_THREAD_POOL_SIZE (option mask A bit 2074 5) on top of an internal polling thread, and is rejected outright 2075 with thread-per-connection. */ 2076 { "thread-pool", 2077 { 18, 0, 0, 0x20, 0, 0, 0, 0, 0x00, 0, 0, 0, 0, 0, 0, 0 }, 2078 REQ_CLOSE }, 2079 { "thread-pool-epoll-stack", 2080 { 20, 0, 0, 0x20 | 0x40, 0, 0, 0, 0, 0x10 | 0x20, 0, 0, 0, 0, 0, 0, 0 }, 2081 REQ_CLOSE }, 2082 2083 /* ---- a real listening socket ---- */ 2084 { "listen-socket", 2085 { 0, 0, 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 2086 REQ_PLAIN }, 2087 { "listen-socket-reuse-backlog", 2088 { 0, 0, 0x01, 0x80, 0x01, 0, 0, 0, 0, 0, 0, 0x01 | 0x06, 0, 0, 0, 0 }, 2089 REQ_PLAIN }, 2090 { "listen-socket-sockaddr", 2091 { 0, 0, 0x01, 0, 0, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 2092 REQ_PLAIN }, 2093 { "listen-socket-ipv6-dual", 2094 { 0, 0, 0x01 | 0x02 | 0x04, 0, 0, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 2095 REQ_PLAIN }, 2096 { "listen-socket-quiesce", 2097 { 0, 0, 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x04 | 0x08, 0, 0, 0 }, 2098 REQ_PLAIN }, 2099 { "listen-socket-internal-thread", 2100 { 18, 0, 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x04, 0, 0, 0 }, 2101 REQ_CLOSE }, 2102 2103 /* ---- the accept() path: a real client on the listening socket ---- 2104 Byte 13 bit 0x40|0x80 asks for the connect(), bit 0x10 installs the 2105 accept policy callback and bit 0x20 makes it say no. Without these 2106 MHD_accept_connection() and the listen-socket branches of the three 2107 event loops are never entered at all: MHD_add_connection() bypasses 2108 accept() completely. */ 2109 { "real-connect", 2110 { 0, 0, 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xC0, 0, 0 }, 2111 REQ_PLAIN }, 2112 { "real-connect-accept-policy", 2113 { 0, 0, 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xC0 | 0x10, 0, 0 }, 2114 REQ_PLAIN }, 2115 { "real-connect-accept-denied", 2116 { 0, 0, 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xC0 | 0x10 | 0x20, 0, 0 }, 2117 REQ_PLAIN }, 2118 { "real-connect-internal-thread", 2119 { 18, 0, 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xC0, 0, 0 }, 2120 REQ_CLOSE }, 2121 { "real-connect-external-epoll", 2122 { 12, 0, 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xC0, 0, 0 }, 2123 REQ_PLAIN }, 2124 2125 /* ---- the memory options ---- */ 2126 { "tiny-pool", 2127 { 0, 0, 0, 0x01 | 0x02, 0, 0, 0x01 | 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 2128 REQ_PLAIN }, 2129 { "big-pool-big-increment", 2130 { 0, 0, 0, 0x01 | 0x02, 0, 0, 0x0C | 0x60, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 2131 REQ_POST }, 2132 2133 /* ---- the connection limits ---- */ 2134 { "connection-limit-one", 2135 { 0, 0, 0, 0x04, 0, 0, 0, 0x01, 0, 0, 0, 0, 0xC0, 0, 0, 0 }, 2136 REQ_PLAIN }, 2137 { "per-ip-limit-one", 2138 { 0, 0, 0, 0x08, 0, 0, 0, 0x10, 0, 0, 0, 0, 0xC0, 0, 0, 0 }, 2139 REQ_PLAIN }, 2140 { "connection-limit-zero", 2141 { 0, 0, 0, 0x04, 0, 0, 0, 0x00, 0, 0, 0, 0, 0, 0, 0, 0 }, 2142 REQ_PLAIN }, 2143 { "connection-timeout", 2144 { 0, 0, 0, 0x10, 0, 0, 0, 0, 0x01, 0, 0, 0, 0, 0, 0, 0 }, 2145 REQ_PLAIN }, 2146 2147 /* ---- parsing discipline and insanity ---- */ 2148 { "discipline-lowest", 2149 { 0, 0, 0, 0, 0x10, 0, 0, 0, 0, 0, 0x00, 0, 0, 0, 0, 0 }, 2150 "GET /a HTTP/1.1\r\n Host: x\r\n\r\n" }, 2151 { "discipline-highest-pedantic", 2152 { 0, 0x01, 0, 0, 0x10, 0, 0, 0, 0, 0, 0x05, 0, 0, 0, 0, 0 }, 2153 REQ_PLAIN }, 2154 { "strict-for-client", 2155 { 0, 0, 0, 0, 0x08, 0, 0, 0, 0, 0, 0x00, 0, 0, 0, 0, 0 }, 2156 REQ_PLAIN }, 2157 { "server-insanity", 2158 { 0, 0, 0, 0, 0x04, 0, 0, 0, 0, 0, 0x40, 0, 0, 0, 0, 0 }, 2159 REQ_PLAIN }, 2160 { "bin-zero-in-uri-path", 2161 { 0, 0, 0, 0, 0x80, 0, 0, 0, 0, 0, 0, 0x20, 0, 0, 0, 0 }, 2162 "GET /a%00b HTTP/1.1\r\nHost: x\r\n\r\n" }, 2163 { "app-fd-setsize", 2164 { 0, 0, 0, 0, 0x40, 0, 0, 0, 0, 0, 0, 0x00, 0x01, 0, 0, 0 }, 2165 REQ_PLAIN }, 2166 2167 /* ---- the callbacks ---- */ 2168 { "notify-completed", 2169 { 0, 0, 0, 0, 0, 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 2170 REQ_PLAIN }, 2171 { "notify-connection", 2172 { 0, 0, 0, 0, 0, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 2173 REQ_PLAIN }, 2174 { "uri-log-callback", 2175 { 0, 0, 0, 0, 0, 0x04, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 2176 "GET /a?q=%41 HTTP/1.1\r\nHost: x\r\n\r\n" }, 2177 { "external-logger", 2178 { 0, 0x40, 0, 0, 0, 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 2179 REQ_PLAIN }, 2180 { "unescape-callback", 2181 { 0, 0, 0, 0, 0, 0x08, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 2182 "GET /%41%42?a=%43 HTTP/1.1\r\nHost: x\r\n\r\n" }, 2183 { "all-callbacks-error-log", 2184 { 0, 0x40, 0, 0, 0, 0x01 | 0x02 | 0x04 | 0x08 | 0x10, 2185 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 2186 REQ_PLAIN }, 2187 2188 /* ---- digest-auth related options ---- */ 2189 { "digest-random", 2190 { 0, 0, 0, 0, 0x02, 0x20, 0, 0, 0, 0x02, 0, 0, 0, 0, 0, 0 }, 2191 REQ_PLAIN }, 2192 { "digest-random-copy", 2193 { 0, 0, 0, 0, 0x02, 0x20 | 0x40, 0, 0, 0, 0x01, 0, 0, 0, 0, 0, 0 }, 2194 REQ_PLAIN }, 2195 { "digest-nonce-bind-and-defaults", 2196 { 0, 0, 0, 0, 0, 0x20, 0, 0, 0, 0x3C, 0, 0, 0, 0, 2197 0x01 | 0x02 | 0x04, 0x11 }, 2198 REQ_PLAIN }, 2199 2200 /* ---- suspend / resume ---- */ 2201 { "suspend-immediate", 2202 { 0, 0x04, 0, 0, 0, 0x01, 0, 0, 0, 0, 0, 0, 0x10, 0, 0, 0 }, 2203 REQ_PLAIN }, 2204 { "suspend-deferred", 2205 { 0, 0x04, 0, 0, 0, 0x01, 0, 0, 0, 0, 0, 0, 0x20, 0, 0, 0 }, 2206 REQ_PLAIN }, 2207 { "suspend-deferred-two-connections", 2208 { 0, 0x04, 0, 0, 0, 0x01, 0, 0, 0, 0, 0, 0, 0x20 | 0x40, 0, 0, 0 }, 2209 REQ_PLAIN }, 2210 2211 /* ---- the remaining flag bits ---- */ 2212 { "turbo-itc-suppress-date", 2213 { 0, 0x02 | 0x10 | 0x20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 2214 REQ_PLAIN }, 2215 { "allow-upgrade", 2216 { 0, 0x08, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 2217 "GET / HTTP/1.1\r\nHost: x\r\nConnection: Upgrade\r\n" 2218 "Upgrade: fuzz-protocol\r\n\r\n" }, 2219 { "no-thread-safety", 2220 { 0, 0, 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 2221 REQ_PLAIN }, 2222 { "tcp-fastopen-listen", 2223 { 0, 0, 0x01 | 0x08, 0, 0, 0, 0, 0, 0, 0, 0, 0x40, 0, 0, 0x08, 0 }, 2224 REQ_PLAIN }, 2225 { "sigpipe-handled-by-app", 2226 { 0, 0, 0, 0, 0x20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 2227 REQ_PLAIN }, 2228 { "listen-socket-option-invalid-fd", 2229 { 0, 0, 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x20, 0 }, 2230 REQ_PLAIN }, 2231 2232 /* ---- inputs whose daemon must not start ---- */ 2233 /* Raw flag escape: MHD_USE_POLL together with MHD_USE_EPOLL. */ 2234 { "raw-flags-poll-and-epoll", 2235 { 0, 0x40, 0xC0 | 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 2236 REQ_PLAIN }, 2237 /* Raw flag escape: MHD_USE_EPOLL with MHD_USE_THREAD_PER_CONNECTION. */ 2238 { "raw-flags-epoll-thread-per-conn", 2239 { 0, 0x04 | 0x08, 0xC0 | 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 2240 REQ_PLAIN }, 2241 /* An option number MHD does not know. */ 2242 { "invalid-option-number", 2243 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x40, 0 }, 2244 REQ_PLAIN }, 2245 /* MHD_OPTION_THREAD_POOL_SIZE without an internal polling thread. */ 2246 { "thread-pool-without-threads", 2247 { 0, 0, 0, 0x20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 2248 REQ_PLAIN }, 2249 2250 /* ---- the handler behaviours ---- */ 2251 { "handler-returns-no", 2252 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x04, 0, 0 }, 2253 REQ_PLAIN }, 2254 { "empty-and-copied-responses", 2255 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01 | 0x08, 0, 0 }, 2256 REQ_CHUNKED }, 2257 { "error-response", 2258 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x03, 0, 0 }, 2259 REQ_POST }, 2260 2261 /* ---- everything at once ---- */ 2262 { "kitchen-sink-external", 2263 { 0, 0x02 | 0x04 | 0x10 | 0x20 | 0x40, 0x01, 2264 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x80, 2265 0x02 | 0x10 | 0x20 | 0x40 | 0x80, 2266 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20, 2267 0x63, 0x45, 0x21, 0x13, 0x02, 0x0A, 2268 0x01 | 0x04 | 0x08 | 0x20, 0x08, 0x01 | 0x02 | 0x04 | 0x10, 0x33 }, 2269 REQ_POST }, 2270 { "kitchen-sink-thread-pool", 2271 { 18, 0x02 | 0x10 | 0x20 | 0x40, 0x01, 2272 0x01 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80, 2273 0x01 | 0x02 | 0x80, 2274 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20, 2275 0x03, 0x46, 0x39, 0x02, 0x00, 0x0A, 2276 0x04 | 0x08, 0x00, 0x01 | 0x08, 0x22 }, 2277 REQ_CLOSE } 2278 }; 2279 2280 static uint8_t seed_render_buf[1024]; 2281 2282 2283 static size_t 2284 fuzz_seed_count (void) 2285 { 2286 return sizeof (seeds) / sizeof (seeds[0]); 2287 } 2288 2289 2290 static const uint8_t * 2291 fuzz_seed_get (size_t idx, 2292 size_t *len) 2293 { 2294 const struct seed_def *sd = &seeds[idx]; 2295 struct sbuf b; 2296 2297 b.p = seed_render_buf; 2298 b.len = 0; 2299 b.cap = sizeof (seed_render_buf); 2300 sb_raw (&b, sd->cfg, CFG_BYTES); 2301 if (NULL != sd->req) 2302 { 2303 size_t n = strlen (sd->req); 2304 unsigned int hv; 2305 uint8_t hdr[2]; 2306 2307 if (n > 0x3FFF) 2308 n = 0x3FFF; 2309 hv = (0u << 14) | (unsigned int) n; 2310 hdr[0] = (uint8_t) (hv & 0xFF); 2311 hdr[1] = (uint8_t) (hv >> 8); 2312 sb_raw (&b, hdr, 2); 2313 sb_raw (&b, sd->req, n); 2314 } 2315 *len = b.len; 2316 return seed_render_buf; 2317 }