GNU libmicrohttpd 1.0.9
Loading...
Searching...
No Matches
fuzz_options.c
Go to the documentation of this file.
1/*
2 This file is part of libmicrohttpd
3 Copyright (C) 2026 Christian Grothoff
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with this library.
17 If not, see <http://www.gnu.org/licenses/>.
18*/
19
135#define FUZZ_HARNESS_NAME "fuzz_options"
136#include "fuzz_common.h"
137
138#include <microhttpd.h>
139#include <sys/socket.h>
140#include <netinet/in.h>
141#include <sys/select.h>
142#include <poll.h>
143#include <limits.h>
144
145/* MHD_OPTION_STRICT_FOR_CLIENT is superseded by
146 MHD_OPTION_CLIENT_DISCIPLINE_LVL but is still shipped API, and its
147 mapping onto the discipline level is exactly the kind of thing this
148 harness is for. */
149#if defined(__GNUC__) || defined(__clang__)
150#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
151#endif
152
154#define CFG_BYTES 16
155
156#define MAX_SEGMENTS 48
157#define MAX_CONNECTIONS 6
158#define MAX_OPTS 40
159#define RESP_DRAIN_BUF 4096
160#define GEN_BUF_SIZE 4096
161
170#define THREAD_WAIT_MS 2
171
173#define PUMP_ROUNDS 3
175#define PUMP_ROUNDS_LONG 10
176
177
178/* ------------------------------------------------------------------ */
179/* Per-iteration configuration */
180/* ------------------------------------------------------------------ */
181
182struct fuzz_cfg
183{
185 int uses_threads;
187 int listen_sock;
189 int err_log;
191 int suspend_mode;
194 unsigned int loop_mode;
196 int quiesce;
198 int daemon_info;
200 int conn_option;
202 unsigned int resp_kind;
204 int handler_no;
206 unsigned int nconn_max;
208 int real_connect;
210 int accept_policy;
211 int accept_policy_deny;
212};
213
214static struct fuzz_cfg cfg;
215
225
231static int tearing_down;
232
234static unsigned long stat_daemons;
235static unsigned long stat_daemons_failed;
236static unsigned long stat_threaded;
237static unsigned long stat_handler_calls;
238static unsigned long stat_conns_added;
239static unsigned long stat_conns_refused;
240static unsigned long stat_real_conns;
242
243
244static void
246{
247 if (! fuzz_verbose)
248 return;
249 fprintf (stderr,
250 "%s: daemons=%lu (failed to start=%lu, threaded=%lu) "
251 "connections=%lu (refused=%lu, accepted from a real socket=%lu) "
252 "handler calls=%lu\n",
257}
258
259
260/* ------------------------------------------------------------------ */
261/* Flags */
262/* ------------------------------------------------------------------ */
263
291
292#define MODE_COUNT (sizeof (mode_tbl) / sizeof (mode_tbl[0]))
293
301#define RAW_FLAG_MASK (~((unsigned int) MHD_USE_TLS))
302
316#define SUSPEND_BIT \
317 (((unsigned int) MHD_ALLOW_SUSPEND_RESUME) \
318 & ~((unsigned int) MHD_USE_ITC))
319
320
327static unsigned int
328flags_from_input (const uint8_t *data)
329{
330 unsigned int flags;
331
332 if (0xC0 == (data[2] & 0xC0))
333 {
334 /* Raw escape: the flags word comes straight off the input. This is
335 what exercises MHD_start_daemon()'s combination checks (POLL with
336 EPOLL, EPOLL with thread-per-connection, AUTO with either, ...),
337 every one of which answers NULL. */
338 flags = ((unsigned int) data[1])
339 | (((unsigned int) (data[2] & 0x3F)) << 8)
340 | (((unsigned int) data[15]) << 14);
341 flags &= RAW_FLAG_MASK;
342 cfg.listen_sock = (0 == (flags & MHD_USE_NO_LISTEN_SOCKET));
343 cfg.err_log = (0 != (flags & MHD_USE_ERROR_LOG));
344 return flags;
345 }
346
347 flags = mode_tbl[data[0] % MODE_COUNT];
348
349 if (0 != (data[1] & 0x01))
351 if (0 != (data[1] & 0x02))
353 if (0 != (data[1] & 0x04))
355 if ( (0 != (data[1] & 0x08)) &&
357 flags |= MHD_ALLOW_UPGRADE;
358 if (0 != (data[1] & 0x10))
359 flags |= MHD_USE_TURBO;
360 if (0 != (data[1] & 0x20))
361 flags |= MHD_USE_ITC;
362 if (0 != (data[1] & 0x40))
363 cfg.err_log = 1;
364 if (0 != (data[1] & 0x80))
365 flags |= MHD_USE_POST_HANDSHAKE_AUTH_SUPPORT; /* ignored without TLS */
366
367 if (0 != (data[2] & 0x01))
368 cfg.listen_sock = 1;
369 if (0 != (data[2] & 0x02))
370 flags |= MHD_USE_IPv6;
371 if (0 != (data[2] & 0x04))
372 flags |= MHD_USE_DUAL_STACK;
373 if (0 != (data[2] & 0x08))
374 flags |= MHD_USE_TCP_FASTOPEN;
375 if (0 != (data[2] & 0x10))
377 if (0 != (data[2] & 0x20))
378 flags |= MHD_USE_INSECURE_TLS_EARLY_DATA; /* ignored without TLS */
379
380 /* MHD_USE_NO_THREAD_SAFETY together with any internal thread is
381 documented as unsupported and answered with NULL. Drop it here
382 rather than burning the iteration; the raw escape above still
383 reaches that check. */
384 if (0 != (flags & (MHD_USE_INTERNAL_POLLING_THREAD
386 flags &= ~((unsigned int) MHD_USE_NO_THREAD_SAFETY);
387 return flags;
388}
389
390
397static int
398uses_epoll (unsigned int flags)
399{
400 if (0 != (flags & MHD_USE_EPOLL))
401 return 1;
402 return (0 != (flags & MHD_USE_AUTO)) &&
403 (0 == (flags & MHD_USE_THREAD_PER_CONNECTION));
404}
405
406
439static int
441{
442 static int val = -1;
443
444 if (0 > val)
445 {
446 const char *e = getenv ("MHD_FUZZ_QUIESCE_EPOLL_RACE");
447
448 val = ((NULL != e) && (0 != atoi (e))) ? 1 : 0;
449 }
450 return val;
451}
452
453
454/* ------------------------------------------------------------------ */
455/* Option values */
456/* ------------------------------------------------------------------ */
457
458static const size_t mem_limit_tbl[] = {
459 0 /* MHD default */, 128, 192, 256, 384, 512, 1024, 1400,
460 1500, 2048, 4096, 8192, 32768, 64, 0, 0
461};
462
463static const size_t mem_increment_tbl[] = {
464 0 /* MHD default */, 1, 16, 64, 128, 256, 1024, 4096,
465 1500, 32768, 0, 0, 0, 0, 0, 0
466};
467
468static const unsigned int conn_limit_tbl[] = {
469 0, 1, 2, 3, 8, 64, 1024, 100000
470};
471
472static const unsigned int per_ip_limit_tbl[] = {
473 0, 1, 2, 3, 4, 8, 64, 1000
474};
475
476static const unsigned int timeout_tbl[] = {
477 0, 1, 2, 5, 60, 3600, 86400, UINT_MAX
478};
479
480static const unsigned int pool_size_tbl[] = { 2, 3, 4, 2 };
481
482/* Small stacks make pthread_create() fail outright under ASAN, which
483 only produces a NULL daemon; keep the values plausible. */
484static const size_t stack_size_tbl[] = {
485 0, 1024u * 1024u, 2048u * 1024u, 8192u * 1024u
486};
487
488/* Bounded on purpose: the nonce-nc array is nonce_nc_size *
489 sizeof(struct MHD_NonceNc) (about 130 bytes per slot) and is
490 allocated and freed once per iteration. */
491static const unsigned int nonce_nc_tbl[] = { 0, 1, 4, 32 };
492
493static const int discipline_tbl[] = { -3, -2, -1, 0, 1, 2, 3, -4 };
494
495static const int strict_tbl[] = { -1, 0, 1, 2 };
496
497static const unsigned int backlog_tbl[] = { 0, 1, 5, 511 };
498
499static const unsigned int fastopen_tbl[] = { 0, 1, 5, 10 };
500
501/* Anything but the platform's own FD_SETSIZE is rejected unless MHD was
502 built with HAS_FD_SETSIZE_OVERRIDABLE, so the odd values are a small
503 minority: they cost a whole iteration each. */
504static const int fd_setsize_tbl[] = {
505 (int) FD_SETSIZE, (int) FD_SETSIZE, (int) FD_SETSIZE, (int) FD_SETSIZE,
506 (int) FD_SETSIZE, (int) FD_SETSIZE, 64, 0
507};
508
510static const char digest_rnd[32] =
511 "\x01\x23\x45\x67\x89\xab\xcd\xef\x01\x23\x45\x67\x89\xab\xcd\xef"
512 "\xfe\xdc\xba\x98\x76\x54\x32\x10\xfe\xdc\xba\x98\x76\x54\x32\x10";
513
514/* Bind addresses for MHD_OPTION_SOCK_ADDR / MHD_OPTION_SOCK_ADDR_LEN.
515 Filled in by prepare_sock_addrs() because htons()/htonl() are not
516 constant expressions. */
517static struct sockaddr_in bind4;
518#ifdef AF_INET6
519static struct sockaddr_in6 bind6;
520#endif
522
523
524static void
526{
528 return;
530 memset (&bind4, 0, sizeof (bind4));
531 bind4.sin_family = AF_INET;
532 bind4.sin_port = htons (0); /* ephemeral */
533 bind4.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
534#ifdef AF_INET6
535 memset (&bind6, 0, sizeof (bind6));
536 bind6.sin6_family = AF_INET6;
537 bind6.sin6_port = htons (0);
538 bind6.sin6_addr = in6addr_loopback;
539#endif
540}
541
542
543/* ------------------------------------------------------------------ */
544/* The daemon callbacks */
545/* ------------------------------------------------------------------ */
546
554static void
555logger_cb (void *cls,
556 const char *fmt,
557 va_list ap)
558{
559 (void) cls;
560 if (! fuzz_verbose)
561 return;
562 (void) vfprintf (stderr, fmt, ap);
563}
564
565
573static void *
574uri_log_cb (void *cls,
575 const char *uri,
576 struct MHD_Connection *con)
577{
578 volatile size_t sink;
579
580 (void) cls;
581 (void) con;
582 sink = (NULL != uri) ? strlen (uri) : 0;
583 (void) sink;
584 return NULL;
585}
586
587
595static size_t
596unescape_cb (void *cls,
597 struct MHD_Connection *conn,
598 char *s)
599{
600 (void) cls;
601 (void) conn;
602 return strlen (s);
603}
604
605
606static void
607completed_cb (void *cls,
608 struct MHD_Connection *connection,
609 void **req_cls,
611{
612 (void) cls;
613 (void) toe;
614 /* MHD is done with this connection, so the deferred resume of suspend
615 mode 2 must not fire for it any more. */
616 if (! cfg.uses_threads)
617 {
618 unsigned int i;
619
620 for (i = 0; i < MAX_CONNECTIONS; i++)
621 if (pending_resume[i] == connection)
622 pending_resume[i] = NULL;
623 }
624 *req_cls = NULL;
625}
626
627
628static void
630 struct MHD_Connection *connection,
631 void **socket_context,
633{
634 (void) cls;
635 (void) connection;
637 *socket_context = NULL;
638}
639
640
641static void
642panic_cb (void *cls,
643 const char *file,
644 unsigned int line,
645 const char *reason)
646{
647 char msg[512];
648
649 (void) cls;
650 (void) snprintf (msg, sizeof (msg),
651 "MHD_PANIC() reached at %s:%u: %s",
652 (NULL != file) ? file : "?",
653 line,
654 (NULL != reason) ? reason : "?");
656}
657
658
659/* ------------------------------------------------------------------ */
660/* Suspend bookkeeping */
661/* ------------------------------------------------------------------ */
662
663static void
665{
666 unsigned int i;
667
668 for (i = 0; i < MAX_CONNECTIONS; i++)
669 {
670 if (NULL == pending_resume[i])
671 {
672 pending_resume[i] = c;
673 return;
674 }
675 }
676}
677
678
685static int
687{
688 unsigned int i;
689 int any = 0;
690
691 for (i = 0; i < MAX_CONNECTIONS; i++)
692 {
693 struct MHD_Connection *c = pending_resume[i];
694
695 if (NULL == c)
696 continue;
697 /* Clear first: MHD_resume_connection() can make MHD run the handler,
698 which may suspend the very same connection again. */
699 pending_resume[i] = NULL;
701 any = 1;
702 }
703 return any;
704}
705
706
714static void
715suspend_maybe (struct MHD_Connection *connection)
716{
717 if ( (0 == cfg.suspend_mode) ||
718 cfg.uses_threads ||
720 return;
721 MHD_suspend_connection (connection);
722 if (1 == cfg.suspend_mode)
723 {
724 MHD_resume_connection (connection);
725 return;
726 }
727 pending_resume_add (connection);
728}
729
730
731/* ------------------------------------------------------------------ */
732/* The access handler */
733/* ------------------------------------------------------------------ */
734
735static const char resp_body[] = "hello";
736
737static enum MHD_Result
738ahc (void *cls,
739 struct MHD_Connection *connection,
740 const char *url,
741 const char *method,
742 const char *version,
743 const char *upload_data,
744 size_t *upload_data_size,
745 void **req_cls)
746{
747 /* The address of this object is the "request already started" marker.
748 Nothing is allocated per request on purpose: this harness runs
749 millions of iterations in one process and the daemon shapes with
750 internal threads cannot guarantee that MHD_OPTION_NOTIFY_COMPLETED
751 is even set, so anything malloc()ed here could leak. */
752 static int req_marker;
753 struct MHD_Response *resp;
754 enum MHD_Result ret;
755 unsigned int code;
756 volatile size_t sink = 0;
757
758 (void) cls;
759 (void) upload_data;
760
761 if (&req_marker != *req_cls)
762 {
763 *req_cls = &req_marker;
764 return MHD_YES;
765 }
767
768 /* Touch the parsed request line the way a real application would. */
769 if (NULL != url)
770 sink += strlen (url);
771 if (NULL != method)
772 sink += strlen (method);
773 if (NULL != version)
774 sink += strlen (version);
775 (void) sink;
776
777 if (0 != *upload_data_size)
778 {
779 *upload_data_size = 0;
780 return MHD_YES;
781 }
782
783 if (cfg.conn_option)
784 (void) MHD_set_connection_option (connection,
786 (unsigned int) 30);
787 if (cfg.handler_no)
788 return MHD_NO; /* never suspend on this path: MHD_NO terminates the
789 connection and terminating a suspended one trips
790 mhd_assert (! connection->suspended) */
791
792 code = MHD_HTTP_OK;
793 switch (cfg.resp_kind)
794 {
795 case 1:
797 code = MHD_HTTP_NO_CONTENT;
798 break;
799 case 2:
800 resp = MHD_create_response_from_buffer (sizeof (resp_body) - 1,
801 (void *) (intptr_t) resp_body,
803 code = MHD_HTTP_FORBIDDEN;
804 break;
805 case 3:
808 break;
809 default:
811 resp_body);
812 break;
813 }
814 if (NULL == resp)
815 return MHD_NO;
816 ret = MHD_queue_response (connection, code, resp);
818 if (MHD_YES == ret)
819 suspend_maybe (connection);
820 return ret;
821}
822
823
824/* ------------------------------------------------------------------ */
825/* Driving the daemon */
826/* ------------------------------------------------------------------ */
827
833static void
834drain (int sock)
835{
836 char tmp[RESP_DRAIN_BUF];
837
838 if (0 > sock)
839 return;
840 while (0 < recv (sock, tmp, sizeof (tmp), MSG_DONTWAIT))
841 /* nothing */;
842}
843
844
853static void
855{
856 fd_set rs;
857 fd_set ws;
858 fd_set es;
860 struct timeval tv;
861
862 if (cfg.daemon_info)
863 {
865 volatile int64_t sink;
866
867 (void) MHD_get_timeout (d, &tl);
868 sink = MHD_get_timeout64s (d);
869 sink += (int64_t) MHD_get_timeout_i (d);
870 (void) sink;
871 }
872 switch (cfg.loop_mode)
873 {
874 case 1:
875 case 3:
876 FD_ZERO (&rs);
877 FD_ZERO (&ws);
878 FD_ZERO (&es);
879 /* MHD_get_fdset and MHD_run_from_select are also macros forwarding
880 to the *2 variants, so the names have to be parenthesised for the
881 v1 entry points to be reached at all. */
882 if (1 == cfg.loop_mode)
883 {
884 if (MHD_YES != MHD_get_fdset2 (d, &rs, &ws, &es, &max_fd,
885 (unsigned int) FD_SETSIZE))
886 {
887 (void) MHD_run (d);
888 return;
889 }
890 }
891 else
892 {
893 if (MHD_YES != (MHD_get_fdset) (d, &rs, &ws, &es, &max_fd))
894 {
895 (void) MHD_run (d);
896 return;
897 }
898 }
899 tv.tv_sec = 0;
900 tv.tv_usec = 0;
901 if (MHD_INVALID_SOCKET != max_fd)
902 (void) select ((int) max_fd + 1, &rs, &ws, &es, &tv);
903 if (1 == cfg.loop_mode)
904 (void) MHD_run_from_select2 (d, &rs, &ws, &es, (unsigned int) FD_SETSIZE);
905 else
906 (void) (MHD_run_from_select) (d, &rs, &ws, &es);
907 break;
908 case 2:
909 (void) MHD_run_wait (d, 0);
910 break;
911 default:
912 (void) MHD_run (d);
913 break;
914 }
915}
916
917
918static void
919pump (struct MHD_Daemon *d,
920 int sock,
921 unsigned int rounds)
922{
923 unsigned int i;
924
925 if (cfg.uses_threads)
926 return;
927 for (i = 0; i < rounds; i++)
928 {
929 /* Deferred resume of suspend mode 2. Each slot is cleared before
930 its connection is resumed, which stays correct even when the
931 resume makes MHD complete (and forget) the connection. */
932 (void) pending_resume_flush ();
933 run_once (d);
934 drain (sock);
935 }
936}
937
938
946static void
948{
949 unsigned int round;
950
951 if (0 > sock)
952 return;
953 for (round = 0; round < 4; round++)
954 {
955 struct pollfd p;
956 char tmp[RESP_DRAIN_BUF];
957 ssize_t n;
958
959 p.fd = sock;
960 p.events = POLLIN;
961 p.revents = 0;
962 if (0 >= poll (&p, 1, THREAD_WAIT_MS))
963 return;
964 if (0 == (p.revents & POLLIN))
965 return; /* POLLHUP/POLLERR only: peer is gone */
966 n = recv (sock, tmp, sizeof (tmp), MSG_DONTWAIT);
967 if (0 >= n)
968 return;
969 }
970}
971
972
973static void
975 int sock,
976 const uint8_t *data,
977 size_t len)
978{
979 size_t off = 0;
980 unsigned int stall = 0;
981
982 if (0 > sock)
983 return;
984 while ( (off < len) &&
985 (stall < 64) )
986 {
987 ssize_t s = send (sock, data + off, len - off, MSG_DONTWAIT);
988
989 if (0 < s)
990 {
991 off += (size_t) s;
992 stall = 0;
993 continue;
994 }
995 stall++;
996 if (cfg.uses_threads)
997 {
998 struct pollfd p;
999
1000 p.fd = sock;
1001 p.events = POLLOUT;
1002 p.revents = 0;
1003 if (0 >= poll (&p, 1, THREAD_WAIT_MS))
1004 break;
1005 }
1006 else
1007 {
1008 pump (d, sock, 2);
1009 }
1010 if ( (0 > s) &&
1011 (EAGAIN != errno) &&
1012 (EWOULDBLOCK != errno) &&
1013 (EINTR != errno) )
1014 break;
1015 }
1016}
1017
1018
1028static int
1030 int *sock)
1031{
1032 int sv[2];
1033 struct sockaddr_in sa;
1034
1035 *sock = -1;
1036 if (0 != socketpair (AF_UNIX, SOCK_STREAM, 0, sv))
1037 return -1;
1038 memset (&sa, 0, sizeof (sa));
1039 sa.sin_family = AF_INET;
1040 sa.sin_port = htons (44444);
1041 sa.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
1042 if (MHD_YES != MHD_add_connection (d,
1043 (MHD_socket) sv[1],
1044 (const struct sockaddr *) &sa,
1045 (socklen_t) sizeof (sa)))
1046 {
1047 /* MHD has already closed sv[1] in that case */
1048 (void) close (sv[0]);
1050 return -1;
1051 }
1053 *sock = sv[0];
1054 return 0;
1055}
1056
1057
1058static void
1060 int *sock)
1061{
1062 if (0 > *sock)
1063 return;
1064 (void) shutdown (*sock, SHUT_WR);
1065 if (cfg.uses_threads)
1066 wait_threaded (*sock);
1067 else
1068 pump (d, *sock, 4);
1069 (void) close (*sock);
1070 *sock = -1;
1071}
1072
1073
1074/* ------------------------------------------------------------------ */
1075/* Building the option array */
1076/* ------------------------------------------------------------------ */
1077
1078#define PICK(tbl, idx) ((tbl)[(idx) % (sizeof (tbl) / sizeof ((tbl)[0]))])
1079
1080static void
1082 unsigned int *nopt,
1083 enum MHD_OPTION option,
1084 intptr_t value,
1085 void *ptr_value)
1086{
1087 if (*nopt + 1 >= MAX_OPTS)
1088 return;
1089 opts[*nopt].option = option;
1090 opts[*nopt].value = value;
1091 opts[*nopt].ptr_value = ptr_value;
1092 (*nopt)++;
1093}
1094
1095
1106static unsigned int
1107build_options (const uint8_t *data,
1108 struct MHD_OptionItem *opts,
1109 unsigned int flags)
1110{
1111 unsigned int nopt = 0;
1112 const uint8_t mask_a = data[3];
1113 const uint8_t mask_b = data[4];
1114 const uint8_t mask_d = data[14];
1115 /* A thread pool needs an internal polling thread and is incompatible
1116 with thread-per-connection. */
1117 const int pool_ok =
1118 (0 != (flags & MHD_USE_INTERNAL_POLLING_THREAD)) &&
1119 (0 == (flags & MHD_USE_THREAD_PER_CONNECTION));
1120 /* Offer options in configurations where MHD rejects them. One input
1121 in eight: the rejection branches are worth reaching, but each one
1122 costs the whole rest of the iteration, because the daemon does not
1123 start at all. */
1124 const int misfit = (0xE0 == (data[15] & 0xE0));
1125
1126 if (0 != (mask_a & 0x01))
1128 (intptr_t) PICK (mem_limit_tbl, data[6] & 0x0F), NULL);
1129 if (0 != (mask_a & 0x02))
1131 (intptr_t) PICK (mem_increment_tbl, (data[6] >> 4) & 0x0F), NULL);
1132 if (0 != (mask_a & 0x04))
1134 (intptr_t) PICK (conn_limit_tbl, data[7] & 0x07), NULL);
1135 if (0 != (mask_a & 0x08))
1137 (intptr_t) PICK (per_ip_limit_tbl, (data[7] >> 4) & 0x07), NULL);
1138 if (0 != (mask_a & 0x10))
1140 (intptr_t) PICK (timeout_tbl, data[8] & 0x07), NULL);
1141 /* MHD_OPTION_THREAD_POOL_SIZE is rejected outright without an
1142 internal polling thread, and again when combined with
1143 MHD_USE_THREAD_PER_CONNECTION. Both branches are worth reaching,
1144 but a daemon that fails to start exercises nothing else, so the
1145 mismatched combinations are gated behind an extra bit instead of
1146 being half of all the inputs that name the option. */
1147 if ( (0 != (mask_a & 0x20)) &&
1148 (pool_ok || misfit) )
1150 (intptr_t) PICK (pool_size_tbl, (data[8] >> 3) & 0x03), NULL);
1151 if (0 != (mask_a & 0x40))
1153 (intptr_t) PICK (stack_size_tbl, (data[8] >> 5) & 0x03), NULL);
1154 if (0 != (mask_a & 0x80))
1156 (intptr_t) (unsigned int) (data[11] & 0x01), NULL);
1157
1158 if (0 != (mask_b & 0x01))
1160 (intptr_t) PICK (backlog_tbl, (data[11] >> 1) & 0x03), NULL);
1161 if (0 != (mask_b & 0x02))
1162 add_opt (opts, &nopt, MHD_OPTION_NONCE_NC_SIZE,
1163 (intptr_t) PICK (nonce_nc_tbl, data[9] & 0x03), NULL);
1164 if (0 != (mask_b & 0x04))
1165 add_opt (opts, &nopt, MHD_OPTION_SERVER_INSANITY,
1166 (intptr_t) (unsigned int) ((data[10] >> 6) & 0x03), NULL);
1167 if (0 != (mask_b & 0x08))
1169 (intptr_t) PICK (strict_tbl, (data[10] >> 4) & 0x03), NULL);
1170 if (0 != (mask_b & 0x10))
1172 (intptr_t) PICK (discipline_tbl, data[10] & 0x07), NULL);
1173 if (0 != (mask_b & 0x20))
1175 /* Truthful: LLVMFuzzerTestOneInput() ignores SIGPIPE
1176 process-wide before anything else happens. */
1177 (intptr_t) 1, NULL);
1178 if (0 != (mask_b & 0x40))
1179 add_opt (opts, &nopt, MHD_OPTION_APP_FD_SETSIZE,
1180 (intptr_t) PICK (fd_setsize_tbl, (data[11] >> 3) & 0x07), NULL);
1181 if (0 != (mask_b & 0x80))
1183 (intptr_t) (int) ((data[11] >> 5) & 0x03), NULL);
1184
1185 if (0 != (mask_d & 0x01))
1187 (intptr_t) (unsigned int) ((data[9] >> 2) & 0x0F), NULL);
1188 if (0 != (mask_d & 0x02))
1190 (intptr_t) (unsigned int) (data[15] & 0x7F), NULL);
1191 if (0 != (mask_d & 0x04))
1193 (intptr_t) (uint32_t) data[15], NULL);
1194 if (0 != (mask_d & 0x08))
1196 (intptr_t) PICK (fastopen_tbl, (data[11] >> 6) & 0x03), NULL);
1197 if (0 != (mask_d & 0x10))
1198 add_opt (opts, &nopt, MHD_OPTION_TLS_NO_ALPN,
1199 (intptr_t) (int) (data[15] & 0x01), NULL);
1200 if (0 != (mask_d & 0x20))
1201 /* MHD_INVALID_SOCKET means "use the socket MHD creates itself"; a
1202 real descriptor is deliberately not offered, because MHD takes
1203 ownership of it and the harness would have to track that. */
1204 add_opt (opts, &nopt, MHD_OPTION_LISTEN_SOCKET,
1205 (intptr_t) MHD_INVALID_SOCKET, NULL);
1206
1207 /* MHD_OPTION_DIGEST_AUTH_RANDOM keeps the caller's buffer, the _COPY
1208 variant makes MHD malloc() a copy that MHD_stop_daemon() has to free
1209 again -- a leak this harness would notice immediately. */
1210 if (0 != (data[5] & 0x20))
1211 add_opt (opts, &nopt,
1212 (0 != (data[5] & 0x40))
1215 (intptr_t) sizeof (digest_rnd),
1216 (void *) (intptr_t) digest_rnd);
1217
1218 /* A bind address is rejected together with MHD_USE_NO_LISTEN_SOCKET;
1219 same reasoning as for the thread pool above. */
1220 if ( (0 != (data[5] & 0x80)) &&
1221 (cfg.listen_sock || misfit) )
1222 {
1224 /* The address family has to match MHD_USE_IPv6, otherwise MHD
1225 rejects the daemon; that check is reached through the raw flag
1226 escape rather than by feeding a mismatched address on purpose. */
1227#ifdef AF_INET6
1228 if (0 != (flags & MHD_USE_IPv6))
1229 {
1230 if (0 != (data[15] & 0x02))
1231 add_opt (opts, &nopt, MHD_OPTION_SOCK_ADDR_LEN,
1232 (intptr_t) (socklen_t) sizeof (bind6), &bind6);
1233 else
1234 add_opt (opts, &nopt, MHD_OPTION_SOCK_ADDR, 0, &bind6);
1235 }
1236 else
1237#endif
1238 if (0 != (data[15] & 0x02))
1239 add_opt (opts, &nopt, MHD_OPTION_SOCK_ADDR_LEN,
1240 (intptr_t) (socklen_t) sizeof (bind4), &bind4);
1241 else
1242 add_opt (opts, &nopt, MHD_OPTION_SOCK_ADDR, 0, &bind4);
1243 }
1244
1245 /* An option number MHD does not know at all. parse_options_va()
1246 answers MHD_NO for it and MHD_start_daemon() returns NULL, which is
1247 the branch this reaches; keep it last so that everything above has
1248 already been parsed. */
1249 if (0 != (mask_d & 0x40))
1250 add_opt (opts, &nopt, (enum MHD_OPTION) (200 + (data[15] & 0x0F)),
1251 0, NULL);
1252
1253 opts[nopt].option = MHD_OPTION_END;
1254 opts[nopt].value = 0;
1255 opts[nopt].ptr_value = NULL;
1256 return nopt;
1257}
1258
1259
1260/* The three callback options whose "not set" state is representable as a
1261 NULL function pointer; MHD checks all three for NULL before calling
1262 them, so passing NULL is exactly equivalent to omitting the option. */
1263typedef void *(*fuzz_uri_log_cb)(void *, const char *,
1264 struct MHD_Connection *);
1265
1266#define VARARG_CALLBACKS \
1267 MHD_OPTION_NOTIFY_COMPLETED, cb_completed, NULL, \
1268 MHD_OPTION_NOTIFY_CONNECTION, cb_notify, NULL, \
1269 MHD_OPTION_URI_LOG_CALLBACK, cb_uri_log, NULL
1270
1271/* The accept policy callback, defined with the accept path further
1272 down. */
1273static enum MHD_Result
1274apc_cb (void *cls,
1275 const struct sockaddr *addr,
1276 socklen_t addrlen);
1277
1278
1290static struct MHD_Daemon *
1291start_daemon_variant (unsigned int flags,
1292 struct MHD_OptionItem *opts,
1293 unsigned int cbsel)
1294{
1295 MHD_AcceptPolicyCallback apc = cfg.accept_policy ? &apc_cb : NULL;
1296 MHD_RequestCompletedCallback cb_completed =
1297 (0 != (cbsel & 0x01)) ? &completed_cb : NULL;
1299 (0 != (cbsel & 0x02)) ? &notify_connection_cb : NULL;
1300 fuzz_uri_log_cb cb_uri_log =
1301 (0 != (cbsel & 0x04)) ? &uri_log_cb : NULL;
1302
1303 switch ((cbsel >> 3) & 0x03)
1304 {
1305 case 1:
1306 return MHD_start_daemon (flags, 0, apc, NULL, &ahc, NULL,
1307 MHD_OPTION_ARRAY, opts,
1311 case 2:
1312 return MHD_start_daemon (flags, 0, apc, NULL, &ahc, NULL,
1314 MHD_OPTION_ARRAY, opts,
1317 case 3:
1318 return MHD_start_daemon (flags, 0, apc, NULL, &ahc, NULL,
1320 MHD_OPTION_ARRAY, opts,
1324 default:
1325 return MHD_start_daemon (flags, 0, apc, NULL, &ahc, NULL,
1326 MHD_OPTION_ARRAY, opts,
1329 }
1330}
1331
1332
1343static enum MHD_Result
1344apc_cb (void *cls,
1345 const struct sockaddr *addr,
1346 socklen_t addrlen)
1347{
1348 volatile size_t sink;
1349
1350 (void) cls;
1351 sink = (size_t) addrlen + ((NULL != addr) ? (size_t) addr->sa_family : 0u);
1352 (void) sink;
1353 return cfg.accept_policy_deny ? MHD_NO : MHD_YES;
1354}
1355
1356
1371static int
1373 unsigned int flags)
1374{
1375 const union MHD_DaemonInfo *di;
1376 struct linger lg;
1377 int s;
1378 uint16_t port;
1379
1381 if ( (NULL == di) ||
1382 (0 == di->port) )
1383 return -1;
1384 port = (uint16_t) di->port;
1386#ifdef AF_INET6
1387 if (0 != (flags & MHD_USE_IPv6))
1388 {
1389 struct sockaddr_in6 to = bind6;
1390
1391 to.sin6_port = htons (port);
1392 s = socket (AF_INET6, SOCK_STREAM, 0);
1393 if (0 > s)
1394 return -1;
1395 if (0 != connect (s, (const struct sockaddr *) &to, sizeof (to)))
1396 {
1397 (void) close (s);
1398 return -1;
1399 }
1400 }
1401 else
1402#endif
1403 {
1404 struct sockaddr_in to = bind4;
1405
1406 to.sin_port = htons (port);
1407 s = socket (AF_INET, SOCK_STREAM, 0);
1408 if (0 > s)
1409 return -1;
1410 if (0 != connect (s, (const struct sockaddr *) &to, sizeof (to)))
1411 {
1412 (void) close (s);
1413 return -1;
1414 }
1415 }
1416 lg.l_onoff = 1;
1417 lg.l_linger = 0;
1418 (void) setsockopt (s, SOL_SOCKET, SO_LINGER, &lg, sizeof (lg));
1420 return s;
1421}
1422
1423
1430static void
1431query_feature (uint8_t sel)
1432{
1433 volatile int sink;
1434
1435 sink = (int) MHD_is_feature_supported ((enum MHD_FEATURE) (sel % 40u));
1436 (void) sink;
1437}
1438
1439
1444static void
1446{
1447 volatile unsigned int sink = 0;
1448 const union MHD_DaemonInfo *di;
1449
1450 sink += (unsigned int) (NULL != MHD_get_version ());
1451 sink += MHD_get_version_bin ();
1452
1454 if (NULL != di)
1455 sink += di->num_connections;
1457 if (NULL != di)
1458 sink += (unsigned int) di->flags;
1460 if (NULL != di)
1461 sink += di->port;
1463 if (NULL != di)
1464 sink += (unsigned int) (di->listen_fd + 1);
1466 if (NULL != di)
1467 sink += (unsigned int) (di->listen_fd + 1);
1468 (void) sink;
1469}
1470
1471
1472/* ------------------------------------------------------------------ */
1473/* The fuzz target */
1474/* ------------------------------------------------------------------ */
1475
1476int
1478 size_t size)
1479{
1480 struct MHD_Daemon *d;
1481 struct MHD_OptionItem opts[MAX_OPTS];
1482 unsigned int flags;
1483 int sock = -1;
1484 int rsock = -1;
1485 size_t pos;
1486 unsigned int nseg = 0;
1487 unsigned int nconn = 1;
1488 unsigned int cbsel;
1489 MHD_socket quiesced = MHD_INVALID_SOCKET;
1490
1491 /* Must happen before the first write() into the socketpair, and must
1492 not be left to the built-in driver: fuzz_install_handlers() is
1493 compiled out under -DFUZZ_NO_MAIN, which is exactly the build every
1494 external fuzzing engine uses. The call is idempotent. See
1495 fuzz_ignore_sigpipe() in fuzz_common.h for what happens without it. */
1497
1498 /* The sixteen configuration bytes are mandatory. An input of exactly
1499 that length is fine and starts a daemon with no traffic. */
1500 if (size < CFG_BYTES)
1501 return 0;
1502
1503 memset (&cfg, 0, sizeof (cfg));
1504 memset (pending_resume, 0, sizeof (pending_resume));
1505 tearing_down = 0;
1506
1507 flags = flags_from_input (data);
1508 if (cfg.listen_sock)
1509 flags &= ~((unsigned int) MHD_USE_NO_LISTEN_SOCKET);
1510 else
1511 flags |= MHD_USE_NO_LISTEN_SOCKET;
1512 if (cfg.err_log)
1513 flags |= MHD_USE_ERROR_LOG;
1514 else
1515 flags &= ~((unsigned int) MHD_USE_ERROR_LOG);
1516
1517 cfg.uses_threads = (0 != (flags & (MHD_USE_INTERNAL_POLLING_THREAD
1519 if (cfg.uses_threads &&
1520 (0 == (flags & MHD_USE_NO_THREAD_SAFETY)))
1521 {
1522 /* Without the inter-thread communication channel a worker only
1523 notices a connection handed to it by MHD_add_connection() when its
1524 own poll times out, so every threaded iteration would pay the full
1525 wait. MHD forces ITC on anyway whenever there is no listen
1526 socket; this extends that to the daemons that have one. */
1527 flags |= MHD_USE_ITC;
1528 }
1529
1530 cfg.loop_mode = (unsigned int) (data[12] & 0x03);
1531 cfg.quiesce = (0 != (data[12] & 0x04));
1532 if (cfg.quiesce &&
1533 cfg.uses_threads &&
1534 uses_epoll (flags) &&
1536 cfg.quiesce = 0;
1537 cfg.daemon_info = (0 != (data[12] & 0x08));
1538 cfg.suspend_mode = (0 != (flags & SUSPEND_BIT))
1539 ? (int) ((data[12] >> 4) & 0x03)
1540 : 0;
1541 if (3 == cfg.suspend_mode)
1542 cfg.suspend_mode = 1;
1543 cfg.nconn_max = 1u + (unsigned int) ((data[12] >> 6) & 0x03);
1544 if (cfg.nconn_max > MAX_CONNECTIONS)
1545 cfg.nconn_max = MAX_CONNECTIONS;
1546
1547 cfg.resp_kind = (unsigned int) (data[13] & 0x03);
1548 cfg.handler_no = (0 != (data[13] & 0x04));
1549 cfg.conn_option = (0 != (data[13] & 0x08));
1550 cfg.accept_policy = (0 != (data[13] & 0x10));
1551 cfg.accept_policy_deny = (0 != (data[13] & 0x20));
1552 /* One iteration in four of those that have a listening socket at all;
1553 a real connect()/accept() pair is much more expensive than
1554 MHD_add_connection() on a socketpair. */
1555 cfg.real_connect = cfg.listen_sock && (0xC0 == (data[13] & 0xC0));
1556
1557 (void) build_options (data, opts, flags);
1558
1559 cbsel = (unsigned int) data[5];
1560 /* MHD_USE_ERROR_LOG without MHD_OPTION_EXTERNAL_LOGGER sends every
1561 message MHD produces to stderr through MHD_default_logger_, which
1562 at fuzzing rates is tens of megabytes of noise. The flag is far too
1563 valuable to drop -- with it set, several hundred MHD_DLOG() call
1564 sites in daemon.c become live -- so the logger is forced on instead
1565 (bit 4 of the callback selector). */
1566 if (cfg.err_log)
1567 cbsel |= 0x10u;
1568
1570 d = start_daemon_variant (flags, opts, cbsel);
1571 if (NULL == d)
1572 {
1573 /* Entirely normal: an unsupported flag combination, an option MHD
1574 rejects, a bind() failure, ... */
1576 return 0;
1577 }
1578 stat_daemons++;
1579 if (cfg.uses_threads)
1580 stat_threaded++;
1581 if (! stats_registered)
1582 {
1583 stats_registered = 1;
1584 (void) atexit (&print_stats);
1585 }
1586
1587 if (cfg.daemon_info)
1589 query_feature (data[15]);
1590
1591 if (cfg.real_connect)
1592 {
1593 rsock = connect_real (d, flags);
1594 if (0 <= rsock)
1595 {
1596 static const char req[] = "GET / HTTP/1.1\r\nHost: x\r\n\r\n";
1597
1598 (void) send (rsock, req, sizeof (req) - 1, MSG_DONTWAIT);
1599 if (cfg.uses_threads)
1600 wait_threaded (rsock);
1601 else
1602 pump (d, rsock, PUMP_ROUNDS_LONG);
1603 }
1604 }
1605
1606 (void) new_connection (d, &sock);
1607
1608 pos = CFG_BYTES;
1609 while ( (pos + 2 <= size) &&
1610 (nseg < MAX_SEGMENTS) )
1611 {
1612 unsigned int hdr = (unsigned int) data[pos]
1613 | ((unsigned int) data[pos + 1] << 8);
1614 unsigned int op = hdr >> 14;
1615 size_t slen = (size_t) (hdr & 0x3FFF);
1616
1617 pos += 2;
1618 nseg++;
1619 if (slen > size - pos)
1620 slen = size - pos;
1621
1622 if ( (3 == op) &&
1623 (nconn < cfg.nconn_max) )
1624 {
1625 close_connection (d, &sock);
1626 (void) new_connection (d, &sock);
1627 nconn++;
1628 }
1629 if (0 != slen)
1630 send_all (d, sock, data + pos, slen);
1631 pos += slen;
1632 if (cfg.uses_threads)
1633 continue; /* the threaded shape drains at close */
1634 pump (d, sock, (0 == op) ? PUMP_ROUNDS : PUMP_ROUNDS_LONG);
1635 }
1636
1637 close_connection (d, &sock);
1638 pump (d, sock, 4);
1639 if (0 <= rsock)
1640 {
1641 (void) shutdown (rsock, SHUT_WR);
1642 if (cfg.uses_threads)
1643 wait_threaded (rsock);
1644 else
1645 pump (d, rsock, 4);
1646 (void) close (rsock);
1647 rsock = -1;
1648 }
1649
1650 /* A connection left suspended makes MHD_stop_daemon() MHD_PANIC(), and
1651 MHD_resume_connection() alone is not enough: it only raises a flag,
1652 and the connection is taken off the daemon's suspended list by
1653 MHD_run(). So flush and run until nothing is parked any more.
1654 tearing_down keeps the handler from parking anything new, which is
1655 what bounds this loop; the cap is only a backstop. */
1656 tearing_down = 1;
1657 if (! cfg.uses_threads)
1658 {
1659 unsigned int i;
1660
1661 for (i = 0; i < MAX_CONNECTIONS + 2u; i++)
1662 {
1663 if (! pending_resume_flush ())
1664 break;
1665 (void) MHD_run (d);
1666 }
1667 }
1668
1669 if (cfg.quiesce)
1670 {
1671 /* The returned socket belongs to the caller from here on, and with
1672 internal threads it must not be closed before MHD_stop_daemon()
1673 has joined them. */
1674 quiesced = MHD_quiesce_daemon (d);
1675 }
1676 MHD_stop_daemon (d);
1677 if (MHD_INVALID_SOCKET != quiesced)
1678 (void) close ((int) quiesced);
1679 return 0;
1680}
1681
1682
1683/* ------------------------------------------------------------------ */
1684/* Structure-aware generator */
1685/* ------------------------------------------------------------------ */
1686
1687/* Defined unconditionally, exactly like the rest of the harness API:
1688 only main() may live behind #ifndef FUZZ_NO_MAIN, and fuzz_common.h
1689 already declares these three with FUZZ_UNUSED so that the libFuzzer
1690 and AFL++ builds, which never call them, compile without a warning. */
1691
1692struct sbuf
1693{
1694 uint8_t *p;
1695 size_t len;
1696 size_t cap;
1697};
1698
1699
1700static void
1701sb_raw (struct sbuf *b,
1702 const void *s,
1703 size_t n)
1704{
1705 if (b->len + n > b->cap)
1706 n = b->cap - b->len;
1707 memcpy (b->p + b->len, s, n);
1708 b->len += n;
1709}
1710
1711
1712static void
1713sb_str (struct sbuf *b,
1714 const char *s)
1715{
1716 sb_raw (b, s, strlen (s));
1717}
1718
1719
1720static void
1721sb_u64 (struct sbuf *b,
1722 uint64_t v,
1723 int hex)
1724{
1725 char tmp[32];
1726
1727 (void) snprintf (tmp, sizeof (tmp),
1728 hex ? "%llx" : "%llu",
1729 (unsigned long long) v);
1730 sb_str (b, tmp);
1731}
1732
1733
1734static const char *const gen_methods[] = {
1735 "GET", "POST", "HEAD", "PUT", "OPTIONS", "DELETE", "TRACE", "CONNECT",
1736 "PATCH", "get", "BREW", ""
1737};
1738
1739static const char *const gen_targets[] = {
1740 "/", "/a", "/a/b/c", "*", "http://x/a", "/%41%42", "/a?b=c&d",
1741 "/very/long/path/that/does/not/fit/into/a/small/connection/memory/pool",
1742 "/a?novalue", "//", "/.%2e/", "/\x01"
1743};
1744
1745static const char *const gen_versions[] = {
1746 "HTTP/1.1", "HTTP/1.0", "HTTP/1.2", "HTTP/0.9", "HTTP/1", ""
1747};
1748
1749static const char *const gen_hdr_names[] = {
1750 "Host", "Connection", "Accept", "User-Agent", "Cookie", "Expect",
1751 "Content-Type", "X-Fuzz", "Upgrade", "Accept-Encoding", "Range",
1752 "If-Modified-Since"
1753};
1754
1755static const char *const gen_hdr_values[] = {
1756 "x", "keep-alive", "close", "*/*", "a=b; c=d", "100-continue",
1757 "text/plain", "1", "fuzz-protocol", "gzip", "bytes=0-1", "chunked"
1758};
1759
1760
1772static void
1774 struct sbuf *b,
1775 unsigned int shape)
1776{
1777 unsigned int nhdr;
1778 unsigned int i;
1779
1780 switch (shape % 10)
1781 {
1782 case 9:
1783 /* Not HTTP at all: MHD has to reject it, which is its own path. */
1784 for (i = 0; i < 24; i++)
1785 {
1786 uint8_t c = fuzz_byte (rng);
1787
1788 sb_raw (b, &c, 1);
1789 }
1790 return;
1791 case 6:
1792 /* Two pipelined requests on one connection. */
1793 sb_str (b, "GET /a HTTP/1.1\r\nHost: x\r\n\r\n");
1794 sb_str (b, "GET /b HTTP/1.1\r\nHost: x\r\nConnection: close\r\n\r\n");
1795 return;
1796 default:
1797 break;
1798 }
1799
1800 sb_str (b, gen_methods[fuzz_below (rng, (uint32_t) (sizeof (gen_methods)
1801 / sizeof (gen_methods[0]
1802 )))]);
1803 sb_str (b, " ");
1804 sb_str (b, gen_targets[fuzz_below (rng, (uint32_t) (sizeof (gen_targets)
1805 / sizeof (gen_targets[0]
1806 )))]);
1807 sb_str (b, " ");
1809 (uint32_t) (sizeof (gen_versions)
1810 / sizeof (gen_versions[0])))
1811 ]);
1812 sb_str (b, "\r\n");
1813
1814 nhdr = (5 == (shape % 10)) ? (8 + fuzz_below (rng, 24)) : fuzz_below (rng, 5);
1815 for (i = 0; i < nhdr; i++)
1816 {
1818 (uint32_t) (sizeof (gen_hdr_names)
1819 / sizeof (gen_hdr_names[0]
1820 )))]);
1821 sb_str (b, ": ");
1823 (uint32_t) (sizeof (gen_hdr_values)
1824 / sizeof (gen_hdr_values[
1825 0])))]);
1826 sb_str (b, "\r\n");
1827 }
1828
1829 switch (shape % 10)
1830 {
1831 case 2: /* Content-Length body */
1832 {
1833 uint32_t n = fuzz_below (rng, 64);
1834
1835 sb_str (b, "Content-Length: ");
1836 sb_u64 (b, n, 0);
1837 sb_str (b, "\r\n\r\n");
1838 for (i = 0; i < n; i++)
1839 sb_str (b, "A");
1840 break;
1841 }
1842 case 3: /* chunked body */
1843 {
1844 unsigned int nch = 1 + fuzz_below (rng, 3);
1845
1846 sb_str (b, "Transfer-Encoding: chunked\r\n\r\n");
1847 for (i = 0; i < nch; i++)
1848 {
1849 uint32_t n = 1 + fuzz_below (rng, 16);
1850 uint32_t k;
1851
1852 sb_u64 (b, n, 1);
1853 sb_str (b, "\r\n");
1854 for (k = 0; k < n; k++)
1855 sb_str (b, "B");
1856 sb_str (b, "\r\n");
1857 }
1858 sb_str (b, "0\r\n\r\n");
1859 break;
1860 }
1861 case 4: /* expect 100-continue */
1862 sb_str (b, "Expect: 100-continue\r\nContent-Length: 4\r\n\r\nabcd");
1863 break;
1864 case 7: /* upgrade request */
1865 sb_str (b, "Connection: Upgrade\r\nUpgrade: fuzz-protocol\r\n\r\n");
1866 break;
1867 case 8: /* truncated: MHD keeps waiting for more */
1868 sb_str (b, "X-Trunc: ");
1869 break;
1870 default:
1871 sb_str (b, "\r\n");
1872 break;
1873 }
1874}
1875
1876
1881static void
1883 struct sbuf *out,
1884 const uint8_t *body,
1885 size_t body_len,
1886 int new_conn_first)
1887{
1888 size_t off = 0;
1889 int first = 1;
1890
1891 while (off < body_len)
1892 {
1893 size_t chunk;
1894 unsigned int op;
1895 uint8_t hdr[2];
1896 unsigned int hv;
1897
1898 switch (fuzz_below (rng, 5))
1899 {
1900 case 0:
1901 chunk = 1;
1902 break;
1903 case 1:
1904 chunk = 2 + fuzz_below (rng, 8);
1905 break;
1906 default:
1907 chunk = body_len - off;
1908 break;
1909 }
1910 if (chunk > body_len - off)
1911 chunk = body_len - off;
1912 if (chunk > 0x3FFF)
1913 chunk = 0x3FFF;
1914 op = (first && new_conn_first) ? 3u : (fuzz_chance (rng, 5) ? 2u : 0u);
1915 hv = (op << 14) | (unsigned int) chunk;
1916 hdr[0] = (uint8_t) (hv & 0xFF);
1917 hdr[1] = (uint8_t) (hv >> 8);
1918 if (out->len + 2 + chunk > out->cap)
1919 return;
1920 sb_raw (out, hdr, 2);
1921 sb_raw (out, body + off, chunk);
1922 off += chunk;
1923 first = 0;
1924 }
1925}
1926
1927
1939static size_t
1941 uint8_t *buf,
1942 size_t cap)
1943{
1944 struct sbuf out;
1945 uint8_t cfg_bytes[CFG_BYTES];
1946 uint8_t req[GEN_BUF_SIZE];
1947 struct sbuf rb;
1948 unsigned int nreq;
1949 unsigned int i;
1950
1951 out.p = buf;
1952 out.len = 0;
1953 out.cap = cap;
1954
1955 for (i = 0; i < CFG_BYTES; i++)
1956 cfg_bytes[i] = fuzz_byte (rng);
1957
1958 /* Byte 0 selects the daemon shape; keep it inside the table so that
1959 the value is not folded by the modulo in an uneven way. */
1960 cfg_bytes[0] = (uint8_t) fuzz_below (rng, (uint32_t) MODE_COUNT);
1961 /* The raw flag escape is 1 in 4 of the byte-2 values, which is far too
1962 often: those daemons mostly fail to start and never reach the option
1963 handling. Clear the escape unless it is explicitly rolled. */
1964 if (! fuzz_chance (rng, 10))
1965 cfg_bytes[2] &= (uint8_t) ~0xC0u;
1966 else
1967 cfg_bytes[2] |= (uint8_t) 0xC0u;
1968 /* Likewise the deliberately invalid option number: useful, but every
1969 input carrying it ends in MHD_start_daemon() == NULL. */
1970 if (! fuzz_chance (rng, 20))
1971 cfg_bytes[14] &= (uint8_t) ~0x40u;
1972
1973 sb_raw (&out, cfg_bytes, CFG_BYTES);
1974
1975 nreq = 1u + (fuzz_chance (rng, 4) ? 1u : 0u);
1976 for (i = 0; i < nreq; i++)
1977 {
1978 rb.p = req;
1979 rb.len = 0;
1980 rb.cap = sizeof (req);
1981 gen_request (rng, &rb, fuzz_below (rng, 10));
1982 emit_segments (rng, &out, req, rb.len,
1983 (0 != i) || fuzz_chance (rng, 6));
1984 }
1985 return out.len;
1986}
1987
1988
1989/* ------------------------------------------------------------------ */
1990/* Built-in seed corpus */
1991/* ------------------------------------------------------------------ */
1992
2004struct seed_def
2005{
2006 const char *name;
2007 unsigned char cfg[CFG_BYTES];
2008 const char *req;
2009};
2010
2011/* Byte positions inside seed_def::cfg, for readability. */
2012#define C_MODE 0
2013#define C_FLAGA 1
2014#define C_FLAGB 2
2015#define C_OPTA 3
2016#define C_OPTB 4
2017#define C_OPTC 5
2018#define C_VAL_MEM 6
2019#define C_VAL_CONN 7
2020#define C_VAL_THR 8
2021#define C_VAL_DAUTH 9
2022#define C_VAL_DISC 10
2023#define C_VAL_SOCK 11
2024#define C_DRIVE 12
2025#define C_HANDLER 13
2026#define C_OPTD 14
2027#define C_SPARE 15
2028
2029#define REQ_PLAIN "GET /a HTTP/1.1\r\nHost: x\r\n\r\n"
2030#define REQ_CLOSE "GET /a HTTP/1.1\r\nHost: x\r\nConnection: close\r\n\r\n"
2031#define REQ_POST \
2032 "POST /a HTTP/1.1\r\nHost: x\r\nContent-Length: 4\r\n\r\nabcd"
2033#define REQ_CHUNKED \
2034 "POST /a HTTP/1.1\r\nHost: x\r\nTransfer-Encoding: chunked\r\n" \
2035 "\r\n3\r\nabc\r\n0\r\n\r\n"
2036
2037static const struct seed_def seeds[] = {
2038 /* ---- the event loops, all external ---- */
2039 { "plain-external-run",
2040 { 0 }, REQ_PLAIN },
2041 { "external-fdset2",
2042 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01 | 0x08, 0, 0, 0 },
2043 REQ_PLAIN },
2044 { "external-run-wait",
2045 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x02, 0, 0, 0 },
2046 REQ_PLAIN },
2047 { "external-fdset-v1",
2048 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x03, 0, 0, 0 },
2049 REQ_PLAIN },
2050 { "external-epoll",
2051 { 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
2052 REQ_PLAIN },
2053 { "external-auto",
2054 { 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
2055 REQ_PLAIN },
2056
2057 /* ---- the daemon shapes that run their own threads ---- */
2058 { "internal-thread-select",
2059 { 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
2060 REQ_CLOSE },
2061 { "internal-thread-poll",
2062 { 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
2063 REQ_CLOSE },
2064 { "internal-thread-epoll",
2065 { 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
2066 REQ_CLOSE },
2067 { "thread-per-connection",
2068 { 21, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
2069 REQ_CLOSE },
2070 { "thread-per-connection-poll",
2071 { 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
2072 REQ_CLOSE },
2073 /* A thread pool needs MHD_OPTION_THREAD_POOL_SIZE (option mask A bit
2074 5) on top of an internal polling thread, and is rejected outright
2075 with thread-per-connection. */
2076 { "thread-pool",
2077 { 18, 0, 0, 0x20, 0, 0, 0, 0, 0x00, 0, 0, 0, 0, 0, 0, 0 },
2078 REQ_CLOSE },
2079 { "thread-pool-epoll-stack",
2080 { 20, 0, 0, 0x20 | 0x40, 0, 0, 0, 0, 0x10 | 0x20, 0, 0, 0, 0, 0, 0, 0 },
2081 REQ_CLOSE },
2082
2083 /* ---- a real listening socket ---- */
2084 { "listen-socket",
2085 { 0, 0, 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
2086 REQ_PLAIN },
2087 { "listen-socket-reuse-backlog",
2088 { 0, 0, 0x01, 0x80, 0x01, 0, 0, 0, 0, 0, 0, 0x01 | 0x06, 0, 0, 0, 0 },
2089 REQ_PLAIN },
2090 { "listen-socket-sockaddr",
2091 { 0, 0, 0x01, 0, 0, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
2092 REQ_PLAIN },
2093 { "listen-socket-ipv6-dual",
2094 { 0, 0, 0x01 | 0x02 | 0x04, 0, 0, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
2095 REQ_PLAIN },
2096 { "listen-socket-quiesce",
2097 { 0, 0, 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x04 | 0x08, 0, 0, 0 },
2098 REQ_PLAIN },
2099 { "listen-socket-internal-thread",
2100 { 18, 0, 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x04, 0, 0, 0 },
2101 REQ_CLOSE },
2102
2103 /* ---- the accept() path: a real client on the listening socket ----
2104 Byte 13 bit 0x40|0x80 asks for the connect(), bit 0x10 installs the
2105 accept policy callback and bit 0x20 makes it say no. Without these
2106 MHD_accept_connection() and the listen-socket branches of the three
2107 event loops are never entered at all: MHD_add_connection() bypasses
2108 accept() completely. */
2109 { "real-connect",
2110 { 0, 0, 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xC0, 0, 0 },
2111 REQ_PLAIN },
2112 { "real-connect-accept-policy",
2113 { 0, 0, 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xC0 | 0x10, 0, 0 },
2114 REQ_PLAIN },
2115 { "real-connect-accept-denied",
2116 { 0, 0, 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xC0 | 0x10 | 0x20, 0, 0 },
2117 REQ_PLAIN },
2118 { "real-connect-internal-thread",
2119 { 18, 0, 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xC0, 0, 0 },
2120 REQ_CLOSE },
2121 { "real-connect-external-epoll",
2122 { 12, 0, 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xC0, 0, 0 },
2123 REQ_PLAIN },
2124
2125 /* ---- the memory options ---- */
2126 { "tiny-pool",
2127 { 0, 0, 0, 0x01 | 0x02, 0, 0, 0x01 | 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
2128 REQ_PLAIN },
2129 { "big-pool-big-increment",
2130 { 0, 0, 0, 0x01 | 0x02, 0, 0, 0x0C | 0x60, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
2131 REQ_POST },
2132
2133 /* ---- the connection limits ---- */
2134 { "connection-limit-one",
2135 { 0, 0, 0, 0x04, 0, 0, 0, 0x01, 0, 0, 0, 0, 0xC0, 0, 0, 0 },
2136 REQ_PLAIN },
2137 { "per-ip-limit-one",
2138 { 0, 0, 0, 0x08, 0, 0, 0, 0x10, 0, 0, 0, 0, 0xC0, 0, 0, 0 },
2139 REQ_PLAIN },
2140 { "connection-limit-zero",
2141 { 0, 0, 0, 0x04, 0, 0, 0, 0x00, 0, 0, 0, 0, 0, 0, 0, 0 },
2142 REQ_PLAIN },
2143 { "connection-timeout",
2144 { 0, 0, 0, 0x10, 0, 0, 0, 0, 0x01, 0, 0, 0, 0, 0, 0, 0 },
2145 REQ_PLAIN },
2146
2147 /* ---- parsing discipline and insanity ---- */
2148 { "discipline-lowest",
2149 { 0, 0, 0, 0, 0x10, 0, 0, 0, 0, 0, 0x00, 0, 0, 0, 0, 0 },
2150 "GET /a HTTP/1.1\r\n Host: x\r\n\r\n" },
2151 { "discipline-highest-pedantic",
2152 { 0, 0x01, 0, 0, 0x10, 0, 0, 0, 0, 0, 0x05, 0, 0, 0, 0, 0 },
2153 REQ_PLAIN },
2154 { "strict-for-client",
2155 { 0, 0, 0, 0, 0x08, 0, 0, 0, 0, 0, 0x00, 0, 0, 0, 0, 0 },
2156 REQ_PLAIN },
2157 { "server-insanity",
2158 { 0, 0, 0, 0, 0x04, 0, 0, 0, 0, 0, 0x40, 0, 0, 0, 0, 0 },
2159 REQ_PLAIN },
2160 { "bin-zero-in-uri-path",
2161 { 0, 0, 0, 0, 0x80, 0, 0, 0, 0, 0, 0, 0x20, 0, 0, 0, 0 },
2162 "GET /a%00b HTTP/1.1\r\nHost: x\r\n\r\n" },
2163 { "app-fd-setsize",
2164 { 0, 0, 0, 0, 0x40, 0, 0, 0, 0, 0, 0, 0x00, 0x01, 0, 0, 0 },
2165 REQ_PLAIN },
2166
2167 /* ---- the callbacks ---- */
2168 { "notify-completed",
2169 { 0, 0, 0, 0, 0, 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
2170 REQ_PLAIN },
2171 { "notify-connection",
2172 { 0, 0, 0, 0, 0, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
2173 REQ_PLAIN },
2174 { "uri-log-callback",
2175 { 0, 0, 0, 0, 0, 0x04, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
2176 "GET /a?q=%41 HTTP/1.1\r\nHost: x\r\n\r\n" },
2177 { "external-logger",
2178 { 0, 0x40, 0, 0, 0, 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
2179 REQ_PLAIN },
2180 { "unescape-callback",
2181 { 0, 0, 0, 0, 0, 0x08, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
2182 "GET /%41%42?a=%43 HTTP/1.1\r\nHost: x\r\n\r\n" },
2183 { "all-callbacks-error-log",
2184 { 0, 0x40, 0, 0, 0, 0x01 | 0x02 | 0x04 | 0x08 | 0x10,
2185 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
2186 REQ_PLAIN },
2187
2188 /* ---- digest-auth related options ---- */
2189 { "digest-random",
2190 { 0, 0, 0, 0, 0x02, 0x20, 0, 0, 0, 0x02, 0, 0, 0, 0, 0, 0 },
2191 REQ_PLAIN },
2192 { "digest-random-copy",
2193 { 0, 0, 0, 0, 0x02, 0x20 | 0x40, 0, 0, 0, 0x01, 0, 0, 0, 0, 0, 0 },
2194 REQ_PLAIN },
2195 { "digest-nonce-bind-and-defaults",
2196 { 0, 0, 0, 0, 0, 0x20, 0, 0, 0, 0x3C, 0, 0, 0, 0,
2197 0x01 | 0x02 | 0x04, 0x11 },
2198 REQ_PLAIN },
2199
2200 /* ---- suspend / resume ---- */
2201 { "suspend-immediate",
2202 { 0, 0x04, 0, 0, 0, 0x01, 0, 0, 0, 0, 0, 0, 0x10, 0, 0, 0 },
2203 REQ_PLAIN },
2204 { "suspend-deferred",
2205 { 0, 0x04, 0, 0, 0, 0x01, 0, 0, 0, 0, 0, 0, 0x20, 0, 0, 0 },
2206 REQ_PLAIN },
2207 { "suspend-deferred-two-connections",
2208 { 0, 0x04, 0, 0, 0, 0x01, 0, 0, 0, 0, 0, 0, 0x20 | 0x40, 0, 0, 0 },
2209 REQ_PLAIN },
2210
2211 /* ---- the remaining flag bits ---- */
2212 { "turbo-itc-suppress-date",
2213 { 0, 0x02 | 0x10 | 0x20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
2214 REQ_PLAIN },
2215 { "allow-upgrade",
2216 { 0, 0x08, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
2217 "GET / HTTP/1.1\r\nHost: x\r\nConnection: Upgrade\r\n"
2218 "Upgrade: fuzz-protocol\r\n\r\n" },
2219 { "no-thread-safety",
2220 { 0, 0, 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
2221 REQ_PLAIN },
2222 { "tcp-fastopen-listen",
2223 { 0, 0, 0x01 | 0x08, 0, 0, 0, 0, 0, 0, 0, 0, 0x40, 0, 0, 0x08, 0 },
2224 REQ_PLAIN },
2225 { "sigpipe-handled-by-app",
2226 { 0, 0, 0, 0, 0x20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
2227 REQ_PLAIN },
2228 { "listen-socket-option-invalid-fd",
2229 { 0, 0, 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x20, 0 },
2230 REQ_PLAIN },
2231
2232 /* ---- inputs whose daemon must not start ---- */
2233 /* Raw flag escape: MHD_USE_POLL together with MHD_USE_EPOLL. */
2234 { "raw-flags-poll-and-epoll",
2235 { 0, 0x40, 0xC0 | 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
2236 REQ_PLAIN },
2237 /* Raw flag escape: MHD_USE_EPOLL with MHD_USE_THREAD_PER_CONNECTION. */
2238 { "raw-flags-epoll-thread-per-conn",
2239 { 0, 0x04 | 0x08, 0xC0 | 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
2240 REQ_PLAIN },
2241 /* An option number MHD does not know. */
2242 { "invalid-option-number",
2243 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x40, 0 },
2244 REQ_PLAIN },
2245 /* MHD_OPTION_THREAD_POOL_SIZE without an internal polling thread. */
2246 { "thread-pool-without-threads",
2247 { 0, 0, 0, 0x20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
2248 REQ_PLAIN },
2249
2250 /* ---- the handler behaviours ---- */
2251 { "handler-returns-no",
2252 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x04, 0, 0 },
2253 REQ_PLAIN },
2254 { "empty-and-copied-responses",
2255 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01 | 0x08, 0, 0 },
2256 REQ_CHUNKED },
2257 { "error-response",
2258 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x03, 0, 0 },
2259 REQ_POST },
2260
2261 /* ---- everything at once ---- */
2262 { "kitchen-sink-external",
2263 { 0, 0x02 | 0x04 | 0x10 | 0x20 | 0x40, 0x01,
2264 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x80,
2265 0x02 | 0x10 | 0x20 | 0x40 | 0x80,
2266 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20,
2267 0x63, 0x45, 0x21, 0x13, 0x02, 0x0A,
2268 0x01 | 0x04 | 0x08 | 0x20, 0x08, 0x01 | 0x02 | 0x04 | 0x10, 0x33 },
2269 REQ_POST },
2270 { "kitchen-sink-thread-pool",
2271 { 18, 0x02 | 0x10 | 0x20 | 0x40, 0x01,
2272 0x01 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
2273 0x01 | 0x02 | 0x80,
2274 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20,
2275 0x03, 0x46, 0x39, 0x02, 0x00, 0x0A,
2276 0x04 | 0x08, 0x00, 0x01 | 0x08, 0x22 },
2277 REQ_CLOSE }
2278};
2279
2280static uint8_t seed_render_buf[1024];
2281
2282
2283static size_t
2285{
2286 return sizeof (seeds) / sizeof (seeds[0]);
2287}
2288
2289
2290static const uint8_t *
2291fuzz_seed_get (size_t idx,
2292 size_t *len)
2293{
2294 const struct seed_def *sd = &seeds[idx];
2295 struct sbuf b;
2296
2297 b.p = seed_render_buf;
2298 b.len = 0;
2299 b.cap = sizeof (seed_render_buf);
2300 sb_raw (&b, sd->cfg, CFG_BYTES);
2301 if (NULL != sd->req)
2302 {
2303 size_t n = strlen (sd->req);
2304 unsigned int hv;
2305 uint8_t hdr[2];
2306
2307 if (n > 0x3FFF)
2308 n = 0x3FFF;
2309 hv = (0u << 14) | (unsigned int) n;
2310 hdr[0] = (uint8_t) (hv & 0xFF);
2311 hdr[1] = (uint8_t) (hv >> 8);
2312 sb_raw (&b, hdr, 2);
2313 sb_raw (&b, sd->req, n);
2314 }
2315 *len = b.len;
2316 return seed_render_buf;
2317}
Shared, header-only fuzzing driver for the MHD in-process fuzzers.
static FUZZ_UNUSED void fuzz_ignore_sigpipe(void)
static FUZZ_UNUSED int fuzz_verbose
static FUZZ_UNUSED uint32_t fuzz_below(struct fuzz_rng *r, uint32_t n)
static FUZZ_UNUSED uint8_t fuzz_byte(struct fuzz_rng *r)
static FUZZ_UNUSED void fuzz_report_finding(const char *what)
static FUZZ_UNUSED int fuzz_chance(struct fuzz_rng *r, uint32_t n)
static void gen_request(struct fuzz_rng *rng, struct sbuf *b, unsigned int shape)
#define PICK(tbl, idx)
static void pending_resume_add(struct MHD_Connection *c)
static void prepare_sock_addrs(void)
#define MAX_CONNECTIONS
#define REQ_CHUNKED
static void emit_segments(struct fuzz_rng *rng, struct sbuf *out, const uint8_t *body, size_t body_len, int new_conn_first)
static void query_daemon_info(struct MHD_Daemon *d)
static unsigned long stat_conns_refused
static void suspend_maybe(struct MHD_Connection *connection)
static size_t fuzz_seed_count(void)
static const char *const gen_hdr_values[]
static void pump(struct MHD_Daemon *d, int sock, unsigned int rounds)
static void run_once(struct MHD_Daemon *d)
static void send_all(struct MHD_Daemon *d, int sock, const uint8_t *data, size_t len)
static void * uri_log_cb(void *cls, const char *uri, struct MHD_Connection *con)
#define SUSPEND_BIT
static const size_t mem_increment_tbl[]
static const struct seed_def seeds[]
static unsigned long stat_conns_added
static int stats_registered
static const char *const gen_targets[]
static unsigned long stat_daemons
static unsigned int build_options(const uint8_t *data, struct MHD_OptionItem *opts, unsigned int flags)
#define PUMP_ROUNDS
static const unsigned int timeout_tbl[]
static void sb_raw(struct sbuf *b, const void *s, size_t n)
#define PUMP_ROUNDS_LONG
static int tearing_down
static const unsigned int backlog_tbl[]
static int connect_real(struct MHD_Daemon *d, unsigned int flags)
static int new_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 enum MHD_Result apc_cb(void *cls, const struct sockaddr *addr, socklen_t addrlen)
#define MODE_COUNT
static void sb_str(struct sbuf *b, const char *s)
static const int fd_setsize_tbl[]
#define MAX_SEGMENTS
static void sb_u64(struct sbuf *b, uint64_t v, int hex)
static const unsigned int per_ip_limit_tbl[]
#define FUZZ_HARNESS_NAME
static void notify_connection_cb(void *cls, struct MHD_Connection *connection, void **socket_context, enum MHD_ConnectionNotificationCode toe)
#define GEN_BUF_SIZE
static const uint8_t * fuzz_seed_get(size_t idx, size_t *len)
static void query_feature(uint8_t sel)
void *(* fuzz_uri_log_cb)(void *, const char *, struct MHD_Connection *)
static const char *const gen_methods[]
static const unsigned int fastopen_tbl[]
static uint8_t seed_render_buf[1024]
static const char *const gen_hdr_names[]
static void logger_cb(void *cls, const char *fmt, va_list ap)
static const size_t stack_size_tbl[]
#define VARARG_CALLBACKS
#define REQ_POST
static unsigned long stat_daemons_failed
static unsigned long stat_real_conns
#define CFG_BYTES
static struct MHD_Connection * pending_resume[MAX_CONNECTIONS]
static const unsigned int mode_tbl[]
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
static int bind_addrs_ready
static const unsigned int nonce_nc_tbl[]
static void drain(int sock)
static struct sockaddr_in bind4
static size_t fuzz_generate(struct fuzz_rng *rng, uint8_t *buf, size_t cap)
static void print_stats(void)
static const unsigned int pool_size_tbl[]
static struct MHD_Daemon * start_daemon_variant(unsigned int flags, struct MHD_OptionItem *opts, unsigned int cbsel)
static void panic_cb(void *cls, const char *file, unsigned int line, const char *reason)
static struct fuzz_cfg cfg
static const unsigned int conn_limit_tbl[]
#define MAX_OPTS
#define REQ_CLOSE
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 unsigned long stat_threaded
static unsigned long stat_handler_calls
static int uses_epoll(unsigned int flags)
static unsigned int flags_from_input(const uint8_t *data)
static void close_connection(struct MHD_Daemon *d, int *sock)
#define RAW_FLAG_MASK
static const char digest_rnd[32]
static int pending_resume_flush(void)
#define RESP_DRAIN_BUF
static const int strict_tbl[]
static void completed_cb(void *cls, struct MHD_Connection *connection, void **req_cls, enum MHD_RequestTerminationCode toe)
static const char resp_body[]
static const int discipline_tbl[]
static const size_t mem_limit_tbl[]
static const char *const gen_versions[]
#define REQ_PLAIN
static int quiesce_epoll_race_allowed(void)
#define THREAD_WAIT_MS
static void wait_threaded(int sock)
static size_t unescape_cb(void *cls, struct MHD_Connection *conn, char *s)
_MHD_EXTERN int MHD_get_timeout_i(struct MHD_Daemon *daemon)
_MHD_EXTERN int64_t MHD_get_timeout64s(struct MHD_Daemon *daemon)
#define MHD_run_from_select(d, r, w, e)
_MHD_EXTERN void MHD_stop_daemon(struct MHD_Daemon *daemon)
Definition daemon.c:9415
_MHD_EXTERN enum MHD_Result MHD_run(struct MHD_Daemon *daemon)
Definition daemon.c:5938
_MHD_EXTERN enum MHD_Result MHD_get_fdset2(struct MHD_Daemon *daemon, fd_set *read_fd_set, fd_set *write_fd_set, fd_set *except_fd_set, MHD_socket *max_fd, unsigned int fd_setsize)
Definition daemon.c:1227
#define MHD_get_fdset(daemon, read_fd_set, write_fd_set, except_fd_set, max_fd)
_MHD_EXTERN enum MHD_Result MHD_get_timeout(struct MHD_Daemon *daemon, MHD_UNSIGNED_LONG_LONG *timeout)
Definition daemon.c:4252
_MHD_EXTERN enum MHD_Result MHD_run_from_select2(struct MHD_Daemon *daemon, const fd_set *read_fd_set, const fd_set *write_fd_set, const fd_set *except_fd_set, unsigned int fd_setsize)
Definition daemon.c:4746
_MHD_EXTERN struct MHD_Daemon * MHD_start_daemon(unsigned int flags, uint16_t port, MHD_AcceptPolicyCallback apc, void *apc_cls, MHD_AccessHandlerCallback dh, void *dh_cls,...)
Definition daemon.c:6217
_MHD_EXTERN enum MHD_Result MHD_run_wait(struct MHD_Daemon *daemon, int32_t millisec)
Definition daemon.c:5988
#define MHD_HTTP_INTERNAL_SERVER_ERROR
Definition microhttpd.h:453
#define MHD_HTTP_OK
Definition microhttpd.h:350
#define MHD_HTTP_NO_CONTENT
Definition microhttpd.h:358
#define MHD_HTTP_FORBIDDEN
Definition microhttpd.h:397
_MHD_EXTERN void MHD_set_panic_func(MHD_PanicCallback cb, void *cls)
Definition mhd_panic.c:94
MHD_ConnectionNotificationCode
void(* MHD_RequestCompletedCallback)(void *cls, struct MHD_Connection *connection, void **req_cls, enum MHD_RequestTerminationCode toe)
void(* MHD_NotifyConnectionCallback)(void *cls, struct MHD_Connection *connection, void **socket_context, enum MHD_ConnectionNotificationCode toe)
MHD_RequestTerminationCode
@ MHD_CONNECTION_NOTIFY_STARTED
_MHD_EXTERN struct MHD_Response * MHD_create_response_from_buffer(size_t size, void *buffer, enum MHD_ResponseMemoryMode mode)
Definition response.c:1470
_MHD_EXTERN enum MHD_Result MHD_queue_response(struct MHD_Connection *connection, unsigned int status_code, struct MHD_Response *response)
_MHD_EXTERN void MHD_destroy_response(struct MHD_Response *response)
Definition response.c:2294
_MHD_EXTERN struct MHD_Response * MHD_create_response_empty(enum MHD_ResponseFlags flags)
Definition response.c:1831
_MHD_EXTERN struct MHD_Response * MHD_create_response_from_buffer_static(size_t size, const void *buffer)
Definition response.c:1512
@ MHD_RESPMEM_MUST_COPY
_MHD_EXTERN enum MHD_Result MHD_add_connection(struct MHD_Daemon *daemon, MHD_socket client_socket, const struct sockaddr *addr, socklen_t addrlen)
Definition daemon.c:3682
_MHD_EXTERN enum MHD_Result MHD_is_feature_supported(enum MHD_FEATURE feature)
Definition daemon.c:9722
_MHD_EXTERN enum MHD_Result MHD_set_connection_option(struct MHD_Connection *connection, enum MHD_CONNECTION_OPTION option,...)
_MHD_EXTERN MHD_socket MHD_quiesce_daemon(struct MHD_Daemon *daemon)
Definition daemon.c:6262
_MHD_EXTERN const union MHD_DaemonInfo * MHD_get_daemon_info(struct MHD_Daemon *daemon, enum MHD_DaemonInfoType info_type,...)
Definition daemon.c:9605
_MHD_EXTERN uint32_t MHD_get_version_bin(void)
Definition daemon.c:9704
_MHD_EXTERN const char * MHD_get_version(void)
Definition daemon.c:9673
#define UINT_MAX
Definition mhd_limits.h:53
#define NULL
public interface to libmicrohttpd
int MHD_socket
Definition microhttpd.h:206
MHD_FEATURE
@ MHD_FEATURE_UPGRADE
MHD_OPTION
MHD options.
@ MHD_OPTION_CONNECTION_MEMORY_INCREMENT
@ MHD_OPTION_DIGEST_AUTH_DEFAULT_NONCE_TIMEOUT
@ MHD_OPTION_CLIENT_DISCIPLINE_LVL
@ MHD_OPTION_SOCK_ADDR_LEN
@ MHD_OPTION_APP_FD_SETSIZE
@ MHD_OPTION_SIGPIPE_HANDLED_BY_APP
@ MHD_OPTION_UNESCAPE_CALLBACK
@ MHD_OPTION_EXTERNAL_LOGGER
@ MHD_OPTION_TLS_NO_ALPN
@ MHD_OPTION_LISTEN_BACKLOG_SIZE
@ MHD_OPTION_LISTENING_ADDRESS_REUSE
@ MHD_OPTION_THREAD_POOL_SIZE
@ MHD_OPTION_CONNECTION_LIMIT
@ MHD_OPTION_PER_IP_CONNECTION_LIMIT
@ MHD_OPTION_DIGEST_AUTH_DEFAULT_MAX_NC
@ MHD_OPTION_TCP_FASTOPEN_QUEUE_SIZE
@ MHD_OPTION_SERVER_INSANITY
@ MHD_OPTION_LISTEN_SOCKET
@ MHD_OPTION_DIGEST_AUTH_RANDOM
@ MHD_OPTION_NONCE_NC_SIZE
@ MHD_OPTION_ALLOW_BIN_ZERO_IN_URI_PATH
@ MHD_OPTION_CONNECTION_MEMORY_LIMIT
@ MHD_OPTION_THREAD_STACK_SIZE
@ MHD_OPTION_DIGEST_AUTH_RANDOM_COPY
@ MHD_OPTION_ARRAY
@ MHD_OPTION_STRICT_FOR_CLIENT
@ MHD_OPTION_DIGEST_AUTH_NONCE_BIND_TYPE
@ MHD_OPTION_SOCK_ADDR
@ MHD_OPTION_CONNECTION_TIMEOUT
@ MHD_OPTION_END
MHD_Result
Definition microhttpd.h:163
@ MHD_YES
Definition microhttpd.h:172
@ MHD_NO
Definition microhttpd.h:167
enum MHD_Result(* MHD_AcceptPolicyCallback)(void *cls, const struct sockaddr *addr, socklen_t addrlen)
#define MHD_UNSIGNED_LONG_LONG
Definition microhttpd.h:311
_MHD_EXTERN void MHD_resume_connection(struct MHD_Connection *connection)
Definition daemon.c:3445
void * data
#define MHD_INVALID_SOCKET
Definition microhttpd.h:207
@ MHD_DAEMON_INFO_BIND_PORT
@ MHD_DAEMON_INFO_EPOLL_FD
@ MHD_DAEMON_INFO_FLAGS
@ MHD_DAEMON_INFO_CURRENT_CONNECTIONS
@ MHD_DAEMON_INFO_LISTEN_FD
@ MHD_USE_EPOLL
@ MHD_ALLOW_SUSPEND_RESUME
@ MHD_USE_TCP_FASTOPEN
@ MHD_USE_THREAD_PER_CONNECTION
@ MHD_USE_AUTO
@ MHD_USE_POST_HANDSHAKE_AUTH_SUPPORT
@ MHD_USE_TURBO
@ MHD_USE_IPv6
@ MHD_USE_SUPPRESS_DATE_NO_CLOCK
@ MHD_USE_DUAL_STACK
@ MHD_USE_POLL
@ MHD_USE_INSECURE_TLS_EARLY_DATA
@ MHD_ALLOW_UPGRADE
@ MHD_USE_ERROR_LOG
@ MHD_USE_NO_LISTEN_SOCKET
@ MHD_USE_PEDANTIC_CHECKS
@ MHD_USE_INTERNAL_POLLING_THREAD
@ MHD_USE_ITC
@ MHD_USE_NO_THREAD_SAFETY
@ MHD_RF_NONE
_MHD_EXTERN void MHD_suspend_connection(struct MHD_Connection *connection)
Definition daemon.c:3402
@ MHD_CONNECTION_OPTION_TIMEOUT
struct sockaddr_storage * addr
Definition internal.h:1421
MHD_AcceptPolicyCallback apc
Definition internal.h:2027
enum MHD_OPTION option
enum MHD_FLAG flags
unsigned int num_connections
MHD_socket listen_fd