GNU libmicrohttpd 1.0.9
Loading...
Searching...
No Matches
fuzz_eventloop.c File Reference

In-process fuzzer for MHD's external event loop and for the scheduling of the connection life cycle. More...

#include "fuzz_common.h"
#include <microhttpd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/select.h>
#include <limits.h>
Include dependency graph for fuzz_eventloop.c:

Go to the source code of this file.

Macros

#define FUZZ_HARNESS_NAME   "fuzz_eventloop"
 
#define MAX_CONNS   8
 
#define MAX_OPS   400
 
#define BIG_BODY_LEN   131072
 
#define DRAIN_BUF   4096
 
#define MAX_LIVE   (MAX_CONNS + 4)
 
#define OPB(o, a)
 

Enumerations

enum  op {
  OP_SEND_FRAG = 0 , OP_SEND_RAW = 1 , OP_FDSET = 2 , OP_RUN = 3 ,
  OP_TIMEOUT = 4 , OP_CLOCK = 5 , OP_SUSPEND = 6 , OP_RESUME = 7 ,
  OP_NEWCONN = 8 , OP_CLOSECONN = 9 , OP_SWITCH = 10 , OP_QUIESCE = 11 ,
  OP_DRAIN = 12 , OP_CONNOPT = 13 , OP_INFO = 14 , OP_POLL = 15
}
 

Functions

static void print_stats (void)
 
static void panic_cb (void *cls, const char *file, unsigned int line, const char *reason)
 
static int slot_of (const struct MHD_Connection *c)
 
static void notify_conn_cb (void *cls, struct MHD_Connection *connection, void **socket_context, enum MHD_ConnectionNotificationCode toe)
 
static int mhd_thinks_suspended (unsigned int i)
 
static void do_suspend (unsigned int i)
 
static void do_resume (unsigned int i)
 
static int resume_all (void)
 
static int scan_live_timeouts (uint64_t *max_ms)
 
static ssize_t crc_cb (void *cls, uint64_t pos, char *buf, size_t max)
 
static void crc_free (void *cls)
 
static struct MHD_Responsemake_response (void)
 
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 check_timeouts (struct MHD_Daemon *d)
 
static void check_fdsets (unsigned int setsize, MHD_socket max_fd, int had_max, int with_es)
 
static void op_fdset (struct MHD_Daemon *d, unsigned int var)
 
static void op_poll (void)
 
static void drain_conn (unsigned int i)
 
static void drain_all (void)
 
static void op_run (struct MHD_Daemon *d, unsigned int var, unsigned int flavour)
 
static int new_connection (struct MHD_Daemon *d)
 
static int queue_unprocessed_conn (struct MHD_Daemon *d)
 
static void close_conn (unsigned int i, int graceful)
 
static void send_bytes (unsigned int i, const void *buf, size_t len)
 
static void wait_for_expiry (struct MHD_Daemon *d)
 
static void op_quiesce (struct MHD_Daemon *d)
 
static void op_info (struct MHD_Daemon *d, unsigned int arg)
 
static void op_connopt (unsigned int i, unsigned int val)
 
int LLVMFuzzerTestOneInput (const uint8_t *data, size_t size)
 
static void sb_byte (struct sbuf *b, uint8_t v)
 
static void sb_op (struct sbuf *b, unsigned int opc, unsigned int arg)
 
static unsigned int gen_frag (struct fuzz_rng *rng)
 
static void gen_loop_round (struct fuzz_rng *rng, struct sbuf *b)
 
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 el_cfg cfg
 
static const size_t mem_limit_tbl [] = { 0, 256, 1024, 4096 }
 
static const unsigned int timeout_tbl [] = { 1, 2, 0, 1 }
 
static const unsigned int setsize_tbl []
 
static int csock [MAX_CONNS]
 
static unsigned int nconns
 
static unsigned int cur_conn
 
static struct live_conn lives [MAX_LIVE]
 
static struct MHD_Daemoncur_daemon
 
static int tearing_down
 
static MHD_socket quiesced_fd = MHD_INVALID_SOCKET
 
static uint64_t clk_ms
 
static uint64_t deadline_ms
 
static int deadline_valid
 
static fd_set g_rs
 
static fd_set g_ws
 
static fd_set g_es
 
static MHD_socket g_max_fd = MHD_INVALID_SOCKET
 
static unsigned int g_setsize = (unsigned int) FD_SETSIZE
 
static int g_have_sets
 
static char big_body [BIG_BODY_LEN]
 
static int big_body_ready
 
static int expiry_budget = 2
 
static int expiry_budget_read
 
static unsigned long stat_daemons
 
static unsigned long stat_handler_calls
 
static unsigned long stat_fdset_v1
 
static unsigned long stat_fdset_v2
 
static unsigned long stat_rfs_v1
 
static unsigned long stat_rfs_v2
 
static unsigned long stat_run
 
static unsigned long stat_run_wait
 
static unsigned long stat_timeouts
 
static unsigned long stat_quiesce
 
static unsigned long stat_suspend
 
static unsigned long stat_resume
 
static unsigned long stat_expiry_waits
 
static unsigned long stat_queued_at_stop
 
static int stats_registered
 
static const char *const frag_tbl [16]
 
static const unsigned char gen_frag_bias []
 
static const struct seed_def seeds []
 
static uint8_t seed_render_buf [64]
 

Detailed Description

In-process fuzzer for MHD's external event loop and for the scheduling of the connection life cycle.

Author
Christian Grothoff

fuzz_request.c already touches these entry points, but only as a side channel: it picks one event-loop mode per iteration and then always pumps in the same rhythm. The bugs this harness is after are in the scheduling – what MHD does when the application polls at adversarial times, ignores the timeout it was given, calls MHD_run_from_select() with descriptor sets that do not match what MHD asked for, or suspends/resumes connections across those calls.

The input is therefore not an HTTP request but a schedule: a short configuration block followed by a program of one-byte opcodes that is interpreted against a live struct MHD_Daemon with up to MAX_CONNS socketpair connections. Request bytes enter the program through two of those opcodes (a fragment table and a raw literal), so the parser is still driven, just not fuzzed.

Input format

byte 0 daemon configuration bits 0-1 MHD_OPTION_CONNECTION_TIMEOUT selector, index into {1, 2, 0, 1} seconds bit 2 give the daemon a real listening socket (port 0) instead of MHD_USE_NO_LISTEN_SOCKET, so that MHD_quiesce_daemon() has something to do and the listen FD shows up in the descriptor sets bit 3 pass MHD_OPTION_APP_FD_SETSIZE bits 4-5 MHD_OPTION_CONNECTION_MEMORY_LIMIT selector, index into {default, 256, 1024, 4096} bit 6 MHD_OPTION_CONNECTION_LIMIT = 2, so that MHD_add_connection() starts failing bit 7 shrink SO_SNDBUF/SO_RCVBUF of the socketpair, so that responses do not fit and connections stay blocked on write byte 1 access-handler behaviour bits 0-1 response kind: 0 small static buffer, 1 chunked callback (MHD_SIZE_UNKNOWN), 2 large (128 KiB) static buffer, 3 empty bit 2 answer 500 instead of 200 bit 3 suspend in the handler and leave it parked bit 4 suspend and resume immediately in the handler bit 5 call MHD_set_connection_option() from the handler bit 6 call MHD_get_connection_info() from the handler bit 7 drain the client side after every run byte 2 event-loop defaults bits 0-2 default MHD_get_fdset*() variant bits 3-4 default run entry point bit 5 honour the timeout MHD returns bit 6 query the timeouts after every run bit 7 MHD_quiesce_daemon() before stopping byte 3 schedule knobs bits 0-1 number of connections opened up front (1-4) bit 2 run the timeout oracle after every operation bit 3 allow a real-time wait for a connection timeout to expire (globally budgeted, see below) bit 4 queue one more connection at the very end and stop the daemon without running the loop again, so that MHD has to dispose of a connection it never started (see queue_unprocessed_conn()) bits 5-7 unused byte 4 artificial-clock step base byte 5. the operation program. Each operation is one byte, (opcode << 4) | argument; see enum op below. OP_SEND_RAW is the only one with an operand: a length byte followed by that many payload bytes.

An input shorter than five bytes is rejected: the configuration block is mandatory and an input that short carries no program either.

Oracles

Beyond ASAN/UBSAN and the MHD_set_panic_func() tripwire (any MHD_PANIC() reached this way is a finding, including the "MHD_stop_daemon() called while we have suspended connections" one that answers the "the daemon must always be stoppable" requirement):

a) the four timeout accessors must agree. MHD_get_timeout(), MHD_get_timeout64(), MHD_get_timeout64s() and MHD_get_timeout_i() read the same state, so either all of them report a timeout or none of them does. Their values are read in sequence, so the clock may advance between two of them and the value may only decrease; connection_get_wait() has a documented 100 ms floor for the "exact match" case, hence the 100 ms slack.

b) a reported timeout must never exceed the largest connection timeout in effect. MHD only ever derives it from last_activity + connection_timeout_ms, so a larger value would mean the deadline is somewhere the application cannot reach.

c) MHD must never ask the application to wait forever while it has a live, not-suspended connection with a non-zero timeout: that is precisely the "timeout in the past" situation, i.e. a connection that can never be reaped. Live connections are tracked through MHD_OPTION_NOTIFY_CONNECTION, which brackets exactly the interval in which MHD owns the connection object.

Both inputs of (b) and (c) – whether a connection is suspended and what its timeout is – are read back from MHD rather than modelled, because both diverge from what the application asked for: MHD_set_connection_option() silently does nothing while a connection is suspended. A harness that models them ends up reporting findings against its own model.

d) MHD_get_fdset*() must not set a descriptor at or above the FD_SETSIZE limit it was given, *max_fd must be at least as large as every descriptor that was added, and *max_fd itself must be one of them.

e) after MHD_quiesce_daemon() the listening socket must be gone from the descriptor sets.

Two contract rules the harness has to respect (violating either makes MHD abort on a schedule that is perfectly legal, so the harness would only be finding its own bugs):

  • a connection is only suspended when the access handler is about to return MHD_YES. MHD_NO asks MHD to terminate the connection, and terminating one that the same callback just suspended trips mhd_assert (! connection->suspended) in MHD_connection_close_();
  • every suspended connection is resumed before MHD_stop_daemon(). Several connections can be parked at once, so this is a set (lives[i].susp), not a single slot, and tearing_down stops the handler from parking anything new while the set is drained.

MHD_suspend_connection() is additionally only called when MHD does not already consider the connection suspended, unless a resume of it is still pending – that is the one case internal_suspend_connection_() handles itself (it cancels the resume), and the harness mirrors the resulting state. Calling it on an already-suspended connection would unlink it from a list it is not on.

One more trap: with MHD_USE_ITC – which MHD_ALLOW_SUSPEND_RESUME implies, so this harness always has it – MHD_add_connection() only queues the socket, and the struct MHD_Connection is created later, from inside a run. There is therefore no moment at which the application could pair its own socket up with the connection object, and the registry of live connections has to be built purely from MHD_OPTION_NOTIFY_CONNECTION. This is why the client sockets (csock) and the connections (lives) are two separate arrays.

Note on MHD_get_timeout_expiration(): no such function exists in the MHD 1.x API. The four accessors above are the complete family; the absolute-deadline form is a MHD 2.x addition.

Definition in file fuzz_eventloop.c.

Macro Definition Documentation

◆ BIG_BODY_LEN

#define BIG_BODY_LEN   131072

Size of the "large response" body.

Definition at line 188 of file fuzz_eventloop.c.

Referenced by LLVMFuzzerTestOneInput(), and make_response().

◆ DRAIN_BUF

#define DRAIN_BUF   4096

Bytes read back from the daemon are counted, never inspected.

Definition at line 191 of file fuzz_eventloop.c.

Referenced by drain_conn().

◆ FUZZ_HARNESS_NAME

#define FUZZ_HARNESS_NAME   "fuzz_eventloop"

Definition at line 172 of file fuzz_eventloop.c.

Referenced by print_stats().

◆ MAX_CONNS

#define MAX_CONNS   8

Largest number of connections a single iteration may have open.

Definition at line 182 of file fuzz_eventloop.c.

Referenced by close_conn(), drain_all(), drain_conn(), LLVMFuzzerTestOneInput(), new_connection(), and send_bytes().

◆ MAX_LIVE

#define MAX_LIVE   (MAX_CONNS + 4)

A few more slots than connections, so the registry cannot overflow.

Definition at line 285 of file fuzz_eventloop.c.

Referenced by do_resume(), do_suspend(), LLVMFuzzerTestOneInput(), notify_conn_cb(), op_connopt(), op_info(), resume_all(), scan_live_timeouts(), and slot_of().

◆ MAX_OPS

#define MAX_OPS   400

Upper bound on the number of operations interpreted per iteration.

Definition at line 185 of file fuzz_eventloop.c.

Referenced by LLVMFuzzerTestOneInput().

◆ OPB

#define OPB ( o,
a )
Value:
(unsigned char) (((o) << 4) | (a))

Definition at line 1909 of file fuzz_eventloop.c.

Enumeration Type Documentation

◆ op

enum op
Enumerator
OP_SEND_FRAG 
OP_SEND_RAW 
OP_FDSET 
OP_RUN 
OP_TIMEOUT 
OP_CLOCK 
OP_SUSPEND 
OP_RESUME 
OP_NEWCONN 
OP_CLOSECONN 
OP_SWITCH 
OP_QUIESCE 
OP_DRAIN 
OP_CONNOPT 
OP_INFO 
OP_POLL 

Definition at line 1274 of file fuzz_eventloop.c.

Function Documentation

◆ ahc()

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

◆ check_fdsets()

static void check_fdsets ( unsigned int setsize,
MHD_socket max_fd,
int had_max,
int with_es )
static

Check the descriptor sets that the last MHD_get_fdset*() produced.

Parameters
setsizethe FD_SETSIZE limit that was passed to MHD
max_fdthe reported maximum, MHD_INVALID_SOCKET if none
had_maxwhether a max_fd pointer was passed at all
with_eswhether an except set was passed

Definition at line 844 of file fuzz_eventloop.c.

References fuzz_report_finding(), g_es, g_rs, g_ws, and MHD_INVALID_SOCKET.

Referenced by op_fdset().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ check_timeouts()

static void check_timeouts ( struct MHD_Daemon * d)
static

Query all four timeout accessors and cross-check them.

The values are read one after the other, so the monotonic clock may advance in between and a later reading may be smaller. It may never be larger, except for the 100 ms floor connection_get_wait() falls back to when the elapsed time exactly matches the timeout.

Definition at line 760 of file fuzz_eventloop.c.

References clk_ms, deadline_ms, deadline_valid, fuzz_report_finding(), MHD_get_timeout(), MHD_get_timeout64(), MHD_get_timeout64s(), MHD_get_timeout_i(), MHD_UNSIGNED_LONG_LONG, MHD_YES, scan_live_timeouts(), and stat_timeouts.

Referenced by LLVMFuzzerTestOneInput(), and op_run().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ close_conn()

static void close_conn ( unsigned int i,
int graceful )
static

Drop our end of connection i.

Parameters
gracefulnon-zero to shut the write side down first (an orderly client close), zero to just close (an abrupt one, which MHD sees as a reset)

Definition at line 1193 of file fuzz_eventloop.c.

References csock, and MAX_CONNS.

Referenced by LLVMFuzzerTestOneInput().

Here is the caller graph for this function:

◆ crc_cb()

static ssize_t crc_cb ( void * cls,
uint64_t pos,
char * buf,
size_t max )
static

Definition at line 590 of file fuzz_eventloop.c.

References MHD_CONTENT_READER_END_OF_STREAM.

Referenced by make_response().

Here is the caller graph for this function:

◆ crc_free()

static void crc_free ( void * cls)
static

Definition at line 613 of file fuzz_eventloop.c.

Referenced by make_response().

Here is the caller graph for this function:

◆ do_resume()

static void do_resume ( unsigned int i)
static

Definition at line 488 of file fuzz_eventloop.c.

References lives, MAX_LIVE, MHD_resume_connection(), NULL, and stat_resume.

Referenced by LLVMFuzzerTestOneInput().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ do_suspend()

static void do_suspend ( unsigned int i)
static

Suspend slot i, if that is legal right now.

MHD_suspend_connection() unlinks the connection from the timeout and connection lists, so calling it on a connection MHD already has on the suspended list corrupts those lists. The one exception is a connection with a resume still pending: internal_suspend_connection_() detects that itself and merely cancels the resume, leaving the connection suspended – which is why susp is set in both branches.

Definition at line 471 of file fuzz_eventloop.c.

References lives, MAX_LIVE, MHD_suspend_connection(), mhd_thinks_suspended(), NULL, and stat_suspend.

Referenced by LLVMFuzzerTestOneInput().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ drain_all()

static void drain_all ( void )
static

Definition at line 1012 of file fuzz_eventloop.c.

References drain_conn(), and MAX_CONNS.

Referenced by LLVMFuzzerTestOneInput(), and op_run().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ drain_conn()

static void drain_conn ( unsigned int i)
static

Drain whatever the daemon has produced on slot i, so that a large response can make progress.

Definition at line 994 of file fuzz_eventloop.c.

References csock, DRAIN_BUF, and MAX_CONNS.

Referenced by drain_all(), and LLVMFuzzerTestOneInput().

Here is the caller graph for this function:

◆ fuzz_generate()

static size_t fuzz_generate ( struct fuzz_rng * rng,
uint8_t * buf,
size_t cap )
static

◆ fuzz_seed_count()

static size_t fuzz_seed_count ( void )
static

Definition at line 2041 of file fuzz_eventloop.c.

References seeds.

◆ fuzz_seed_get()

static const uint8_t * fuzz_seed_get ( size_t idx,
size_t * len )
static

Definition at line 2048 of file fuzz_eventloop.c.

References seed_render_buf, and seeds.

◆ gen_frag()

static unsigned int gen_frag ( struct fuzz_rng * rng)
static

Definition at line 1739 of file fuzz_eventloop.c.

References fuzz_below(), fuzz_chance(), and gen_frag_bias.

Referenced by fuzz_generate().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ gen_loop_round()

static void gen_loop_round ( struct fuzz_rng * rng,
struct sbuf * b )
static

Emit "collect the descriptors, poll, run" – the shape a real external event loop has – with a randomly chosen variant of each step.

Definition at line 1753 of file fuzz_eventloop.c.

References fuzz_below(), fuzz_chance(), OP_FDSET, OP_POLL, OP_RUN, OP_TIMEOUT, and sb_op().

Referenced by fuzz_generate().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ LLVMFuzzerTestOneInput()

int LLVMFuzzerTestOneInput ( const uint8_t * data,
size_t size )

◆ make_response()

static struct MHD_Response * make_response ( void )
static

Definition at line 620 of file fuzz_eventloop.c.

References big_body, BIG_BODY_LEN, cfg, crc_cb(), crc_free(), MHD_create_response_empty(), MHD_create_response_from_buffer_static(), MHD_create_response_from_callback(), MHD_RF_NONE, MHD_SIZE_UNKNOWN, and NULL.

Referenced by ahc().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ mhd_thinks_suspended()

static int mhd_thinks_suspended ( unsigned int i)
static

Ask MHD whether it considers slot i suspended.

Definition at line 446 of file fuzz_eventloop.c.

References lives, MHD_CONNECTION_INFO_CONNECTION_SUSPENDED, MHD_get_connection_info(), MHD_YES, NULL, and MHD_ConnectionInfo::suspended.

Referenced by do_suspend().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ new_connection()

static int new_connection ( struct MHD_Daemon * d)
static

Definition at line 1099 of file fuzz_eventloop.c.

References cfg, csock, cur_conn, MAX_CONNS, MHD_add_connection(), MHD_YES, and nconns.

Referenced by LLVMFuzzerTestOneInput().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ notify_conn_cb()

static void notify_conn_cb ( void * cls,
struct MHD_Connection * connection,
void ** socket_context,
enum MHD_ConnectionNotificationCode toe )
static

Definition at line 406 of file fuzz_eventloop.c.

References lives, MAX_LIVE, MHD_CONNECTION_NOTIFY_STARTED, NULL, and slot_of().

Referenced by LLVMFuzzerTestOneInput().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ op_connopt()

static void op_connopt ( unsigned int i,
unsigned int val )
static

Definition at line 1378 of file fuzz_eventloop.c.

References lives, MAX_LIVE, MHD_CONNECTION_OPTION_TIMEOUT, MHD_set_connection_option(), and NULL.

Referenced by LLVMFuzzerTestOneInput().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ op_fdset()

static void op_fdset ( struct MHD_Daemon * d,
unsigned int var )
static

Collect the descriptor sets.

Variant 0 goes through the real v1 entry point. microhttpd.h also defines MHD_get_fdset as a macro forwarding to MHD_get_fdset2 with FD_SETSIZE, so the name has to be parenthesised or the v1 function is never reached at all. Same trick for MHD_run_from_select below.

Definition at line 911 of file fuzz_eventloop.c.

References cfg, check_fdsets(), fuzz_report_finding(), g_es, g_have_sets, g_max_fd, g_rs, g_setsize, g_ws, MHD_get_fdset, MHD_get_fdset2(), MHD_INVALID_SOCKET, NULL, quiesced_fd, setsize_tbl, stat_fdset_v1, and stat_fdset_v2.

Referenced by LLVMFuzzerTestOneInput().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ op_info()

static void op_info ( struct MHD_Daemon * d,
unsigned int arg )
static

◆ op_poll()

static void op_poll ( void )
static

select() on the sets collected last, with a zero timeout.

The harness is single threaded and everything MHD could be waiting for has already been written into the socketpairs, so blocking would only burn wall clock; select() is called purely to fill in the readiness.

Definition at line 976 of file fuzz_eventloop.c.

References g_es, g_have_sets, g_max_fd, g_rs, g_ws, and MHD_INVALID_SOCKET.

Referenced by LLVMFuzzerTestOneInput().

Here is the caller graph for this function:

◆ op_quiesce()

static void op_quiesce ( struct MHD_Daemon * d)
static

Definition at line 1326 of file fuzz_eventloop.c.

References fuzz_report_finding(), MHD_INVALID_SOCKET, MHD_quiesce_daemon(), quiesced_fd, and stat_quiesce.

Referenced by LLVMFuzzerTestOneInput().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ op_run()

static void op_run ( struct MHD_Daemon * d,
unsigned int var,
unsigned int flavour )
static

Advance the daemon once.

Parameters
dthe daemon
varwhich entry point to use
flavourwhich descriptor sets to hand over: 0 the ones MHD asked for, 1 all-zero, 2 all-ones, 3 only the read set as collected

Definition at line 1031 of file fuzz_eventloop.c.

References cfg, check_timeouts(), deadline_valid, drain_all(), g_es, g_rs, g_setsize, g_ws, MHD_run(), MHD_run_from_select, MHD_run_from_select2(), MHD_run_wait(), stat_rfs_v1, stat_rfs_v2, stat_run, and stat_run_wait.

Referenced by LLVMFuzzerTestOneInput().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ panic_cb()

static void panic_cb ( void * cls,
const char * file,
unsigned int line,
const char * reason )
static

Definition at line 371 of file fuzz_eventloop.c.

References fuzz_report_finding(), and NULL.

Referenced by LLVMFuzzerTestOneInput().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ print_stats()

static void print_stats ( void )
static

◆ queue_unprocessed_conn()

static int queue_unprocessed_conn ( struct MHD_Daemon * d)
static

Hand the daemon one more connection and do not run the loop again.

MHD_add_connection() on a thread-safe daemon does not build the struct MHD_Connection right away; it puts the socket on daemon->new_connections_head and leaves the rest to the next run. Stopping the daemon before that run is the only way to reach new_connection_close_() in daemon.c, which is where MHD disposes of a connection it accepted but never started. The ordinary teardown below always calls MHD_run() once more, which is why 1.6 billion executions left that function at zero coverage.

The socket pair is deliberately kept out of the harness's own connection table: nothing is ever sent on it, no notify callback fires for it, and by this point the teardown has already closed every tracked slot. Our end is returned so that the caller can close it after MHD_stop_daemon().

Parameters
dthe daemon, about to be stopped
Returns
our end of the socket pair, or -1 if nothing was queued

Definition at line 1160 of file fuzz_eventloop.c.

References MHD_add_connection(), MHD_YES, and stat_queued_at_stop.

Referenced by LLVMFuzzerTestOneInput().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ resume_all()

static int resume_all ( void )
static

Resume everything still parked.

Returns
non-zero if at least one connection was resumed, so that the caller knows the daemon needs another round to act on it

Definition at line 509 of file fuzz_eventloop.c.

References lives, MAX_LIVE, MHD_resume_connection(), NULL, and stat_resume.

Referenced by LLVMFuzzerTestOneInput(), and wait_for_expiry().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ sb_byte()

static void sb_byte ( struct sbuf * b,
uint8_t v )
static

Definition at line 1711 of file fuzz_eventloop.c.

Referenced by fuzz_generate(), and sb_op().

Here is the caller graph for this function:

◆ sb_op()

static void sb_op ( struct sbuf * b,
unsigned int opc,
unsigned int arg )
static

Definition at line 1720 of file fuzz_eventloop.c.

References sb_byte().

Referenced by fuzz_generate(), and gen_loop_round().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ scan_live_timeouts()

static int scan_live_timeouts ( uint64_t * max_ms)
static

Summarise the connections MHD currently owns.

Both properties are read back from MHD rather than modelled: a connection leaves the timeout lists exactly when MHD sets connection->suspended, and its timeout is exactly what MHD_set_connection_option() left there – which is not what the application passed if the connection happened to be suspended at the time. Modelling either of them means the oracle eventually fires on the model rather than on MHD.

Parameters
[out]max_mslargest timeout, in milliseconds, of the connections that are in a timeout list right now
Returns
non-zero if at least one such connection exists, i.e. if MHD must report a timeout

Definition at line 545 of file fuzz_eventloop.c.

References MHD_ConnectionInfo::connection_timeout, lives, MAX_LIVE, MHD_CONNECTION_INFO_CONNECTION_SUSPENDED, MHD_CONNECTION_INFO_CONNECTION_TIMEOUT, MHD_get_connection_info(), MHD_YES, NULL, and MHD_ConnectionInfo::suspended.

Referenced by check_timeouts().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ send_bytes()

static void send_bytes ( unsigned int i,
const void * buf,
size_t len )
static

Definition at line 1207 of file fuzz_eventloop.c.

References csock, cur_daemon, MAX_CONNS, MHD_run(), and NULL.

Referenced by LLVMFuzzerTestOneInput().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ slot_of()

static int slot_of ( const struct MHD_Connection * c)
static

Find the registry slot of c, or -1.

Definition at line 393 of file fuzz_eventloop.c.

References lives, MAX_LIVE, and NULL.

Referenced by ahc(), and notify_conn_cb().

Here is the caller graph for this function:

◆ wait_for_expiry()

static void wait_for_expiry ( struct MHD_Daemon * d)
static

Wait in real time until the connection timeout of the current daemon has certainly expired, so that the expiry path is actually reached.

MHD_OPTION_CONNECTION_TIMEOUT has a resolution of one second, so this costs about that much wall clock and is therefore globally budgeted.

Definition at line 1303 of file fuzz_eventloop.c.

References cfg, clk_ms, expiry_budget, MHD_run(), NULL, resume_all(), and stat_expiry_waits.

Referenced by LLVMFuzzerTestOneInput().

Here is the call graph for this function:
Here is the caller graph for this function:

Variable Documentation

◆ big_body

char big_body[BIG_BODY_LEN]
static

The large response body, filled once.

Definition at line 316 of file fuzz_eventloop.c.

Referenced by LLVMFuzzerTestOneInput(), and make_response().

◆ big_body_ready

int big_body_ready
static

Definition at line 317 of file fuzz_eventloop.c.

Referenced by LLVMFuzzerTestOneInput().

◆ cfg

struct el_cfg cfg
static

◆ clk_ms

uint64_t clk_ms
static

Artificial clock; only the harness's own scheduling depends on it.

Definition at line 303 of file fuzz_eventloop.c.

Referenced by check_timeouts(), LLVMFuzzerTestOneInput(), and wait_for_expiry().

◆ csock

int csock[MAX_CONNS]
static

The client end of one socketpair. Deliberately not tied to a struct MHD_Connection: with MHD_USE_ITC (which MHD_ALLOW_SUSPEND_RESUME implies) MHD_add_connection() only queues the socket and the connection object is created later, from inside a run. There is therefore no moment at which the harness could pair the two up, and trying to would silently mistrack every connection.

Definition at line 265 of file fuzz_eventloop.c.

Referenced by close_conn(), drain_conn(), LLVMFuzzerTestOneInput(), new_connection(), and send_bytes().

◆ cur_conn

unsigned int cur_conn
static

Definition at line 267 of file fuzz_eventloop.c.

Referenced by LLVMFuzzerTestOneInput(), and new_connection().

◆ cur_daemon

struct MHD_Daemon* cur_daemon
static

Daemon of the current iteration.

Definition at line 290 of file fuzz_eventloop.c.

Referenced by LLVMFuzzerTestOneInput(), and send_bytes().

◆ deadline_ms

uint64_t deadline_ms
static

Definition at line 304 of file fuzz_eventloop.c.

Referenced by check_timeouts(), and LLVMFuzzerTestOneInput().

◆ deadline_valid

int deadline_valid
static

Definition at line 305 of file fuzz_eventloop.c.

Referenced by check_timeouts(), LLVMFuzzerTestOneInput(), and op_run().

◆ expiry_budget

int expiry_budget = 2
static

Budget for iterations that are allowed to wait in real time for a connection timeout to expire. MHD_OPTION_CONNECTION_TIMEOUT has a resolution of one second, so the expiry path cannot be reached without actually burning about that much wall clock; the budget keeps the whole run in the "couple of seconds" range while still exercising it. Override with MHD_FUZZ_EXPIRY_BUDGET.

Definition at line 327 of file fuzz_eventloop.c.

Referenced by LLVMFuzzerTestOneInput(), and wait_for_expiry().

◆ expiry_budget_read

int expiry_budget_read
static

Definition at line 328 of file fuzz_eventloop.c.

Referenced by LLVMFuzzerTestOneInput().

◆ frag_tbl

const char* const frag_tbl[16]
static
Initial value:
= {
"GET / HTTP/1.1\r\nHost: x\r\n\r\n",
"GET /a HTTP/1.1\r\nHost: x\r\n",
"\r\n",
"POST /p HTTP/1.1\r\nHost: x\r\nContent-Length: 10\r\n\r\n",
"0123456789",
"POST /c HTTP/1.1\r\nHost: x\r\nTransfer-Encoding: chunked\r\n\r\n",
"5\r\nabcde\r\n",
"0\r\n\r\n",
"GET / HTTP/1.1\r\nHost: x\r\nConnection: close\r\n\r\n",
"GET / HTTP/1.0\r\n\r\n",
"HEAD / HTTP/1.1\r\nHost: x\r\n\r\n",
"GET / HTTP/1.1\r\nHost: x\r\n\r\nGET /2 HTTP/1.1\r\nHost: x\r\n\r\n",
"G",
"ET / HTTP/1.1\r\n",
"Host: x\r\n\r\n",
"\r\n\r\n"
}

Request fragments. Whole requests, halves of requests and pipelines, so that a schedule can leave a connection in any parser state while it plays with the event loop.

Definition at line 1250 of file fuzz_eventloop.c.

Referenced by LLVMFuzzerTestOneInput().

◆ g_es

fd_set g_es
static

Definition at line 310 of file fuzz_eventloop.c.

Referenced by check_fdsets(), op_fdset(), op_poll(), and op_run().

◆ g_have_sets

int g_have_sets
static

Definition at line 313 of file fuzz_eventloop.c.

Referenced by LLVMFuzzerTestOneInput(), op_fdset(), and op_poll().

◆ g_max_fd

MHD_socket g_max_fd = MHD_INVALID_SOCKET
static

Definition at line 311 of file fuzz_eventloop.c.

Referenced by LLVMFuzzerTestOneInput(), op_fdset(), and op_poll().

◆ g_rs

fd_set g_rs
static

Descriptor sets collected by the last OP_FDSET.

Definition at line 308 of file fuzz_eventloop.c.

Referenced by check_fdsets(), op_fdset(), op_poll(), and op_run().

◆ g_setsize

unsigned int g_setsize = (unsigned int) FD_SETSIZE
static

Definition at line 312 of file fuzz_eventloop.c.

Referenced by LLVMFuzzerTestOneInput(), op_fdset(), and op_run().

◆ g_ws

fd_set g_ws
static

Definition at line 309 of file fuzz_eventloop.c.

Referenced by check_fdsets(), op_fdset(), op_poll(), and op_run().

◆ gen_frag_bias

const unsigned char gen_frag_bias[]
static
Initial value:
= {
0, 0, 0, 3, 4, 5, 6, 7, 8, 9, 10, 11, 11, 1, 2, 12, 13, 14, 15
}

Fragment indices, weighted towards the ones that complete a request: a schedule that never reaches the access handler exercises very little of the connection life cycle.

Definition at line 1733 of file fuzz_eventloop.c.

Referenced by gen_frag().

◆ lives

◆ mem_limit_tbl

const size_t mem_limit_tbl[] = { 0, 256, 1024, 4096 }
static

Connection memory limits selectable by byte 0.

Definition at line 231 of file fuzz_eventloop.c.

Referenced by LLVMFuzzerTestOneInput().

◆ nconns

unsigned int nconns
static

Definition at line 266 of file fuzz_eventloop.c.

Referenced by LLVMFuzzerTestOneInput(), and new_connection().

◆ quiesced_fd

MHD_socket quiesced_fd = MHD_INVALID_SOCKET
static

Listening socket returned by MHD_quiesce_daemon(), ours to close.

Definition at line 300 of file fuzz_eventloop.c.

Referenced by LLVMFuzzerTestOneInput(), op_fdset(), and op_quiesce().

◆ seed_render_buf

uint8_t seed_render_buf[64]
static

Definition at line 2037 of file fuzz_eventloop.c.

Referenced by fuzz_seed_get().

◆ seeds

const struct seed_def seeds[]
static

Definition at line 1911 of file fuzz_eventloop.c.

Referenced by fuzz_seed_count(), and fuzz_seed_get().

◆ setsize_tbl

const unsigned int setsize_tbl[]
static
Initial value:
= {
(unsigned int) FD_SETSIZE,
(unsigned int) FD_SETSIZE,
(unsigned int) FD_SETSIZE / 2,
64, 16, 4, 1, (unsigned int) FD_SETSIZE
}

FD_SETSIZE values handed to MHD_get_fdset2()/MHD_run_from_select2().

All of them are <= FD_SETSIZE on purpose. Passing a larger value than the fd_set objects actually hold would let MHD write past them, which is the application lying about its own buffers rather than anything MHD could defend against; the interesting direction is the smaller one, where MHD has to refuse descriptors that do not fit.

Definition at line 245 of file fuzz_eventloop.c.

Referenced by op_fdset().

◆ stat_daemons

unsigned long stat_daemons
static

Statistics, printed at exit with –verbose.

Definition at line 331 of file fuzz_eventloop.c.

Referenced by LLVMFuzzerTestOneInput(), and print_stats().

◆ stat_expiry_waits

unsigned long stat_expiry_waits
static

Definition at line 343 of file fuzz_eventloop.c.

Referenced by print_stats(), and wait_for_expiry().

◆ stat_fdset_v1

unsigned long stat_fdset_v1
static

Definition at line 333 of file fuzz_eventloop.c.

Referenced by op_fdset(), and print_stats().

◆ stat_fdset_v2

unsigned long stat_fdset_v2
static

Definition at line 334 of file fuzz_eventloop.c.

Referenced by op_fdset(), and print_stats().

◆ stat_handler_calls

unsigned long stat_handler_calls
static

Definition at line 332 of file fuzz_eventloop.c.

Referenced by ahc(), and print_stats().

◆ stat_queued_at_stop

unsigned long stat_queued_at_stop
static

Definition at line 344 of file fuzz_eventloop.c.

Referenced by print_stats(), and queue_unprocessed_conn().

◆ stat_quiesce

unsigned long stat_quiesce
static

Definition at line 340 of file fuzz_eventloop.c.

Referenced by op_quiesce(), and print_stats().

◆ stat_resume

unsigned long stat_resume
static

Definition at line 342 of file fuzz_eventloop.c.

Referenced by ahc(), do_resume(), print_stats(), and resume_all().

◆ stat_rfs_v1

unsigned long stat_rfs_v1
static

Definition at line 335 of file fuzz_eventloop.c.

Referenced by op_run(), and print_stats().

◆ stat_rfs_v2

unsigned long stat_rfs_v2
static

Definition at line 336 of file fuzz_eventloop.c.

Referenced by op_run(), and print_stats().

◆ stat_run

unsigned long stat_run
static

Definition at line 337 of file fuzz_eventloop.c.

Referenced by op_run(), and print_stats().

◆ stat_run_wait

unsigned long stat_run_wait
static

Definition at line 338 of file fuzz_eventloop.c.

Referenced by op_run(), and print_stats().

◆ stat_suspend

unsigned long stat_suspend
static

Definition at line 341 of file fuzz_eventloop.c.

Referenced by ahc(), do_suspend(), and print_stats().

◆ stat_timeouts

unsigned long stat_timeouts
static

Definition at line 339 of file fuzz_eventloop.c.

Referenced by check_timeouts(), and print_stats().

◆ stats_registered

int stats_registered
static

Definition at line 345 of file fuzz_eventloop.c.

Referenced by LLVMFuzzerTestOneInput().

◆ tearing_down

int tearing_down
static

Set once the iteration only wants to drain the daemon. The handler then stops parking connections, which is what makes the flush loop in the teardown terminate.

Definition at line 297 of file fuzz_eventloop.c.

Referenced by ahc(), and LLVMFuzzerTestOneInput().

◆ timeout_tbl

const unsigned int timeout_tbl[] = { 1, 2, 0, 1 }
static

Connection timeouts (seconds) selectable by byte 0.

Definition at line 234 of file fuzz_eventloop.c.

Referenced by LLVMFuzzerTestOneInput().