|
GNU libmicrohttpd 1.0.9
|
In-process fuzzer for MHD's daemon option handling and for the daemon start-up / shutdown paths. More...
#include "fuzz_common.h"#include <microhttpd.h>#include <sys/socket.h>#include <netinet/in.h>#include <sys/select.h>#include <poll.h>#include <limits.h>
Go to the source code of this file.
Macros | |
| #define | FUZZ_HARNESS_NAME "fuzz_options" |
| #define | CFG_BYTES 16 |
| #define | MAX_SEGMENTS 48 |
| #define | MAX_CONNECTIONS 6 |
| #define | MAX_OPTS 40 |
| #define | RESP_DRAIN_BUF 4096 |
| #define | GEN_BUF_SIZE 4096 |
| #define | THREAD_WAIT_MS 2 |
| #define | PUMP_ROUNDS 3 |
| #define | PUMP_ROUNDS_LONG 10 |
| #define | MODE_COUNT (sizeof (mode_tbl) / sizeof (mode_tbl[0])) |
| #define | RAW_FLAG_MASK (~((unsigned int) MHD_USE_TLS)) |
| #define | SUSPEND_BIT |
| #define | PICK(tbl, idx) |
| #define | VARARG_CALLBACKS |
| #define | C_MODE 0 |
| #define | C_FLAGA 1 |
| #define | C_FLAGB 2 |
| #define | C_OPTA 3 |
| #define | C_OPTB 4 |
| #define | C_OPTC 5 |
| #define | C_VAL_MEM 6 |
| #define | C_VAL_CONN 7 |
| #define | C_VAL_THR 8 |
| #define | C_VAL_DAUTH 9 |
| #define | C_VAL_DISC 10 |
| #define | C_VAL_SOCK 11 |
| #define | C_DRIVE 12 |
| #define | C_HANDLER 13 |
| #define | C_OPTD 14 |
| #define | C_SPARE 15 |
| #define | REQ_PLAIN "GET /a HTTP/1.1\r\nHost: x\r\n\r\n" |
| #define | REQ_CLOSE "GET /a HTTP/1.1\r\nHost: x\r\nConnection: close\r\n\r\n" |
| #define | REQ_POST "POST /a HTTP/1.1\r\nHost: x\r\nContent-Length: 4\r\n\r\nabcd" |
| #define | REQ_CHUNKED |
Typedefs | |
| typedef void *(* | fuzz_uri_log_cb) (void *, const char *, struct MHD_Connection *) |
Functions | |
| static void | print_stats (void) |
| static unsigned int | flags_from_input (const uint8_t *data) |
| static int | uses_epoll (unsigned int flags) |
| static int | quiesce_epoll_race_allowed (void) |
| static void | prepare_sock_addrs (void) |
| static void | logger_cb (void *cls, const char *fmt, va_list ap) |
| static void * | uri_log_cb (void *cls, const char *uri, struct MHD_Connection *con) |
| static size_t | unescape_cb (void *cls, struct MHD_Connection *conn, char *s) |
| static void | completed_cb (void *cls, struct MHD_Connection *connection, void **req_cls, enum MHD_RequestTerminationCode toe) |
| static void | notify_connection_cb (void *cls, struct MHD_Connection *connection, void **socket_context, enum MHD_ConnectionNotificationCode toe) |
| static void | panic_cb (void *cls, const char *file, unsigned int line, const char *reason) |
| static void | pending_resume_add (struct MHD_Connection *c) |
| static int | pending_resume_flush (void) |
| static void | suspend_maybe (struct MHD_Connection *connection) |
| static enum MHD_Result | ahc (void *cls, struct MHD_Connection *connection, const char *url, const char *method, const char *version, const char *upload_data, size_t *upload_data_size, void **req_cls) |
| static void | drain (int sock) |
| static void | run_once (struct MHD_Daemon *d) |
| static void | pump (struct MHD_Daemon *d, int sock, unsigned int rounds) |
| static void | wait_threaded (int sock) |
| static void | send_all (struct MHD_Daemon *d, int sock, const uint8_t *data, size_t len) |
| static int | new_connection (struct MHD_Daemon *d, int *sock) |
| static void | close_connection (struct MHD_Daemon *d, int *sock) |
| static void | add_opt (struct MHD_OptionItem *opts, unsigned int *nopt, enum MHD_OPTION option, intptr_t value, void *ptr_value) |
| static unsigned int | build_options (const uint8_t *data, struct MHD_OptionItem *opts, unsigned int flags) |
| static enum MHD_Result | apc_cb (void *cls, const struct sockaddr *addr, socklen_t addrlen) |
| static struct MHD_Daemon * | start_daemon_variant (unsigned int flags, struct MHD_OptionItem *opts, unsigned int cbsel) |
| static int | connect_real (struct MHD_Daemon *d, unsigned int flags) |
| static void | query_feature (uint8_t sel) |
| static void | query_daemon_info (struct MHD_Daemon *d) |
| int | LLVMFuzzerTestOneInput (const uint8_t *data, size_t size) |
| static void | sb_raw (struct sbuf *b, const void *s, size_t n) |
| static void | sb_str (struct sbuf *b, const char *s) |
| static void | sb_u64 (struct sbuf *b, uint64_t v, int hex) |
| static void | gen_request (struct fuzz_rng *rng, struct sbuf *b, unsigned int shape) |
| static void | emit_segments (struct fuzz_rng *rng, struct sbuf *out, const uint8_t *body, size_t body_len, int new_conn_first) |
| static size_t | fuzz_generate (struct fuzz_rng *rng, uint8_t *buf, size_t cap) |
| static size_t | fuzz_seed_count (void) |
| static const uint8_t * | fuzz_seed_get (size_t idx, size_t *len) |
Variables | |
| static struct fuzz_cfg | cfg |
| static struct MHD_Connection * | pending_resume [MAX_CONNECTIONS] |
| static int | tearing_down |
| static unsigned long | stat_daemons |
| static unsigned long | stat_daemons_failed |
| static unsigned long | stat_threaded |
| static unsigned long | stat_handler_calls |
| static unsigned long | stat_conns_added |
| static unsigned long | stat_conns_refused |
| static unsigned long | stat_real_conns |
| static int | stats_registered |
| static const unsigned int | mode_tbl [] |
| static const size_t | mem_limit_tbl [] |
| static const size_t | mem_increment_tbl [] |
| static const unsigned int | conn_limit_tbl [] |
| static const unsigned int | per_ip_limit_tbl [] |
| static const unsigned int | timeout_tbl [] |
| static const unsigned int | pool_size_tbl [] = { 2, 3, 4, 2 } |
| static const size_t | stack_size_tbl [] |
| static const unsigned int | nonce_nc_tbl [] = { 0, 1, 4, 32 } |
| static const int | discipline_tbl [] = { -3, -2, -1, 0, 1, 2, 3, -4 } |
| static const int | strict_tbl [] = { -1, 0, 1, 2 } |
| static const unsigned int | backlog_tbl [] = { 0, 1, 5, 511 } |
| static const unsigned int | fastopen_tbl [] = { 0, 1, 5, 10 } |
| static const int | fd_setsize_tbl [] |
| static const char | digest_rnd [32] |
| static struct sockaddr_in | bind4 |
| static int | bind_addrs_ready |
| static const char | resp_body [] = "hello" |
| static const char *const | gen_methods [] |
| static const char *const | gen_targets [] |
| static const char *const | gen_versions [] |
| static const char *const | gen_hdr_names [] |
| static const char *const | gen_hdr_values [] |
| static const struct seed_def | seeds [] |
| static uint8_t | seed_render_buf [1024] |
In-process fuzzer for MHD's daemon option handling and for the daemon start-up / shutdown paths.
fuzz_request.c fuzzes what MHD does with the bytes on the wire, but it always starts the daemon in one single shape: external polling, no listen socket, five fixed options. Everything in daemon.c that is reached through any other configuration – the flag validation in MHD_start_daemon(), parse_options_va(), the internal polling thread, the thread pool, epoll/poll/select, the listen socket, the per-IP and per-daemon connection limits, quiesce – is therefore dead code as far as the fuzzing suite is concerned. This harness closes that gap: the input bytes pick a combination of MHD_FLAG bits and an MHD_OPTION array, the daemon is started with them, one short HTTP request is driven through it over a socketpair, and the daemon is stopped again.
The request-driving machinery is the one from fuzz_request.c: MHD_USE_NO_LISTEN_SOCKET plus MHD_add_connection() on an AF_UNIX socketpair(), with a fake 127.0.0.1 peer address so that the per-IP accounting sees something sane, and MHD_set_panic_func() installed as a tripwire. What is new here is that the daemon is a variable.
Two shapes of daemon exist, and the harness drives them differently:
Input format (see README section 2.2 for the fuzz_request one, which the segment stream below is deliberately identical to):
byte 0 daemon shape: index into mode_tbl[] (event loop and threading; the entries are the combinations MHD documents as valid, so that most iterations get a live daemon) byte 1 MHD_FLAG bits, group A (see flags_from_input()) byte 2 MHD_FLAG bits, group B; 0xC0 in the top two bits switches on the "raw" escape, which feeds bytes 1, 2 and 15 straight into the flags word. That is what fuzzes MHD_start_daemon()'s own combination checks; MHD answering NULL is a normal outcome and simply ends the iteration. byte 3 option presence mask A (sizes and limits) byte 4 option presence mask B (discipline, insanity, fd_setsize) byte 5 option presence mask C (the callbacks and the pointers) byte 6 value selector: connection memory limit / increment byte 7 value selector: connection limit / per-IP limit byte 8 value selector: connection timeout / pool size / stack byte 9 value selector: nonce-nc size / digest random / bind type byte 10 value selector: discipline / strict / insanity / bzero URI byte 11 value selector: backlog / fd_setsize / reuse / fastopen byte 12 driver behaviour: event loop variant, quiesce, introspection, suspend mode, number of connections byte 13 handler behaviour (which response, MHD_NO, per-connection option) and the accept path: whether an accept policy callback is installed, what it answers, and whether the iteration additionally connect()s to the daemon's real listening socket byte 14 option presence mask D (the digest and TLS-adjacent options, MHD_OPTION_LISTEN_SOCKET, and the deliberately invalid option number) byte 15 spare entropy; also supplies flag bits 14+ in raw mode byte 16. a sequence of send segments, each introduced by a little endian 16 bit header (op << 14) | length op 0 send the payload on the current connection op 1 send the payload, then pump extra rounds op 2 send the payload, then pump extra rounds op 3 close the current connection, open a fresh one on the same daemon, then send
The sixteen configuration bytes are mandatory; a shorter input is rejected. An input of exactly sixteen bytes is meaningful and is not a degenerate case: it starts and stops a daemon with one connection and no traffic at all, which is precisely the start-up/shutdown path this harness is about.
Two rules the harness obeys, both of them application contract rather than anything worth fuzzing:
Environment:
MHD_FUZZ_QUIESCE_EPOLL_RACE=1 let the harness call MHD_quiesce_daemon() on an epoll daemon that runs its own thread(s). Off by default because that combination reaches an open MHD defect; see quiesce_epoll_race_allowed() below.
Definition in file fuzz_options.c.
| #define C_DRIVE 12 |
Definition at line 2024 of file fuzz_options.c.
| #define C_FLAGA 1 |
Definition at line 2013 of file fuzz_options.c.
| #define C_FLAGB 2 |
Definition at line 2014 of file fuzz_options.c.
| #define C_HANDLER 13 |
Definition at line 2025 of file fuzz_options.c.
| #define C_MODE 0 |
Definition at line 2012 of file fuzz_options.c.
| #define C_OPTA 3 |
Definition at line 2015 of file fuzz_options.c.
| #define C_OPTB 4 |
Definition at line 2016 of file fuzz_options.c.
| #define C_OPTC 5 |
Definition at line 2017 of file fuzz_options.c.
| #define C_OPTD 14 |
Definition at line 2026 of file fuzz_options.c.
| #define C_SPARE 15 |
Definition at line 2027 of file fuzz_options.c.
| #define C_VAL_CONN 7 |
Definition at line 2019 of file fuzz_options.c.
| #define C_VAL_DAUTH 9 |
Definition at line 2021 of file fuzz_options.c.
| #define C_VAL_DISC 10 |
Definition at line 2022 of file fuzz_options.c.
| #define C_VAL_MEM 6 |
Definition at line 2018 of file fuzz_options.c.
| #define C_VAL_SOCK 11 |
Definition at line 2023 of file fuzz_options.c.
| #define C_VAL_THR 8 |
Definition at line 2020 of file fuzz_options.c.
| #define CFG_BYTES 16 |
Number of mandatory configuration bytes at the start of the input.
Definition at line 154 of file fuzz_options.c.
Referenced by fuzz_generate(), fuzz_seed_get(), and LLVMFuzzerTestOneInput().
| #define FUZZ_HARNESS_NAME "fuzz_options" |
Definition at line 135 of file fuzz_options.c.
Referenced by print_stats().
| #define GEN_BUF_SIZE 4096 |
Definition at line 160 of file fuzz_options.c.
Referenced by fuzz_generate().
| #define MAX_CONNECTIONS 6 |
Definition at line 157 of file fuzz_options.c.
Referenced by completed_cb(), LLVMFuzzerTestOneInput(), pending_resume_add(), and pending_resume_flush().
| #define MAX_OPTS 40 |
Definition at line 158 of file fuzz_options.c.
Referenced by add_opt(), and LLVMFuzzerTestOneInput().
| #define MAX_SEGMENTS 48 |
Definition at line 156 of file fuzz_options.c.
Referenced by LLVMFuzzerTestOneInput().
Definition at line 292 of file fuzz_options.c.
Referenced by flags_from_input(), and fuzz_generate().
| #define PICK | ( | tbl, | |
| idx ) |
Definition at line 1078 of file fuzz_options.c.
Referenced by build_options().
| #define PUMP_ROUNDS 3 |
Rounds of MHD_run() after each segment in the external shape.
Definition at line 173 of file fuzz_options.c.
Referenced by LLVMFuzzerTestOneInput().
| #define PUMP_ROUNDS_LONG 10 |
Rounds of MHD_run() after a segment sent with op 1 or 2.
Definition at line 175 of file fuzz_options.c.
Referenced by LLVMFuzzerTestOneInput().
| #define RAW_FLAG_MASK (~((unsigned int) MHD_USE_TLS)) |
Flag bits the raw escape is allowed to set. MHD_USE_TLS is masked out: without a certificate the daemon merely fails to start, so it would only waste iterations, and fuzzing GnuTLS is not what this harness is for. Everything else, including the bits above MHD_USE_NO_THREAD_SAFETY that no MHD_FLAG uses, is fair game.
Definition at line 301 of file fuzz_options.c.
Referenced by flags_from_input().
| #define REQ_CHUNKED |
Definition at line 2033 of file fuzz_options.c.
| #define REQ_CLOSE "GET /a HTTP/1.1\r\nHost: x\r\nConnection: close\r\n\r\n" |
Definition at line 2030 of file fuzz_options.c.
| #define REQ_PLAIN "GET /a HTTP/1.1\r\nHost: x\r\n\r\n" |
Definition at line 2029 of file fuzz_options.c.
| #define REQ_POST "POST /a HTTP/1.1\r\nHost: x\r\nContent-Length: 4\r\n\r\nabcd" |
Definition at line 2031 of file fuzz_options.c.
| #define RESP_DRAIN_BUF 4096 |
Definition at line 159 of file fuzz_options.c.
Referenced by drain(), and wait_threaded().
| #define SUSPEND_BIT |
The bit that MHD_suspend_connection() actually tests.
MHD_ALLOW_SUSPEND_RESUME is a compound value (8192 | MHD_USE_ITC), so "0 != (flags & MHD_ALLOW_SUSPEND_RESUME)" is already true for a daemon that merely asked for MHD_USE_ITC – and suspending such a connection is answered with MHD_PANIC ("Cannot suspend connections without enabling MHD_ALLOW_SUSPEND_RESUME"), which is the harness violating the API rather than a finding. internal.h calls the single bit MHD_TEST_ALLOW_SUSPEND_RESUME; this is the same value, expressed without reaching into a private header.
Definition at line 316 of file fuzz_options.c.
Referenced by LLVMFuzzerTestOneInput().
| #define THREAD_WAIT_MS 2 |
Milliseconds the internal-thread shape waits for the daemon to answer. The write side of the socketpair is shut down before the wait, so the daemon never sits on an incomplete request: either it answers (the common case, tens of microseconds) or it closes the connection, and both wake poll() up immediately. The timeout is therefore only paid by inputs that make MHD drop the connection without a reply.
Definition at line 170 of file fuzz_options.c.
Referenced by send_all(), and wait_threaded().
| #define VARARG_CALLBACKS |
Definition at line 1266 of file fuzz_options.c.
Referenced by start_daemon_variant().
| typedef void *(* fuzz_uri_log_cb) (void *, const char *, struct MHD_Connection *) |
Definition at line 1263 of file fuzz_options.c.
|
static |
Definition at line 1081 of file fuzz_options.c.
References MAX_OPTS, MHD_OptionItem::option, MHD_OptionItem::ptr_value, and MHD_OptionItem::value.
Referenced by build_options().

|
static |
Definition at line 738 of file fuzz_options.c.
References cfg, MHD_CONNECTION_OPTION_TIMEOUT, MHD_create_response_empty(), MHD_create_response_from_buffer(), MHD_create_response_from_buffer_static(), MHD_destroy_response(), MHD_HTTP_FORBIDDEN, MHD_HTTP_INTERNAL_SERVER_ERROR, MHD_HTTP_NO_CONTENT, MHD_HTTP_OK, MHD_NO, MHD_queue_response(), MHD_RESPMEM_MUST_COPY, MHD_RF_NONE, MHD_set_connection_option(), MHD_YES, NULL, resp_body, stat_handler_calls, and suspend_maybe().
Referenced by start_daemon_variant().


|
static |
The accept policy callback. MHD_accept_connection() calls it with the peer address of every socket it accepts, and answering MHD_NO makes new_connection_prepare_() close the socket and drop the IP-limit entry before any connection object exists – a path nothing else here reaches. (It does not reach new_connection_close_(): that one is only called from close_all_connections(), for connections queued by MHD_add_connection() and never started. See the byte 3 bit 4 scenario in fuzz_eventloop.c.)
Definition at line 1344 of file fuzz_options.c.
References cfg, MHD_NO, MHD_YES, and NULL.
Referenced by start_daemon_variant().

|
static |
Translate the presence masks (bytes 3, 4, 14) and the value selectors (bytes 6..11, 15) into an MHD_OptionItem array.
Only the options that can be expressed through MHD_OPTION_ARRAY are built here. The five callback options take two pointers, and putting a function pointer into the array's intptr_t value member is not strictly conforming C, so those go through the varargs of MHD_start_daemon() instead (see start_daemon_variant()).
Definition at line 1107 of file fuzz_options.c.
References add_opt(), backlog_tbl, bind4, cfg, conn_limit_tbl, data, digest_rnd, discipline_tbl, fastopen_tbl, fd_setsize_tbl, mem_increment_tbl, mem_limit_tbl, MHD_INVALID_SOCKET, MHD_OPTION_ALLOW_BIN_ZERO_IN_URI_PATH, MHD_OPTION_APP_FD_SETSIZE, MHD_OPTION_CLIENT_DISCIPLINE_LVL, MHD_OPTION_CONNECTION_LIMIT, MHD_OPTION_CONNECTION_MEMORY_INCREMENT, MHD_OPTION_CONNECTION_MEMORY_LIMIT, MHD_OPTION_CONNECTION_TIMEOUT, MHD_OPTION_DIGEST_AUTH_DEFAULT_MAX_NC, MHD_OPTION_DIGEST_AUTH_DEFAULT_NONCE_TIMEOUT, MHD_OPTION_DIGEST_AUTH_NONCE_BIND_TYPE, MHD_OPTION_DIGEST_AUTH_RANDOM, MHD_OPTION_DIGEST_AUTH_RANDOM_COPY, MHD_OPTION_END, MHD_OPTION_LISTEN_BACKLOG_SIZE, MHD_OPTION_LISTEN_SOCKET, MHD_OPTION_LISTENING_ADDRESS_REUSE, MHD_OPTION_NONCE_NC_SIZE, MHD_OPTION_PER_IP_CONNECTION_LIMIT, MHD_OPTION_SERVER_INSANITY, MHD_OPTION_SIGPIPE_HANDLED_BY_APP, MHD_OPTION_SOCK_ADDR, MHD_OPTION_SOCK_ADDR_LEN, MHD_OPTION_STRICT_FOR_CLIENT, MHD_OPTION_TCP_FASTOPEN_QUEUE_SIZE, MHD_OPTION_THREAD_POOL_SIZE, MHD_OPTION_THREAD_STACK_SIZE, MHD_OPTION_TLS_NO_ALPN, MHD_USE_INTERNAL_POLLING_THREAD, MHD_USE_IPv6, MHD_USE_THREAD_PER_CONNECTION, nonce_nc_tbl, NULL, MHD_OptionItem::option, per_ip_limit_tbl, PICK, pool_size_tbl, prepare_sock_addrs(), MHD_OptionItem::ptr_value, stack_size_tbl, strict_tbl, timeout_tbl, and MHD_OptionItem::value.
Referenced by LLVMFuzzerTestOneInput().


|
static |
Definition at line 1059 of file fuzz_options.c.
References cfg, pump(), and wait_threaded().
Referenced by LLVMFuzzerTestOneInput().


|
static |
Definition at line 607 of file fuzz_options.c.
References cfg, MAX_CONNECTIONS, NULL, and pending_resume.
Referenced by start_daemon_variant().

|
static |
Really connect to the daemon's listening socket.
MHD_add_connection() bypasses accept(), so without this the whole accept path – MHD_accept_connection(), the accept policy callback, the listen-socket branches of the three event loops – is unreachable. The client end is given SO_LINGER {1, 0} so that close() sends a RST and neither side ends up in TIME_WAIT: at fuzzing rates the ephemeral port range would otherwise be exhausted within a couple of minutes.
| d | the daemon, which must have a listening socket |
| flags | the flags it was started with, to pick the address family |
Definition at line 1372 of file fuzz_options.c.
References bind4, MHD_DAEMON_INFO_BIND_PORT, MHD_get_daemon_info(), MHD_USE_IPv6, NULL, MHD_DaemonInfo::port, prepare_sock_addrs(), and stat_real_conns.
Referenced by LLVMFuzzerTestOneInput().


|
static |
Read and discard whatever the daemon has produced so far. The harness has no response oracle – fuzz_request owns that job – but the data has to be taken off the socket so that MHD's writes keep succeeding.
Definition at line 834 of file fuzz_options.c.
References RESP_DRAIN_BUF.
Referenced by pump().

|
static |
Serialise body into the segment stream understood by LLVMFuzzerTestOneInput().
Definition at line 1882 of file fuzz_options.c.
References fuzz_below(), fuzz_chance(), and sb_raw().
Referenced by fuzz_generate().


|
static |
Build the flags word from bytes 0, 1, 2 and 15 of the input.
| data | the input |
Definition at line 328 of file fuzz_options.c.
References cfg, data, MHD_ALLOW_SUSPEND_RESUME, MHD_ALLOW_UPGRADE, MHD_FEATURE_UPGRADE, MHD_is_feature_supported(), MHD_USE_DUAL_STACK, MHD_USE_ERROR_LOG, MHD_USE_INSECURE_TLS_EARLY_DATA, MHD_USE_INTERNAL_POLLING_THREAD, MHD_USE_IPv6, MHD_USE_ITC, MHD_USE_NO_LISTEN_SOCKET, MHD_USE_NO_THREAD_SAFETY, MHD_USE_PEDANTIC_CHECKS, MHD_USE_POST_HANDSHAKE_AUTH_SUPPORT, MHD_USE_SUPPRESS_DATE_NO_CLOCK, MHD_USE_TCP_FASTOPEN, MHD_USE_THREAD_PER_CONNECTION, MHD_USE_TURBO, MHD_YES, MODE_COUNT, mode_tbl, and RAW_FLAG_MASK.
Referenced by LLVMFuzzerTestOneInput().


|
static |
The generator.
Purely random configuration bytes are already useful for this harness – unlike an HTTP request, an option array has no grammar to get wrong – but two things still need help. Byte 0 is biased into the range of mode_tbl[] so that most iterations get a daemon that actually starts, and the tail of the input has to look like HTTP, or MHD closes every connection before any of the configured behaviour has a chance to matter.
Definition at line 1940 of file fuzz_options.c.
References CFG_BYTES, emit_segments(), fuzz_below(), fuzz_byte(), fuzz_chance(), GEN_BUF_SIZE, gen_request(), MODE_COUNT, and sb_raw().

|
static |
Definition at line 2284 of file fuzz_options.c.
References seeds.
|
static |
Definition at line 2291 of file fuzz_options.c.
References CFG_BYTES, NULL, sb_raw(), seed_render_buf, and seeds.

|
static |
Emit one HTTP request into b.
The shapes are deliberately unambitious – the request parser is fuzz_request's subject, not this harness's. What matters here is that the bytes form something MHD will actually take through its state machine under whatever daemon configuration the configuration bytes describe, so that the option handling is exercised against a live connection rather than against a connection MHD drops on the first byte.
Definition at line 1773 of file fuzz_options.c.
References fuzz_below(), fuzz_byte(), gen_hdr_names, gen_hdr_values, gen_methods, gen_targets, gen_versions, sb_raw(), sb_str(), and sb_u64().
Referenced by fuzz_generate().


| int LLVMFuzzerTestOneInput | ( | const uint8_t * | data, |
| size_t | size ) |
The fuzz target. Signature is the libFuzzer one on purpose, so that the very same harness can be linked with libFuzzer or AFL++ later.
< see queue_unprocessed_conn()
Definition at line 1477 of file fuzz_options.c.
References build_options(), cfg, CFG_BYTES, close_connection(), connect_real(), data, flags_from_input(), fuzz_ignore_sigpipe(), MAX_CONNECTIONS, MAX_OPTS, MAX_SEGMENTS, MHD_INVALID_SOCKET, MHD_quiesce_daemon(), MHD_run(), MHD_set_panic_func(), MHD_stop_daemon(), MHD_USE_ERROR_LOG, MHD_USE_INTERNAL_POLLING_THREAD, MHD_USE_ITC, MHD_USE_NO_LISTEN_SOCKET, MHD_USE_NO_THREAD_SAFETY, MHD_USE_THREAD_PER_CONNECTION, new_connection(), NULL, panic_cb(), pending_resume, pending_resume_flush(), print_stats(), pump(), PUMP_ROUNDS, PUMP_ROUNDS_LONG, query_daemon_info(), query_feature(), quiesce_epoll_race_allowed(), send_all(), start_daemon_variant(), stat_daemons, stat_daemons_failed, stat_threaded, stats_registered, SUSPEND_BIT, tearing_down, uses_epoll(), and wait_threaded().

|
static |
MHD_OPTION_EXTERNAL_LOGGER. Swallows the message unless –verbose is in effect. This is what makes MHD_USE_ERROR_LOG affordable: with the flag set, MHD executes several hundred MHD_DLOG() call sites in daemon.c that are otherwise dead, and without a logger of our own all of that would land on stderr.
Definition at line 555 of file fuzz_options.c.
References fuzz_verbose.
Referenced by start_daemon_variant().

|
static |
Hand a fresh socketpair to the daemon.
| d | the daemon | |
| [out] | sock | set to the harness side of the pair |
Definition at line 1029 of file fuzz_options.c.
References MHD_add_connection(), MHD_YES, stat_conns_added, and stat_conns_refused.
Referenced by LLVMFuzzerTestOneInput().


|
static |
Definition at line 629 of file fuzz_options.c.
References MHD_CONNECTION_NOTIFY_STARTED, and NULL.
Referenced by start_daemon_variant().

|
static |
Definition at line 642 of file fuzz_options.c.
References fuzz_report_finding(), and NULL.
Referenced by LLVMFuzzerTestOneInput().


|
static |
Definition at line 664 of file fuzz_options.c.
References MAX_CONNECTIONS, NULL, and pending_resume.
Referenced by suspend_maybe().

|
static |
Resume everything still parked.
Definition at line 686 of file fuzz_options.c.
References MAX_CONNECTIONS, MHD_resume_connection(), NULL, and pending_resume.
Referenced by LLVMFuzzerTestOneInput(), and pump().


|
static |
Definition at line 525 of file fuzz_options.c.
References bind4, and bind_addrs_ready.
Referenced by build_options(), and connect_real().

|
static |
Definition at line 245 of file fuzz_options.c.
References FUZZ_HARNESS_NAME, fuzz_verbose, stat_conns_added, stat_conns_refused, stat_daemons, stat_daemons_failed, stat_handler_calls, stat_real_conns, and stat_threaded.
Referenced by LLVMFuzzerTestOneInput().

|
static |
Definition at line 919 of file fuzz_options.c.
References cfg, drain(), pending_resume_flush(), and run_once().
Referenced by close_connection(), LLVMFuzzerTestOneInput(), and send_all().


|
static |
Read everything MHD_get_daemon_info() offers. All of it lives in daemon.c and none of it is reachable from the other harnesses.
Definition at line 1445 of file fuzz_options.c.
References MHD_DaemonInfo::flags, MHD_DaemonInfo::listen_fd, MHD_DAEMON_INFO_BIND_PORT, MHD_DAEMON_INFO_CURRENT_CONNECTIONS, MHD_DAEMON_INFO_EPOLL_FD, MHD_DAEMON_INFO_FLAGS, MHD_DAEMON_INFO_LISTEN_FD, MHD_get_daemon_info(), MHD_get_version(), MHD_get_version_bin(), NULL, MHD_DaemonInfo::num_connections, and MHD_DaemonInfo::port.
Referenced by LLVMFuzzerTestOneInput().


|
static |
Ask MHD_is_feature_supported() about one feature. The function is a large switch in daemon.c that no other harness touches; the index comes off the input so that invalid values reach its default branch as well.
Definition at line 1431 of file fuzz_options.c.
References MHD_is_feature_supported().
Referenced by LLVMFuzzerTestOneInput().


|
static |
Is the harness allowed to call MHD_quiesce_daemon() on an epoll daemon that runs its own thread(s)?
It is not, by default, because that combination reaches an open MHD defect and would make "make check" fail at random:
MHD_quiesce_daemon() sets daemon->was_quiesced and then removes the listen FD from the epoll set itself (daemon.c:6208), tolerating ENOENT with the comment "can happen due to race with MHD_epoll()". MHD_epoll() removes the very same FD in two places. The first (daemon.c:5556) mirrors that and tolerates ENOENT. The second (daemon.c:5593), the "at the connection limit, disable listen socket" branch, also fires when daemon->was_quiesced is set – and it does not tolerate anything:
if (0 != epoll_ctl (daemon->epoll_fd, EPOLL_CTL_DEL, ls, NULL))
MHD_PANIC (_ ("Failed to remove listen FD from epoll set."));
The worker can read daemon->was_quiesced as already true and daemon->listen_socket_in_epoll as still true – neither is atomic and neither is under a lock – and then lose the race for the removal, so epoll_ctl() answers ENOENT and MHD aborts the process. Confirmed with errno == ENOENT, was_quiesced == 1, connections == 0. The same unguarded MHD_PANIC() sits in the worker-pool loop of MHD_quiesce_daemon() itself (daemon.c:6189).
Set MHD_FUZZ_QUIESCE_EPOLL_RACE=1 to reach it. Quiescing is left enabled everywhere else, so the poll()/select() side of MHD_quiesce_daemon(), including its worker-pool loop, keeps being exercised.
Definition at line 440 of file fuzz_options.c.
References NULL.
Referenced by LLVMFuzzerTestOneInput().

|
static |
Advance an externally polled daemon by one cycle, through whichever of the four event-loop entry points byte 12 selected.
The select() timeout is always zero: the harness is single threaded and everything the daemon could be waiting for has already been written into the socketpair, so blocking would only burn wall clock.
Definition at line 854 of file fuzz_options.c.
References cfg, MHD_get_fdset, MHD_get_fdset2(), MHD_get_timeout(), MHD_get_timeout64s(), MHD_get_timeout_i(), MHD_INVALID_SOCKET, MHD_run(), MHD_run_from_select, MHD_run_from_select2(), MHD_run_wait(), MHD_UNSIGNED_LONG_LONG, and MHD_YES.
Referenced by pump().


|
static |
Definition at line 1701 of file fuzz_options.c.
Referenced by emit_segments(), fuzz_generate(), fuzz_seed_get(), gen_request(), and sb_str().

|
static |
Definition at line 1713 of file fuzz_options.c.
References sb_raw().
Referenced by gen_request(), and sb_u64().


|
static |
Definition at line 1721 of file fuzz_options.c.
References sb_str().
Referenced by gen_request().


|
static |
Definition at line 974 of file fuzz_options.c.
References cfg, data, pump(), and THREAD_WAIT_MS.
Referenced by LLVMFuzzerTestOneInput().


|
static |
Start the daemon.
MHD_OPTION_EXTERNAL_LOGGER and MHD_OPTION_UNESCAPE_CALLBACK cannot be "passed as NULL": MHD calls both unconditionally, so their absence has to be expressed by leaving the option out of the varargs, which is why there are four spellings of the same call. The logger comes first on purpose – MHD_OPTION_EXTERNAL_LOGGER only catches the messages emitted after it has been parsed, and parsing the option array emits several.
Definition at line 1291 of file fuzz_options.c.
References ahc(), MHD_Daemon::apc, apc_cb(), cfg, completed_cb(), logger_cb(), MHD_OPTION_ARRAY, MHD_OPTION_END, MHD_OPTION_EXTERNAL_LOGGER, MHD_OPTION_UNESCAPE_CALLBACK, MHD_start_daemon(), notify_connection_cb(), NULL, unescape_cb(), uri_log_cb(), and VARARG_CALLBACKS.
Referenced by LLVMFuzzerTestOneInput().


|
static |
Suspend the connection if the input asks for it. Only ever called in the external-polling shape: resuming from the harness thread while an internal worker owns the connection is legal but untimed, and this harness has no way to tell whether the resume happened before or after the worker looked at the connection.
Definition at line 715 of file fuzz_options.c.
References cfg, MHD_resume_connection(), MHD_suspend_connection(), pending_resume_add(), and tearing_down.
Referenced by ahc().


|
static |
MHD_OPTION_UNESCAPE_CALLBACK. Replaces MHD's own unescaping, so it must honour the contract: unescape s in place and return the new length. Leaving the string alone is a legal implementation and keeps the harness out of the business of re-testing mhd_str.c, which fuzz_str does.
Definition at line 596 of file fuzz_options.c.
Referenced by start_daemon_variant().

|
static |
MHD_OPTION_URI_LOG_CALLBACK. The return value becomes the initial *req_cls of the request, so it has to stay NULL: the access handler uses "req_cls is still NULL" to recognise its first invocation, and a non-NULL value here would also have to be freed somewhere, which is not possible when MHD_OPTION_NOTIFY_COMPLETED is not set.
Definition at line 574 of file fuzz_options.c.
References NULL.
Referenced by start_daemon_variant().

|
static |
MHD_USE_AUTO resolves to epoll for everything except thread-per-connection, which it resolves to poll.
Definition at line 398 of file fuzz_options.c.
References MHD_USE_AUTO, MHD_USE_EPOLL, and MHD_USE_THREAD_PER_CONNECTION.
Referenced by LLVMFuzzerTestOneInput().

|
static |
Wait, briefly and with a hard bound, for a daemon with internal threads to answer. The caller has already shut the write side down, so MHD sees end-of-stream and either answers or closes; both wake poll() immediately. An input that makes MHD do neither pays the full timeout, which is why it is only a couple of milliseconds.
Definition at line 947 of file fuzz_options.c.
References RESP_DRAIN_BUF, and THREAD_WAIT_MS.
Referenced by close_connection(), and LLVMFuzzerTestOneInput().

|
static |
Definition at line 497 of file fuzz_options.c.
Referenced by build_options().
|
static |
Definition at line 517 of file fuzz_options.c.
Referenced by build_options(), connect_real(), and prepare_sock_addrs().
|
static |
Definition at line 521 of file fuzz_options.c.
Referenced by prepare_sock_addrs().
|
static |
Definition at line 214 of file fuzz_options.c.
Referenced by ahc(), apc_cb(), build_options(), close_connection(), completed_cb(), flags_from_input(), LLVMFuzzerTestOneInput(), pump(), run_once(), send_all(), start_daemon_variant(), and suspend_maybe().
|
static |
Definition at line 468 of file fuzz_options.c.
Referenced by build_options().
|
static |
Fixed entropy, so that a digest nonce is reproducible across runs.
Definition at line 510 of file fuzz_options.c.
Referenced by build_options().
|
static |
Definition at line 493 of file fuzz_options.c.
Referenced by build_options().
|
static |
Definition at line 499 of file fuzz_options.c.
Referenced by build_options().
|
static |
Definition at line 504 of file fuzz_options.c.
Referenced by build_options().
|
static |
Definition at line 1749 of file fuzz_options.c.
Referenced by gen_request().
|
static |
Definition at line 1755 of file fuzz_options.c.
Referenced by gen_request().
|
static |
Definition at line 1734 of file fuzz_options.c.
Referenced by gen_request().
|
static |
Definition at line 1739 of file fuzz_options.c.
Referenced by gen_request().
|
static |
Definition at line 1745 of file fuzz_options.c.
Referenced by gen_request().
|
static |
Definition at line 463 of file fuzz_options.c.
Referenced by build_options().
|
static |
Definition at line 458 of file fuzz_options.c.
Referenced by build_options().
|
static |
The event-loop / threading core of the flags word. Every entry is a combination MHD documents as valid, so that the ordinary path through this harness gets a live daemon and actually exercises the option handling; the deliberately invalid combinations are reached through the raw escape of byte 2 instead.
The distribution is on purpose: entries 0..17 are the external polling modes, 18..23 the ones with internal threads. An iteration with internal threads costs a thread creation, a join and a short wait for the answer, i.e. roughly an order of magnitude more than an external one, so they are a quarter of the iterations rather than a half.
Definition at line 277 of file fuzz_options.c.
Referenced by flags_from_input().
|
static |
Definition at line 491 of file fuzz_options.c.
Referenced by build_options().
|
static |
Connections suspended by suspend mode 2, which the pump loop still has to resume. Cleared by completed_cb() so that a connection MHD has finished with is never resumed afterwards. A connection that is still suspended when MHD_stop_daemon() runs makes MHD answer with MHD_PANIC ("MHD_stop_daemon() called while we have suspended connections"), which would look exactly like an MHD bug.
Definition at line 224 of file fuzz_options.c.
Referenced by completed_cb(), LLVMFuzzerTestOneInput(), pending_resume_add(), and pending_resume_flush().
|
static |
Definition at line 472 of file fuzz_options.c.
Referenced by build_options().
|
static |
Definition at line 480 of file fuzz_options.c.
Referenced by build_options().
|
static |
Definition at line 735 of file fuzz_options.c.
Referenced by ahc().
|
static |
Definition at line 2280 of file fuzz_options.c.
Referenced by fuzz_seed_get().
|
static |
Definition at line 2037 of file fuzz_options.c.
Referenced by fuzz_seed_count(), and fuzz_seed_get().
|
static |
Definition at line 484 of file fuzz_options.c.
Referenced by build_options().
|
static |
Definition at line 238 of file fuzz_options.c.
Referenced by new_connection(), and print_stats().
|
static |
Definition at line 239 of file fuzz_options.c.
Referenced by new_connection(), and print_stats().
|
static |
Statistics, printed at exit with –verbose.
Definition at line 234 of file fuzz_options.c.
Referenced by LLVMFuzzerTestOneInput(), and print_stats().
|
static |
Definition at line 235 of file fuzz_options.c.
Referenced by LLVMFuzzerTestOneInput(), and print_stats().
|
static |
Definition at line 237 of file fuzz_options.c.
Referenced by ahc(), and print_stats().
|
static |
Definition at line 240 of file fuzz_options.c.
Referenced by connect_real(), and print_stats().
|
static |
Definition at line 236 of file fuzz_options.c.
Referenced by LLVMFuzzerTestOneInput(), and print_stats().
|
static |
Definition at line 241 of file fuzz_options.c.
Referenced by LLVMFuzzerTestOneInput().
|
static |
Definition at line 495 of file fuzz_options.c.
Referenced by build_options().
|
static |
Set once the iteration only wants to drain the daemon. The handler then stops parking new connections, which is what makes the flush loop in the teardown provably terminate.
Definition at line 231 of file fuzz_options.c.
Referenced by LLVMFuzzerTestOneInput(), and suspend_maybe().
|
static |
Definition at line 476 of file fuzz_options.c.
Referenced by build_options().