|
GNU libmicrohttpd 1.0.9
|
End-to-end in-process fuzzer for the MHD request parser. More...
#include "fuzz_common.h"#include <microhttpd.h>#include <sys/socket.h>#include <netinet/in.h>#include <sys/select.h>
Go to the source code of this file.
Macros | |
| #define | _GNU_SOURCE 1 |
| #define | FUZZ_HARNESS_NAME "fuzz_request" |
| #define | MAX_SEGMENTS 96 |
| #define | MAX_CONNECTIONS 8 |
| #define | RESP_BUF_SIZE 16384 |
| #define | GEN_BUF_SIZE 8192 |
| #define | MAX_EXPECT_BODY 4096 |
| #define | RESP_KIND_COUNT 16 |
| #define | DAUTH_VARIANT_COUNT 10 |
| #define | MAX_DIGEST_BIN 64 |
| #define | DIGEST_REALM "TestRealm" |
| #define | DIGEST_USER "user" |
| #define | DIGEST_PASS "pass" |
| #define | RESP_BODY_LEN (sizeof (resp_body) - 1) |
| #define | HDR_COUNT (sizeof (hdr_names) / sizeof (hdr_names[0])) |
| #define | DAUTH_CHALLENGE 0 |
| #define | DAUTH_PASSED 1 |
| #define | DAUTH_STALE 2 |
| #define | P_END { 0, NULL } |
Enumerations | |
| enum | gen_shape { SHAPE_PLAIN = 0 , SHAPE_NOHDR_QARG , SHAPE_CL_BODY , SHAPE_CHUNKED , SHAPE_DIGEST_SIMPLE , SHAPE_DIGEST_REPLAY , SHAPE_BASIC , SHAPE_POST_FORM , SHAPE_WEIRD , SHAPE_COUNT } |
Functions | |
| static void | print_stats (void) |
| static enum MHD_Result | kv_iter (void *cls, enum MHD_ValueKind kind, const char *key, const char *value) |
| static void | pending_resume_add (struct MHD_Connection *c) |
| static void | pending_resume_drop (const struct MHD_Connection *c) |
| static int | pending_resume_flush (void) |
| static ssize_t | crc_cb (void *cls, uint64_t pos, char *buf, size_t max) |
| static void | crc_free (void *cls) |
| static void | upgrade_cb (void *cls, struct MHD_Connection *connection, void *req_cls, const char *extra_in, size_t extra_in_size, MHD_socket sock, struct MHD_UpgradeResponseHandle *urh) |
| static int | make_memfd (void) |
| static int | make_pipe_fd (void) |
| static void | decorate_response (struct MHD_Response *r) |
| static struct MHD_Response * | make_response (void) |
| static enum MHD_Result | kv_iter_n (void *cls, enum MHD_ValueKind kind, const char *key, size_t key_size, const char *value, size_t value_size) |
| static enum MHD_Result | post_iter (void *cls, enum MHD_ValueKind kind, const char *key, const char *filename, const char *content_type, const char *transfer_encoding, const char *data, uint64_t off, size_t size) |
| static void | oracle_check_body (struct hstate *hs, const char *data, size_t size) |
| static void | call_calc_helpers (void) |
| static int | run_digest_check (struct MHD_Connection *connection) |
| static enum MHD_Result | queue_auth_challenge (struct MHD_Connection *connection, int stale) |
| static void | exercise_connection_api (struct MHD_Connection *connection) |
| 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 | completed_cb (void *cls, struct MHD_Connection *connection, void **req_cls, enum MHD_RequestTerminationCode toe) |
| static void | panic_cb (void *cls, const char *file, unsigned int line, const char *reason) |
| static void | drain_and_harvest (int sock) |
| static void | run_once (struct MHD_Daemon *d) |
| static void | pump (struct MHD_Daemon *d, int sock, unsigned int rounds) |
| static size_t | expand_nonce (const uint8_t *in, size_t in_len, uint8_t *out, size_t out_cap) |
| 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) |
| int | LLVMFuzzerTestOneInput (const uint8_t *data, size_t size) |
| static void | sb_raw (struct sbuf *b, const void *v, 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, unsigned int min_digits) |
| static void | gen_query (struct fuzz_rng *rng, struct sbuf *b, int force_trailing_novalue) |
| static void | gen_headers (struct fuzz_rng *rng, struct sbuf *b, unsigned int n) |
| static void | gen_chunked_body (struct fuzz_rng *rng, struct sbuf *b, int oracle) |
| static void | gen_digest_header (struct fuzz_rng *rng, struct sbuf *b, const char *uri, int use_nonce_token, int overlong_response, int prefer_valid) |
| static int | gen_one_request (struct fuzz_rng *rng, struct sbuf *b, enum gen_shape shape, int second_of_pair) |
| 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 char | nonce_val [192] |
| static size_t | nonce_len |
| static uint8_t | expect_body [MAX_EXPECT_BODY] |
| static size_t | expect_body_len |
| static int | oracle_on |
| static unsigned long | stat_daemons |
| static unsigned long | stat_handler_calls |
| static unsigned long | stat_final_calls |
| static unsigned long | stat_body_bytes |
| static unsigned long | stat_challenges |
| static unsigned long | stat_auth_ok |
| static int | stats_registered |
| static char | resp_buf [RESP_BUF_SIZE] |
| static size_t | resp_len |
| static const size_t | mem_limit_tbl [] |
| static const int | discipline_tbl [] = { -3, -2, -1, 0, 1, 2 } |
| static int | min_discipline = -3 |
| static int | min_discipline_read |
| static int | forced_shape = -1 |
| static int | forced_shape_read |
| static size_t | min_mem_limit |
| static int | min_mem_limit_read |
| static enum MHD_DigestAuthMultiAlgo3 | algo_tbl [] |
| static const unsigned int | nnc_tbl [] = { 4, 1, 8, 64 } |
| static const char | digest_rnd [32] |
| static struct MHD_Daemon * | cur_daemon |
| static struct MHD_Connection * | pending_resume [MAX_CONNECTIONS] |
| static int | tearing_down |
| static const char | resp_body [] = "hello world, this is the response body" |
| static const char *const | hdr_names [] |
| static const char *const | hdr_values [] |
| static enum MHD_DigestAuthAlgorithm | algo1_tbl [] |
| static enum MHD_DigestAuthAlgo3 | algo3_tbl [] |
| static const char *const | gen_methods [] |
| static const char *const | gen_versions [] |
| static const char *const | gen_targets [] |
| static const char *const | gen_hdr_names [] |
| static const char *const | gen_hdr_values [] |
| static const char *const | gen_algos [] |
| static const char *const | gen_ctypes [] |
| static const struct seed_def | seeds [] |
| static uint8_t | seed_render_buf [4096] |
End-to-end in-process fuzzer for the MHD request parser.
A real struct MHD_Daemon is created with MHD_USE_NO_LISTEN_SOCKET and driven through a socketpair() that is handed to MHD with MHD_add_connection(). The daemon runs in external-polling mode, so everything happens in the fuzzer's thread: fully deterministic, no TCP stack, no ports, no races.
Input format (see README):
byte 0 configuration: connection memory limit byte 1 configuration: handler behaviour bitmask byte 2 configuration: client discipline / insanity level byte 3 configuration: digest-auth parameters byte 4 response construction: which MHD_create_response_*() to use, how many response headers to add, footers, header-manipulation API byte 5 which authentication entry point to call: the v1/v2 compatibility wrappers, the username/request-info queries, or the differing fail-response variants byte 6 event-loop mode (MHD_run() vs MHD_get_fdset*() + MHD_run_from_select*()) and the connection-introspection calls byte 7 suspend/resume and HTTP "Upgrade" byte 8 seed for the generated response header names/values byte 9 seed for the content-reader callback behaviour byte 10. a sequence of send-segments, each introduced by a little-endian 16 bit header (op << 14) | length op 0 send the payload op 1 the payload is the expected decoded request body (ground truth for the body oracle); nothing is sent op 2 send the payload and pump the daemon extra rounds op 3 close the connection, open a fresh one, send
All ten configuration bytes are always present; an input shorter than that is rejected outright. The all-zero configuration is the plainest one – a static two byte buffer response, MHD_run() as the event loop, no suspend, no upgrade, no extra introspection – so zeroing bytes 4-9 of any input reduces it to what the harness did before those bytes existed.
Splitting the byte stream into explicit segments matters: MHD's parser is incremental and several past bugs only showed up for particular split points.
The literal ASCII token "%%NONCE%%" inside a segment is replaced, at send time, by the most recent nonce="..." value seen in a response from the daemon. This is what allows the fuzzer to walk through the Digest-Auth challenge/response handshake and reach the code that is only executed for a valid nonce.
Definition in file fuzz_request.c.
| #define _GNU_SOURCE 1 |
Definition at line 83 of file fuzz_request.c.
| #define DAUTH_CHALLENGE 0 |
Outcome of run_digest_check().
Definition at line 905 of file fuzz_request.c.
Referenced by run_digest_check().
| #define DAUTH_PASSED 1 |
Definition at line 906 of file fuzz_request.c.
Referenced by ahc(), and run_digest_check().
| #define DAUTH_STALE 2 |
Definition at line 907 of file fuzz_request.c.
Referenced by ahc(), and run_digest_check().
| #define DAUTH_VARIANT_COUNT 10 |
Number of authentication entry points selectable by byte 5.
Definition at line 123 of file fuzz_request.c.
Referenced by LLVMFuzzerTestOneInput().
| #define DIGEST_PASS "pass" |
Definition at line 129 of file fuzz_request.c.
Referenced by call_calc_helpers(), and run_digest_check().
| #define DIGEST_REALM "TestRealm" |
Definition at line 127 of file fuzz_request.c.
Referenced by call_calc_helpers(), gen_digest_header(), queue_auth_challenge(), and run_digest_check().
| #define DIGEST_USER "user" |
Definition at line 128 of file fuzz_request.c.
Referenced by call_calc_helpers(), gen_digest_header(), and run_digest_check().
| #define FUZZ_HARNESS_NAME "fuzz_request" |
Definition at line 86 of file fuzz_request.c.
Referenced by print_stats().
| #define GEN_BUF_SIZE 8192 |
Definition at line 117 of file fuzz_request.c.
Referenced by fuzz_generate().
Definition at line 403 of file fuzz_request.c.
Referenced by decorate_response().
| #define MAX_CONNECTIONS 8 |
Definition at line 115 of file fuzz_request.c.
Referenced by LLVMFuzzerTestOneInput(), pending_resume_add(), pending_resume_drop(), and pending_resume_flush().
| #define MAX_DIGEST_BIN 64 |
Largest digest MHD_digest_auth_calc_userdigest() can produce.
Definition at line 125 of file fuzz_request.c.
Referenced by call_calc_helpers(), and run_digest_check().
| #define MAX_EXPECT_BODY 4096 |
Definition at line 118 of file fuzz_request.c.
Referenced by fuzz_generate(), gen_chunked_body(), gen_one_request(), and LLVMFuzzerTestOneInput().
| #define MAX_SEGMENTS 96 |
Definition at line 114 of file fuzz_request.c.
Referenced by LLVMFuzzerTestOneInput().
| #define P_END { 0, NULL } |
Definition at line 2756 of file fuzz_request.c.
| #define RESP_BODY_LEN (sizeof (resp_body) - 1) |
Definition at line 367 of file fuzz_request.c.
Referenced by make_memfd(), make_pipe_fd(), and make_response().
| #define RESP_BUF_SIZE 16384 |
Definition at line 116 of file fuzz_request.c.
Referenced by drain_and_harvest().
| #define RESP_KIND_COUNT 16 |
Number of entries in the response-construction table.
Definition at line 121 of file fuzz_request.c.
| enum gen_shape |
| Enumerator | |
|---|---|
| SHAPE_PLAIN | |
| SHAPE_NOHDR_QARG | |
| SHAPE_CL_BODY | |
| SHAPE_CHUNKED | |
| SHAPE_DIGEST_SIMPLE | |
| SHAPE_DIGEST_REPLAY | |
| SHAPE_BASIC | |
| SHAPE_POST_FORM | |
| SHAPE_WEIRD | |
| SHAPE_COUNT | |
Definition at line 2438 of file fuzz_request.c.
|
static |
Definition at line 1360 of file fuzz_request.c.
References call_calc_helpers(), cfg, DAUTH_PASSED, DAUTH_STALE, decorate_response(), exercise_connection_api(), expect_body_len, fuzz_report_finding(), kv_iter(), kv_iter_n(), make_response(), MHD_add_response_header(), MHD_basic_auth_get_username_password(), MHD_basic_auth_get_username_password3(), MHD_COOKIE_KIND, MHD_create_post_processor(), MHD_create_response_for_upgrade(), MHD_destroy_response(), MHD_FOOTER_KIND, MHD_free(), MHD_GET_ARGUMENT_KIND, MHD_get_connection_values(), MHD_get_connection_values_n(), MHD_HEADER_KIND, MHD_HTTP_FORBIDDEN, MHD_HTTP_HEADER_CONNECTION, MHD_HTTP_HEADER_UPGRADE, MHD_HTTP_OK, MHD_HTTP_SWITCHING_PROTOCOLS, MHD_HTTP_VERSION_1_1, MHD_lookup_connection_value(), MHD_NO, MHD_post_process(), MHD_queue_response(), MHD_YES, NULL, oracle_check_body(), oracle_on, MHD_BasicAuthInfo::password, MHD_BasicAuthInfo::password_len, post_iter(), queue_auth_challenge(), run_digest_check(), stat_auth_ok, stat_body_bytes, stat_challenges, stat_final_calls, stat_handler_calls, suspend_maybe(), upgrade_cb(), MHD_BasicAuthInfo::username, and MHD_BasicAuthInfo::username_len.
Referenced by LLVMFuzzerTestOneInput().

|
static |
Exercise the digest helpers that take no connection: MHD_digest_get_hash_size(), MHD_digest_auth_calc_userdigest(), MHD_digest_auth_calc_userhash() and MHD_digest_auth_calc_userhash_hex().
The output buffers are heap allocations of exactly the size that is passed to MHD, and that size is deliberately varied down to zero. A correct implementation must reject every buffer that is too small; if it instead writes the full digest, ASAN's redzone catches it immediately. This is the shape of the bug fixed in commit 5a73c1ae, where an over-long userhash overflowed a fixed-size decode buffer.
Definition at line 924 of file fuzz_request.c.
References algo3_tbl, cfg, DIGEST_PASS, DIGEST_REALM, DIGEST_USER, MAX_DIGEST_BIN, MHD_digest_auth_calc_userdigest(), MHD_digest_auth_calc_userhash(), MHD_digest_auth_calc_userhash_hex(), MHD_digest_get_hash_size(), and NULL.
Referenced by ahc().


|
static |
Definition at line 1869 of file fuzz_request.c.
References pump().
Referenced by LLVMFuzzerTestOneInput().


|
static |
Definition at line 1587 of file fuzz_request.c.
References MHD_destroy_post_processor(), NULL, and pending_resume_drop().
Referenced by LLVMFuzzerTestOneInput().


|
static |
Definition at line 421 of file fuzz_request.c.
References MHD_CONTENT_READER_END_OF_STREAM, and MHD_CONTENT_READER_END_WITH_ERROR.
Referenced by make_response().

|
static |
Definition at line 452 of file fuzz_request.c.
Referenced by make_response().

|
static |
Add response headers, footers and option flags as selected by byte 4 and the header seed in byte 8.
Footers are only meaningful for a chunked response – MHD emits them as trailers after the last chunk – which is why they are worth fuzzing together with the callback-based response kinds.
Definition at line 555 of file fuzz_request.c.
References cfg, HDR_COUNT, hdr_names, hdr_values, kv_iter(), MHD_add_response_footer(), MHD_add_response_header(), MHD_del_response_header(), MHD_get_response_header(), MHD_get_response_headers(), MHD_RF_HEAD_ONLY_RESPONSE, MHD_RF_HTTP_VERSION_1_0_RESPONSE, MHD_RF_INSANITY_HEADER_CONTENT_LENGTH, MHD_RF_SEND_KEEP_ALIVE_HEADER, MHD_RO_END, MHD_set_response_options(), and NULL.
Referenced by ahc(), and make_response().


|
static |
Read whatever the daemon has produced so far and look for a fresh Digest-Auth nonce in it.
Definition at line 1635 of file fuzz_request.c.
References nonce_len, nonce_val, NULL, resp_buf, RESP_BUF_SIZE, and resp_len.
Referenced by pump().

|
static |
Serialise body into the segment format understood by LLVMFuzzerTestOneInput(), starting at out[*out_len].
Definition at line 2588 of file fuzz_request.c.
References fuzz_below(), fuzz_chance(), and sb_raw().
Referenced by fuzz_generate().


|
static |
Call the read-only connection accessors on attacker-supplied header data. MHD_lookup_connection_value*() run the case-insensitive header matcher over whatever the client sent, which is exactly the sort of input the fuzzer is good at producing.
Definition at line 1247 of file fuzz_request.c.
References cfg, cur_daemon, MHD_CONNECTION_INFO_CLIENT_ADDRESS, MHD_CONNECTION_INFO_CONNECTION_FD, MHD_CONNECTION_INFO_CONNECTION_SUSPENDED, MHD_CONNECTION_INFO_CONNECTION_TIMEOUT, MHD_CONNECTION_INFO_DAEMON, MHD_CONNECTION_INFO_HTTP_STATUS, MHD_CONNECTION_INFO_REQUEST_HEADER_SIZE, MHD_CONNECTION_INFO_SOCKET_CONTEXT, MHD_CONNECTION_OPTION_TIMEOUT, MHD_DAEMON_INFO_BIND_PORT, MHD_DAEMON_INFO_CURRENT_CONNECTIONS, MHD_DAEMON_INFO_FLAGS, MHD_DAEMON_INFO_LISTEN_FD, MHD_get_connection_info(), MHD_get_connection_URI_path_n(), MHD_get_daemon_info(), MHD_get_version(), MHD_get_version_bin(), MHD_HEADER_KIND, MHD_lookup_connection_value(), MHD_lookup_connection_value_n(), MHD_set_connection_option(), MHD_set_connection_value(), MHD_YES, and NULL.
Referenced by ahc().


|
static |
Copy in to out, expanding every occurrence of the ASCII token "%%NONCE%%" to the harvested nonce.
Definition at line 1780 of file fuzz_request.c.
References nonce_len, and nonce_val.
Referenced by LLVMFuzzerTestOneInput().

|
static |
Definition at line 2638 of file fuzz_request.c.
References emit_segments(), expect_body, expect_body_len, forced_shape, forced_shape_read, fuzz_below(), fuzz_byte(), fuzz_chance(), GEN_BUF_SIZE, gen_one_request(), MAX_EXPECT_BODY, NULL, sb_raw(), SHAPE_CHUNKED, SHAPE_CL_BODY, SHAPE_COUNT, SHAPE_DIGEST_REPLAY, SHAPE_DIGEST_SIMPLE, SHAPE_NOHDR_QARG, and SHAPE_POST_FORM.

|
static |
Definition at line 3023 of file fuzz_request.c.
References seeds.
|
static |
Definition at line 3030 of file fuzz_request.c.
References NULL, sb_raw(), seed_render_buf, and seeds.

|
static |
Emit a chunked body. When oracle is non-zero the body is strictly RFC 9112 conformant and the decoded payload is recorded in expect_body, so that the harness can verify what MHD hands to the application. Chunk extensions are emitted frequently on purpose.
Definition at line 2314 of file fuzz_request.c.
References expect_body, expect_body_len, fuzz_below(), fuzz_chance(), MAX_EXPECT_BODY, sb_raw(), sb_str(), and sb_u64().
Referenced by gen_one_request().


|
static |
Definition at line 2374 of file fuzz_request.c.
References DIGEST_REALM, DIGEST_USER, fuzz_below(), fuzz_chance(), gen_algos, sb_raw(), sb_str(), and sb_u64().
Referenced by gen_one_request().


|
static |
Definition at line 2278 of file fuzz_request.c.
References fuzz_below(), fuzz_chance(), gen_hdr_names, gen_hdr_values, and sb_str().
Referenced by gen_one_request().


|
static |
Build one HTTP request into b.
Definition at line 2460 of file fuzz_request.c.
References expect_body, expect_body_len, fuzz_below(), fuzz_chance(), gen_chunked_body(), gen_ctypes, gen_digest_header(), gen_headers(), gen_methods, gen_query(), gen_targets, gen_versions, MAX_EXPECT_BODY, sb_raw(), sb_str(), sb_u64(), SHAPE_BASIC, SHAPE_CHUNKED, SHAPE_CL_BODY, SHAPE_COUNT, SHAPE_DIGEST_REPLAY, SHAPE_DIGEST_SIMPLE, SHAPE_NOHDR_QARG, SHAPE_PLAIN, SHAPE_POST_FORM, and SHAPE_WEIRD.
Referenced by fuzz_generate().


|
static |
Append a random query string, optionally ending in an argument without '=' (which is the shape needed for the read-buffer shift-back bug).
Definition at line 2246 of file fuzz_request.c.
References fuzz_below(), fuzz_chance(), sb_str(), and sb_u64().
Referenced by gen_one_request().


|
static |
Definition at line 780 of file fuzz_request.c.
References MHD_PostProcessor::cls, MHD_YES, and NULL.
Referenced by ahc(), and decorate_response().

|
static |
Length-aware counterpart of kv_iter(), for MHD_get_connection_values_n(). Unlike the NUL-terminated iterator it is handed explicit sizes, so it reads exactly what MHD says is there rather than trusting a terminator.
Definition at line 805 of file fuzz_request.c.
References MHD_PostProcessor::cls, MHD_YES, and NULL.
Referenced by ahc().

| 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 1886 of file fuzz_request.c.
References ahc(), algo_tbl, cfg, close_connection(), completed_cb(), cur_daemon, data, DAUTH_VARIANT_COUNT, digest_rnd, discipline_tbl, expand_nonce(), expect_body, expect_body_len, fuzz_ignore_sigpipe(), FUZZ_MAX_INPUT, fuzz_pristine, fuzz_verbose, MAX_CONNECTIONS, MAX_EXPECT_BODY, MAX_SEGMENTS, mem_limit_tbl, MHD_ALLOW_SUSPEND_RESUME, MHD_ALLOW_UPGRADE, MHD_DIGEST_AUTH_MULT_QOP_ANY_NON_INT, MHD_DIGEST_AUTH_MULT_QOP_AUTH, MHD_DSC_SANE, MHD_FEATURE_UPGRADE, MHD_is_feature_supported(), MHD_OPTION_ARRAY, MHD_OPTION_CLIENT_DISCIPLINE_LVL, MHD_OPTION_CONNECTION_MEMORY_LIMIT, MHD_OPTION_DIGEST_AUTH_RANDOM, MHD_OPTION_END, MHD_OPTION_NONCE_NC_SIZE, MHD_OPTION_NOTIFY_COMPLETED, MHD_OPTION_SERVER_INSANITY, MHD_quiesce_daemon(), MHD_run(), MHD_set_panic_func(), MHD_start_daemon(), MHD_stop_daemon(), MHD_USE_ERROR_LOG, MHD_USE_NO_LISTEN_SOCKET, MHD_YES, min_discipline, min_discipline_read, min_mem_limit, min_mem_limit_read, new_connection(), nnc_tbl, nonce_len, nonce_val, NULL, MHD_OptionItem::option, oracle_on, panic_cb(), pending_resume, pending_resume_flush(), print_stats(), MHD_OptionItem::ptr_value, pump(), resp_buf, resp_len, send_all(), stat_daemons, stats_registered, tearing_down, and MHD_OptionItem::value.

|
static |
A file descriptor holding RESP_BODY_LEN bytes, backed by anonymous memory so that no filesystem is involved.
Definition at line 504 of file fuzz_request.c.
References fd, resp_body, and RESP_BODY_LEN.
Referenced by make_response().

|
static |
The read end of a pipe holding RESP_BODY_LEN bytes. The body is far below the pipe capacity, so the write cannot block.
Definition at line 529 of file fuzz_request.c.
References resp_body, and RESP_BODY_LEN.
Referenced by make_response().

|
static |
Build the response body for this iteration.
Every constructor that fails is responsible for nothing: MHD releases neither the buffer nor the file descriptor when it returns NULL (see MHD_create_response_from_buffer_with_free_callback_cls() and MHD_create_response_from_pipe() in response.c), so ownership stays here and the resource has to be released explicitly. Getting this backwards would produce double frees that look exactly like MHD bugs.
Definition at line 610 of file fuzz_request.c.
References cfg, crc_cb(), crc_free(), decorate_response(), fd, MHD_IoVec::iov_base, MHD_IoVec::iov_len, make_memfd(), make_pipe_fd(), MHD_create_response_empty(), MHD_create_response_from_buffer(), MHD_create_response_from_buffer_copy(), MHD_create_response_from_buffer_static(), MHD_create_response_from_buffer_with_free_callback(), MHD_create_response_from_callback(), MHD_create_response_from_data(), MHD_create_response_from_fd(), MHD_create_response_from_fd64(), MHD_create_response_from_fd_at_offset, MHD_create_response_from_iovec(), MHD_create_response_from_pipe(), MHD_RESPMEM_MUST_COPY, MHD_RESPMEM_MUST_FREE, MHD_RESPMEM_PERSISTENT, MHD_RF_NONE, MHD_SIZE_UNKNOWN, NULL, resp_body, and RESP_BODY_LEN.
Referenced by ahc().


|
static |
Definition at line 1842 of file fuzz_request.c.
References MHD_add_connection(), and MHD_YES.
Referenced by LLVMFuzzerTestOneInput().


|
static |
Check the body MHD hands to the application against the body that the generator encoded into the request. MHD is allowed to reject the request at any point, but every byte that it does deliver must be the next expected byte. A mismatch means the framing layer (Content-Length or chunked transfer coding) got out of sync, which is exactly the class of bug that enables request smuggling.
Definition at line 865 of file fuzz_request.c.
References data, expect_body, expect_body_len, fuzz_report_finding(), and oracle_on.
Referenced by ahc().


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


|
static |
Record c as suspended. Silently ignores the overflow case: the array has one slot per connection the harness can open, so it cannot overflow, and a dropped entry would only cost the resume below.
Definition at line 310 of file fuzz_request.c.
References MAX_CONNECTIONS, NULL, and pending_resume.
Referenced by suspend_maybe().

|
static |
Forget c without resuming it (it is gone).
Definition at line 327 of file fuzz_request.c.
References MAX_CONNECTIONS, NULL, and pending_resume.
Referenced by completed_cb().

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


|
static |
Definition at line 826 of file fuzz_request.c.
References MHD_PostProcessor::cls, MHD_PostProcessor::content_type, data, MHD_YES, and NULL.
Referenced by ahc().

|
static |
Definition at line 195 of file fuzz_request.c.
References FUZZ_HARNESS_NAME, fuzz_verbose, stat_auth_ok, stat_body_bytes, stat_challenges, stat_daemons, stat_final_calls, and stat_handler_calls.
Referenced by LLVMFuzzerTestOneInput().

|
static |
Definition at line 1754 of file fuzz_request.c.
References drain_and_harvest(), pending_resume_flush(), and run_once().
Referenced by close_connection(), LLVMFuzzerTestOneInput(), and send_all().


|
static |
Send the "authentication required" reply through the variant of the API selected by byte 5. The v1/v2 forms take a plain response and an algorithm rather than the qop/algo pair of the v3 form.
Definition at line 1178 of file fuzz_request.c.
References algo1_tbl, cfg, DIGEST_REALM, MHD_create_response_from_buffer_static(), MHD_destroy_response(), MHD_NO, MHD_queue_auth_fail_response(), MHD_queue_auth_fail_response2(), MHD_queue_auth_required_response3(), MHD_queue_basic_auth_fail_response(), MHD_queue_basic_auth_required_response3(), MHD_YES, and NULL.
Referenced by ahc().


|
static |
Run the digest-authentication entry point selected by byte 5.
Variant 0 is MHD_digest_auth_check3(), which is the plainest one and what the all-zero configuration selects. Variants 1-5 are the v1/v2 compatibility wrappers and the pre-computed-digest forms, which do their own buffer sizing and are therefore worth driving directly. Variants 6-8 are the query functions, which parse the client's Authorization header but return no verdict, so they run in addition to check3().
Definition at line 985 of file fuzz_request.c.
References algo1_tbl, cfg, MHD_DigestAuthInfo::cnonce_len, MHD_PostProcessor::connection, DAUTH_CHALLENGE, DAUTH_PASSED, DAUTH_STALE, DIGEST_PASS, DIGEST_REALM, DIGEST_USER, MAX_DIGEST_BIN, MHD_DAUTH_NONCE_STALE, MHD_DAUTH_OK, MHD_DIGEST_ALG_MD5, MHD_DIGEST_ALG_SHA256, MHD_DIGEST_AUTH_ALGO3_MD5, MHD_DIGEST_AUTH_ALGO3_MD5_SESSION, MHD_DIGEST_AUTH_ALGO3_SHA256, MHD_DIGEST_AUTH_ALGO3_SHA256_SESSION, MHD_DIGEST_AUTH_ALGO3_SHA512_256, MHD_DIGEST_AUTH_ALGO3_SHA512_256_SESSION, MHD_digest_auth_calc_userdigest(), MHD_digest_auth_check(), MHD_digest_auth_check2(), MHD_digest_auth_check3(), MHD_digest_auth_check_digest(), MHD_digest_auth_check_digest2(), MHD_digest_auth_check_digest3(), MHD_digest_auth_get_request_info3(), MHD_digest_auth_get_username(), MHD_digest_auth_get_username3(), MHD_DIGEST_AUTH_MULT_ALGO3_ANY_NON_SESSION, MHD_digest_get_hash_size(), MHD_free(), MHD_INVALID_NONCE, MHD_MD5_DIGEST_SIZE, MHD_YES, MHD_DigestAuthInfo::nc, NULL, MHD_DigestAuthInfo::opaque, MHD_DigestAuthInfo::opaque_len, MHD_DigestAuthInfo::realm, MHD_DigestAuthInfo::realm_len, MHD_DigestAuthInfo::userhash_hex, MHD_DigestAuthUsernameInfo::userhash_hex, MHD_DigestAuthInfo::userhash_hex_len, MHD_DigestAuthUsernameInfo::userhash_hex_len, MHD_DigestAuthInfo::username, MHD_DigestAuthUsernameInfo::username, MHD_DigestAuthInfo::username_len, and MHD_DigestAuthUsernameInfo::username_len.
Referenced by ahc().


|
static |
Advance the daemon by one cycle, through the event-loop API selected by byte 6 of the input.
Mode 0 is MHD_run(), which is what the harness used before the extension and which does the descriptor bookkeeping internally. Modes 1-3 drive the daemon the way an application with its own event loop does: MHD_get_fdset*() to collect the descriptors, select(), then MHD_run_from_select*(). That is the most common production integration and none of it was reachable before.
The select() timeout is always zero. The harness is single threaded and whatever the daemon is waiting for has already been written into the socketpair, so blocking would only burn wall clock; select() is called purely to populate the ready sets.
Definition at line 1692 of file fuzz_request.c.
References cfg, MHD_get_fdset, MHD_get_fdset2(), MHD_get_timeout(), MHD_get_timeout64(), MHD_get_timeout64s(), MHD_get_timeout_i(), MHD_INVALID_SOCKET, MHD_run(), MHD_run_from_select, MHD_run_from_select2(), MHD_UNSIGNED_LONG_LONG, and MHD_YES.
Referenced by pump().


|
static |
Definition at line 2150 of file fuzz_request.c.
Referenced by emit_segments(), fuzz_generate(), fuzz_seed_get(), gen_chunked_body(), gen_digest_header(), gen_one_request(), sb_str(), and sb_u64().

|
static |
Definition at line 2162 of file fuzz_request.c.
References sb_raw().
Referenced by gen_chunked_body(), gen_digest_header(), gen_headers(), gen_one_request(), and gen_query().


|
static |
Definition at line 2170 of file fuzz_request.c.
References sb_raw().
Referenced by gen_chunked_body(), gen_digest_header(), gen_one_request(), and gen_query().


|
static |
Definition at line 1811 of file fuzz_request.c.
Referenced by LLVMFuzzerTestOneInput().


|
static |
Suspend the connection if byte 7 asks for it.
Mode 1 suspends and resumes straight away, which exercises both state transitions without leaving the connection parked. Mode 2 leaves it suspended and records it in pending_resume so that the pump loop resumes it on its next round; completed_cb() drops the record, so a connection MHD has finished with is never resumed after the fact, and the iteration flushes the whole set before stopping the daemon. A connection left suspended makes MHD_stop_daemon() MHD_PANIC().
Definition at line 1344 of file fuzz_request.c.
References cfg, MHD_resume_connection(), MHD_suspend_connection(), pending_resume_add(), and tearing_down.
Referenced by ahc().


|
static |
Handler for an upgraded ("101 Switching Protocols") connection.
extra_in holds whatever the client had already pipelined behind the request, which is attacker-controlled and therefore read here.
The connection MUST be closed through MHD_upgrade_action(): the socket belongs to the application from this point on and MHD_stop_daemon() cannot complete while an upgraded connection is still outstanding.
Definition at line 470 of file fuzz_request.c.
References cfg, MHD_upgrade_action(), MHD_UPGRADE_ACTION_CLOSE, MHD_UPGRADE_ACTION_CORK_OFF, MHD_UPGRADE_ACTION_CORK_ON, and NULL.
Referenced by ahc().


|
static |
Definition at line 886 of file fuzz_request.c.
Referenced by queue_auth_challenge(), and run_digest_check().
|
static |
Definition at line 893 of file fuzz_request.c.
Referenced by call_calc_helpers().
|
static |
Definition at line 249 of file fuzz_request.c.
Referenced by LLVMFuzzerTestOneInput().
|
static |
Definition at line 173 of file fuzz_request.c.
Referenced by ahc(), call_calc_helpers(), decorate_response(), exercise_connection_api(), LLVMFuzzerTestOneInput(), make_response(), queue_auth_challenge(), run_digest_check(), run_once(), suspend_maybe(), and upgrade_cb().
|
static |
Daemon of the current iteration; needed by MHD_get_daemon_info().
Definition at line 277 of file fuzz_request.c.
Referenced by exercise_connection_api(), and LLVMFuzzerTestOneInput().
|
static |
Fixed entropy so that nonces are reproducible across runs.
Definition at line 259 of file fuzz_request.c.
Referenced by LLVMFuzzerTestOneInput().
|
static |
Definition at line 217 of file fuzz_request.c.
Referenced by LLVMFuzzerTestOneInput().
|
static |
Ground-truth body of a generated (pristine) request, see README.
Definition at line 180 of file fuzz_request.c.
Referenced by fuzz_generate(), gen_chunked_body(), gen_one_request(), LLVMFuzzerTestOneInput(), and oracle_check_body().
|
static |
Definition at line 181 of file fuzz_request.c.
Referenced by ahc(), fuzz_generate(), gen_chunked_body(), gen_one_request(), LLVMFuzzerTestOneInput(), and oracle_check_body().
|
static |
Restrict the generator to a single grammar shape (see enum gen_shape). -1 (the default) means "pick a random shape every time". Set with MHD_FUZZ_SHAPE=<n>; handy for triage and for regression testing a specific past bug.
Definition at line 235 of file fuzz_request.c.
Referenced by fuzz_generate().
|
static |
Definition at line 236 of file fuzz_request.c.
Referenced by fuzz_generate().
|
static |
Deliberately includes tokens MHD does not know: bug #1 lives here.
Definition at line 2225 of file fuzz_request.c.
Referenced by gen_digest_header().
|
static |
Definition at line 2231 of file fuzz_request.c.
Referenced by gen_one_request().
|
static |
Definition at line 2212 of file fuzz_request.c.
Referenced by gen_headers().
|
static |
Definition at line 2218 of file fuzz_request.c.
Referenced by gen_headers().
|
static |
Definition at line 2195 of file fuzz_request.c.
Referenced by gen_one_request().
|
static |
Definition at line 2205 of file fuzz_request.c.
Referenced by gen_one_request().
|
static |
Definition at line 2200 of file fuzz_request.c.
Referenced by gen_one_request().
|
static |
Response header names and values, indexed by cfg.hdr_seed. The list mixes ordinary headers with ones MHD has to reject or handle specially: an empty name, a name carrying a control character, and a value containing CRLF – the classic response-splitting vector, which MHD_add_response_header() is supposed to refuse. Exercising that validator is the point of this table.
The message-framing headers are deliberately NOT here. MHD generates Content-Length and Transfer-Encoding itself from the response object, so an application that also sets them by hand is lying to the library about its own body: a manual "Transfer-Encoding: chunked" on, say, an iovec response sends MHD down the chunked path for a response that has neither a content reader nor a flat buffer, and it aborts in try_ready_chunked_body(). That is the harness misusing the API rather than a defect worth reporting, and leaving it in would bury every real finding under the same false report. MHD_RF_INSANITY_- HEADER_CONTENT_LENGTH, set through MHD_set_response_options() in decorate_response(), is the sanctioned way to explore that corner.
Definition at line 389 of file fuzz_request.c.
Referenced by decorate_response().
|
static |
Definition at line 397 of file fuzz_request.c.
Referenced by decorate_response().
|
static |
Definition at line 212 of file fuzz_request.c.
Referenced by LLVMFuzzerTestOneInput().
|
static |
Lower bound for MHD_OPTION_CLIENT_DISCIPLINE_LVL, -3 is the full range. On a tree without the patches listed in README section 6, levels below 0 reach two stale mhd_assert()s in get_req_header(); set MHD_FUZZ_MIN_DISCIPLINE=0 there so that the rest of the state space keeps being explored.
Definition at line 226 of file fuzz_request.c.
Referenced by LLVMFuzzerTestOneInput().
|
static |
Definition at line 227 of file fuzz_request.c.
Referenced by LLVMFuzzerTestOneInput().
|
static |
Lower bound for MHD_OPTION_CONNECTION_MEMORY_LIMIT. 0 (the default) fuzzes the full range, including the 128 byte pools that are needed to reach the read-buffer "shift back" code. On a tree without the patches listed in README section 6, pools below ~500 bytes combined with a chunked request body reach two mhd_assert()s in connection.c; set MHD_FUZZ_MIN_MEM_LIMIT=512 there.
Definition at line 246 of file fuzz_request.c.
Referenced by LLVMFuzzerTestOneInput().
|
static |
Definition at line 247 of file fuzz_request.c.
Referenced by LLVMFuzzerTestOneInput().
|
static |
Definition at line 256 of file fuzz_request.c.
Referenced by LLVMFuzzerTestOneInput().
|
static |
Definition at line 177 of file fuzz_request.c.
Referenced by drain_and_harvest(), expand_nonce(), and LLVMFuzzerTestOneInput().
|
static |
Most recent nonce harvested from a 401 response.
Definition at line 176 of file fuzz_request.c.
Referenced by drain_and_harvest(), expand_nonce(), and LLVMFuzzerTestOneInput().
|
static |
Definition at line 182 of file fuzz_request.c.
Referenced by ahc(), LLVMFuzzerTestOneInput(), and oracle_check_body().
|
static |
Connections suspended by suspend mode 2, which the pump loop still has to resume. An entry is cleared by completed_cb() so that a connection MHD has finished with is never resumed afterwards.
This has to be a set, not a single slot: op 3 opens a fresh connection without waiting for the previous one to finish, so several connections can be parked at the same time. A single slot silently loses all but the last, and the forgotten one is still suspended when the iteration calls MHD_stop_daemon(), which answers with MHD_PANIC ("MHD_stop_daemon() called while we have suspended connections") – a harness bug that looks exactly like an MHD bug.
Definition at line 292 of file fuzz_request.c.
Referenced by LLVMFuzzerTestOneInput(), pending_resume_add(), pending_resume_drop(), and pending_resume_flush().
|
static |
Body used by the buffer / fd / pipe / iovec response kinds.
Definition at line 366 of file fuzz_request.c.
Referenced by make_memfd(), make_pipe_fd(), and make_response().
|
static |
Bytes received from the daemon during the current iteration.
Definition at line 209 of file fuzz_request.c.
Referenced by drain_and_harvest(), and LLVMFuzzerTestOneInput().
|
static |
Definition at line 210 of file fuzz_request.c.
Referenced by drain_and_harvest(), and LLVMFuzzerTestOneInput().
|
static |
Definition at line 3019 of file fuzz_request.c.
Referenced by fuzz_seed_get().
|
static |
Definition at line 2758 of file fuzz_request.c.
Referenced by fuzz_seed_count(), and fuzz_seed_get().
|
static |
Definition at line 190 of file fuzz_request.c.
Referenced by ahc(), and print_stats().
|
static |
Definition at line 188 of file fuzz_request.c.
Referenced by ahc(), and print_stats().
|
static |
Definition at line 189 of file fuzz_request.c.
Referenced by ahc(), and print_stats().
|
static |
Statistics, printed at exit with –verbose.
Definition at line 185 of file fuzz_request.c.
Referenced by LLVMFuzzerTestOneInput(), and print_stats().
|
static |
Definition at line 187 of file fuzz_request.c.
Referenced by ahc(), and print_stats().
|
static |
Definition at line 186 of file fuzz_request.c.
Referenced by ahc(), and print_stats().
|
static |
Definition at line 191 of file fuzz_request.c.
Referenced by LLVMFuzzerTestOneInput().
|
static |
Set once the iteration has stopped feeding the daemon and only wants to drain it. suspend_maybe() then does nothing, which is what makes the flush loop in the teardown provably terminate: without it, resuming a connection lets the handler run and park it again, for as many rounds as there are pipelined requests still buffered.
Definition at line 301 of file fuzz_request.c.
Referenced by LLVMFuzzerTestOneInput(), and suspend_maybe().