GNU libmicrohttpd 1.0.9
Loading...
Searching...
No Matches
fuzz_eventloop.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
172#define FUZZ_HARNESS_NAME "fuzz_eventloop"
173#include "fuzz_common.h"
174
175#include <microhttpd.h>
176#include <sys/socket.h>
177#include <netinet/in.h>
178#include <sys/select.h>
179#include <limits.h>
180
182#define MAX_CONNS 8
183
185#define MAX_OPS 400
186
188#define BIG_BODY_LEN 131072
189
191#define DRAIN_BUF 4096
192
193
194/* ------------------------------------------------------------------ */
195/* Per-iteration configuration */
196/* ------------------------------------------------------------------ */
197
198struct el_cfg
199{
200 unsigned int timeout_s;
201 int listen_sock;
202 int app_fd_setsize;
203 size_t mem_limit;
204 int conn_limit;
205 int small_sockbuf;
207 unsigned int resp_kind;
208 int error_reply;
209 int susp_park;
210 int susp_now;
211 int hnd_connopt;
212 int hnd_info;
213 int auto_drain;
215 unsigned int fdset_var;
216 unsigned int run_var;
217 int honour_timeout;
218 int timeout_every_run;
219 int quiesce_end;
221 unsigned int nconn_up_front;
222 int check_always;
223 int allow_real_wait;
224 int stop_with_queued;
225 uint8_t clock_seed;
226};
227
228static struct el_cfg cfg;
229
231static const size_t mem_limit_tbl[] = { 0, 256, 1024, 4096 };
232
234static const unsigned int timeout_tbl[] = { 1, 2, 0, 1 };
235
245static const unsigned int setsize_tbl[] = {
246 (unsigned int) FD_SETSIZE,
247 (unsigned int) FD_SETSIZE,
248 (unsigned int) FD_SETSIZE / 2,
249 64, 16, 4, 1, (unsigned int) FD_SETSIZE
250};
251
252
253/* ------------------------------------------------------------------ */
254/* Per-iteration state */
255/* ------------------------------------------------------------------ */
256
265static int csock[MAX_CONNS];
266static unsigned int nconns;
267static unsigned int cur_conn;
268
278struct live_conn
279{
280 struct MHD_Connection *mc;
281 int susp;
282};
283
285#define MAX_LIVE (MAX_CONNS + 4)
286
287static struct live_conn lives[MAX_LIVE];
288
290static struct MHD_Daemon *cur_daemon;
291
297static int tearing_down;
298
301
303static uint64_t clk_ms;
304static uint64_t deadline_ms;
305static int deadline_valid;
306
308static fd_set g_rs;
309static fd_set g_ws;
310static fd_set g_es;
312static unsigned int g_setsize = (unsigned int) FD_SETSIZE;
313static int g_have_sets;
314
317static int big_body_ready;
318
327static int expiry_budget = 2;
329
331static unsigned long stat_daemons;
332static unsigned long stat_handler_calls;
333static unsigned long stat_fdset_v1;
334static unsigned long stat_fdset_v2;
335static unsigned long stat_rfs_v1;
336static unsigned long stat_rfs_v2;
337static unsigned long stat_run;
338static unsigned long stat_run_wait;
339static unsigned long stat_timeouts;
340static unsigned long stat_quiesce;
341static unsigned long stat_suspend;
342static unsigned long stat_resume;
343static unsigned long stat_expiry_waits;
344static unsigned long stat_queued_at_stop;
346
347
348static void
350{
351 if (! fuzz_verbose)
352 return;
353 fprintf (stderr,
354 "%s: daemons=%lu handler=%lu get_fdset=%lu get_fdset2=%lu "
355 "run_from_select=%lu run_from_select2=%lu run=%lu run_wait=%lu "
356 "timeout queries=%lu quiesce=%lu suspend=%lu resume=%lu "
357 "expiry waits=%lu queued at stop=%lu\n",
363}
364
365
366/* ------------------------------------------------------------------ */
367/* Callbacks */
368/* ------------------------------------------------------------------ */
369
370static void
371panic_cb (void *cls,
372 const char *file,
373 unsigned int line,
374 const char *reason)
375{
376 char msg[512];
377
378 (void) cls;
379 (void) snprintf (msg, sizeof (msg),
380 "MHD_PANIC() reached from an event-loop schedule "
381 "at %s:%u: %s",
382 (NULL != file) ? file : "?",
383 line,
384 (NULL != reason) ? reason : "?");
386}
387
388
392static int
393slot_of (const struct MHD_Connection *c)
394{
395 unsigned int i;
396
397 for (i = 0; i < MAX_LIVE; i++)
398 if ( (NULL != lives[i].mc) &&
399 (lives[i].mc == c) )
400 return (int) i;
401 return -1;
402}
403
404
405static void
406notify_conn_cb (void *cls,
407 struct MHD_Connection *connection,
408 void **socket_context,
410{
411 unsigned int i;
412 int idx;
413
414 (void) cls;
415 (void) socket_context;
417 {
418 for (i = 0; i < MAX_LIVE; i++)
419 {
420 if (NULL != lives[i].mc)
421 continue;
422 lives[i].mc = connection;
423 lives[i].susp = 0;
424 return;
425 }
426 return;
427 }
428 /* MHD_CONNECTION_NOTIFY_CLOSED: the object is about to be freed. */
429 idx = slot_of (connection);
430 if (0 <= idx)
431 {
432 lives[idx].mc = NULL;
433 lives[idx].susp = 0;
434 }
435}
436
437
438/* ------------------------------------------------------------------ */
439/* Suspend / resume bookkeeping */
440/* ------------------------------------------------------------------ */
441
445static int
446mhd_thinks_suspended (unsigned int i)
447{
448 const union MHD_ConnectionInfo *ci;
449
450 if (NULL == lives[i].mc)
451 return 0;
452 ci = MHD_get_connection_info (lives[i].mc,
454 if (NULL == ci)
455 return 0;
456 return (MHD_YES == ci->suspended);
457}
458
459
470static void
471do_suspend (unsigned int i)
472{
473 int mhd_susp;
474
475 if ( (i >= MAX_LIVE) ||
476 (NULL == lives[i].mc) )
477 return;
478 mhd_susp = mhd_thinks_suspended (i);
479 if (mhd_susp && lives[i].susp)
480 return; /* really suspended already */
482 lives[i].susp = 1;
483 stat_suspend++;
484}
485
486
487static void
488do_resume (unsigned int i)
489{
490 if ( (i >= MAX_LIVE) ||
491 (NULL == lives[i].mc) ||
492 (! lives[i].susp) )
493 return;
494 /* Clear first: MHD_resume_connection() may end up running the handler
495 later, which is allowed to suspend the very same connection again. */
496 lives[i].susp = 0;
498 stat_resume++;
499}
500
501
508static int
510{
511 unsigned int i;
512 int any = 0;
513
514 for (i = 0; i < MAX_LIVE; i++)
515 {
516 if ( (NULL == lives[i].mc) ||
517 (! lives[i].susp) )
518 continue;
519 lives[i].susp = 0;
521 stat_resume++;
522 any = 1;
523 }
524 return any;
525}
526
527
544static int
545scan_live_timeouts (uint64_t *max_ms)
546{
547 unsigned int i;
548 int any = 0;
549
550 *max_ms = 0;
551 for (i = 0; i < MAX_LIVE; i++)
552 {
553 const union MHD_ConnectionInfo *ci;
554 uint64_t ms;
555
556 if (NULL == lives[i].mc)
557 continue;
558 ci = MHD_get_connection_info (lives[i].mc,
560 if ( (NULL != ci) &&
561 (MHD_YES == ci->suspended) )
562 continue; /* not in any timeout list */
563 ci = MHD_get_connection_info (lives[i].mc,
565 if (NULL == ci)
566 continue;
567 ms = ((uint64_t) ci->connection_timeout) * 1000;
568 if (0 == ms)
569 continue; /* in a list, but exempt from timeouts */
570 any = 1;
571 if (ms > *max_ms)
572 *max_ms = ms;
573 }
574 return any;
575}
576
577
578/* ------------------------------------------------------------------ */
579/* The access handler */
580/* ------------------------------------------------------------------ */
581
582struct crc_state
583{
584 uint64_t total;
585 unsigned int pattern;
586};
587
588
589static ssize_t
590crc_cb (void *cls,
591 uint64_t pos,
592 char *buf,
593 size_t max)
594{
595 struct crc_state *st = (struct crc_state *) cls;
596 size_t n;
597 size_t i;
598
599 if (pos >= st->total)
601 n = (size_t) (st->total - pos);
602 if (n > max)
603 n = max;
604 if (0 == n)
606 for (i = 0; i < n; i++)
607 buf[i] = (char) ('a' + (int) ((pos + i + st->pattern) % 26));
608 return (ssize_t) n;
609}
610
611
612static void
613crc_free (void *cls)
614{
615 free (cls);
616}
617
618
619static struct MHD_Response *
621{
622 struct crc_state *st;
623 struct MHD_Response *r;
624
625 switch (cfg.resp_kind)
626 {
627 case 1:
628 st = (struct crc_state *) calloc (1, sizeof (struct crc_state));
629 if (NULL == st)
630 return NULL;
631 st->total = 700;
632 st->pattern = cfg.clock_seed;
634 128,
635 &crc_cb,
636 st,
637 &crc_free);
638 if (NULL == r)
639 free (st);
640 return r;
641 case 2:
642 /* Large enough not to fit into the socket buffers, so that the
643 connection stays blocked on write and MHD has to schedule it
644 through the write descriptor set over several rounds. */
646 case 3:
648 default:
650 }
651}
652
653
654static enum MHD_Result
655ahc (void *cls,
656 struct MHD_Connection *connection,
657 const char *url,
658 const char *method,
659 const char *version,
660 const char *upload_data,
661 size_t *upload_data_size,
662 void **req_cls)
663{
664 struct MHD_Response *resp;
665 enum MHD_Result ret;
666 int idx;
667
668 (void) cls;
669 (void) url;
670 (void) method;
671 (void) version;
672 (void) upload_data;
673 if (NULL == *req_cls)
674 {
675 *req_cls = (void *) (intptr_t) 1;
676 return MHD_YES;
677 }
679 if (0 != *upload_data_size)
680 {
681 *upload_data_size = 0;
682 return MHD_YES;
683 }
684
685 idx = slot_of (connection);
686 if (cfg.hnd_connopt)
687 {
688 /* Moves the connection between the "normal" and the "manual" timeout
689 list, i.e. changes which connection MHD_get_timeout*() reports. */
690 unsigned int nt = (0 == cfg.timeout_s) ? 1u : (cfg.timeout_s + 1u);
691
692 (void) MHD_set_connection_option (connection,
694 nt);
695 }
696 if (cfg.hnd_info)
697 {
698 volatile size_t sink = 0;
699 const union MHD_ConnectionInfo *ci;
700
701 ci = MHD_get_connection_info (connection,
703 if (NULL != ci)
704 sink += (size_t) (ci->connect_fd + 1);
705 ci = MHD_get_connection_info (connection,
707 if (NULL != ci)
708 sink += (size_t) ci->connection_timeout;
709 (void) sink;
710 }
711
712 resp = make_response ();
713 if (NULL == resp)
714 return MHD_NO;
715 ret = MHD_queue_response (connection,
716 cfg.error_reply
718 resp);
720 if (MHD_YES != ret)
721 return ret;
722 /* Only once the response was accepted: returning MHD_NO tells MHD to
723 terminate the connection, and terminating one that this very callback
724 suspended trips mhd_assert (! connection->suspended) in
725 MHD_connection_close_(). */
726 if (tearing_down ||
727 (0 > idx))
728 return ret;
729 if (cfg.susp_now)
730 {
731 MHD_suspend_connection (connection);
732 MHD_resume_connection (connection);
733 stat_suspend++;
734 stat_resume++;
735 }
736 else if (cfg.susp_park &&
737 (! lives[idx].susp))
738 {
739 MHD_suspend_connection (connection);
740 lives[idx].susp = 1;
741 stat_suspend++;
742 }
743 return ret;
744}
745
746
747/* ------------------------------------------------------------------ */
748/* Oracles */
749/* ------------------------------------------------------------------ */
750
759static void
761{
763 uint64_t t64 = 0;
764 int64_t t64s;
765 int ti;
766 enum MHD_Result r1;
767 enum MHD_Result r2;
768 int have[4];
769 uint64_t val[4];
770 unsigned int k;
771 uint64_t max_ms;
772 int any_timed;
773 char msg[256];
774
776 r1 = MHD_get_timeout (d, &tl);
777 r2 = MHD_get_timeout64 (d, &t64);
778 t64s = MHD_get_timeout64s (d);
779 ti = MHD_get_timeout_i (d);
780
781 have[0] = (MHD_YES == r1);
782 have[1] = (MHD_YES == r2);
783 have[2] = (0 <= t64s);
784 have[3] = (0 <= ti);
785 val[0] = (uint64_t) tl;
786 val[1] = t64;
787 val[2] = (uint64_t) (t64s < 0 ? 0 : t64s);
788 val[3] = (uint64_t) (ti < 0 ? 0 : ti);
789
790 for (k = 1; k < 4; k++)
791 if (have[k] != have[0])
793 "MHD_get_timeout*(): the four accessors disagree about whether "
794 "a timeout is in effect");
795
796 any_timed = scan_live_timeouts (&max_ms);
797 if (! have[0])
798 {
799 /* An indefinite wait while a live, not-suspended connection has a
800 non-zero timeout means that connection can never be reaped: its
801 deadline is already unreachable for the application. */
802 if (any_timed)
804 "MHD_get_timeout*() reported no timeout although the daemon has a "
805 "live, not suspended connection with a non-zero timeout");
806 deadline_valid = 0;
807 return;
808 }
809
810 for (k = 0; k < 4; k++)
811 {
812 if (val[k] <= max_ms)
813 continue;
814 (void) snprintf (msg, sizeof (msg),
815 "MHD_get_timeout*() [accessor %u] returned %llu ms, "
816 "larger than the largest connection timeout in effect "
817 "(%llu ms)",
818 k,
819 (unsigned long long) val[k],
820 (unsigned long long) max_ms);
822 }
823 for (k = 1; k < 4; k++)
824 if ( (val[k] > val[k - 1]) &&
825 (val[k] > 100) )
827 "MHD_get_timeout*(): a later accessor reported a larger timeout "
828 "than an earlier one, although the deadline cannot have moved");
829
830 deadline_ms = clk_ms + val[1];
831 deadline_valid = 1;
832}
833
834
843static void
844check_fdsets (unsigned int setsize,
845 MHD_socket max_fd,
846 int had_max,
847 int with_es)
848{
849 int f;
850 int hi = -1;
851 int max_seen = 0;
852
853 for (f = 0; f < (int) FD_SETSIZE; f++)
854 {
855 int in_set = FD_ISSET (f, &g_rs) || FD_ISSET (f, &g_ws);
856
857 if (with_es && FD_ISSET (f, &g_es))
858 in_set = 1;
859 if (! in_set)
860 continue;
861 if ((unsigned int) f >= setsize)
863 "MHD_get_fdset*() added a descriptor at or above the FD_SETSIZE "
864 "limit it was given");
865 if (f > hi)
866 hi = f;
867 if ( (had_max) &&
868 (MHD_INVALID_SOCKET != max_fd) &&
869 (f == (int) max_fd) )
870 max_seen = 1;
871 }
872 if (! had_max)
873 return;
874 if (0 > hi)
875 {
876 if (MHD_INVALID_SOCKET != max_fd)
878 "MHD_get_fdset*() set max_fd although it added no descriptor");
879 return;
880 }
881 if (MHD_INVALID_SOCKET == max_fd)
883 "MHD_get_fdset*() added descriptors but left max_fd unset");
884 if (hi > (int) max_fd)
886 "MHD_get_fdset*() added a descriptor larger than the max_fd it "
887 "reported");
888 if ((unsigned int) max_fd >= setsize)
890 "MHD_get_fdset*() reported a max_fd at or above the FD_SETSIZE "
891 "limit it was given");
892 if (! max_seen)
894 "MHD_get_fdset*() reported a max_fd that is not in any of the sets");
895}
896
897
898/* ------------------------------------------------------------------ */
899/* Event-loop primitives */
900/* ------------------------------------------------------------------ */
901
910static void
912 unsigned int var)
913{
915 unsigned int setsize = (unsigned int) FD_SETSIZE;
916 int had_max = 1;
917 int with_es = 1;
918
919 FD_ZERO (&g_rs);
920 FD_ZERO (&g_ws);
921 FD_ZERO (&g_es);
922 switch (var % 5)
923 {
924 case 0:
926 (void) (MHD_get_fdset) (d, &g_rs, &g_ws, &g_es, &max_fd);
927 break;
928 case 1:
930 (void) MHD_get_fdset2 (d, &g_rs, &g_ws, &g_es, &max_fd,
931 (unsigned int) FD_SETSIZE);
932 break;
933 case 2:
935 setsize = setsize_tbl[(var + cfg.clock_seed)
936 % (sizeof (setsize_tbl)
937 / sizeof (setsize_tbl[0]))];
938 (void) MHD_get_fdset2 (d, &g_rs, &g_ws, &g_es, &max_fd, setsize);
939 break;
940 case 3:
941 /* max_fd is documented as optional */
943 had_max = 0;
944 (void) MHD_get_fdset2 (d, &g_rs, &g_ws, &g_es, NULL,
945 (unsigned int) FD_SETSIZE);
946 break;
947 default:
948 /* no except set: deprecated, but shipped API */
950 with_es = 0;
951 (void) MHD_get_fdset2 (d, &g_rs, &g_ws, NULL, &max_fd,
952 (unsigned int) FD_SETSIZE);
953 break;
954 }
955 check_fdsets (setsize, max_fd, had_max, with_es);
956 g_max_fd = max_fd;
957 g_setsize = setsize;
958 g_have_sets = 1;
960 ((unsigned int) quiesced_fd < (unsigned int) FD_SETSIZE) &&
961 FD_ISSET (quiesced_fd, &g_rs) )
963 "MHD_get_fdset*() still watches the listening socket after "
964 "MHD_quiesce_daemon() handed it back to the application");
965}
966
967
975static void
977{
978 struct timeval tv;
979
980 if ( (! g_have_sets) ||
982 return;
983 tv.tv_sec = 0;
984 tv.tv_usec = 0;
985 (void) select ((int) g_max_fd + 1, &g_rs, &g_ws, &g_es, &tv);
986}
987
988
993static void
994drain_conn (unsigned int i)
995{
996 char tmp[DRAIN_BUF];
997
998 if ( (i >= MAX_CONNS) ||
999 (0 > csock[i]) )
1000 return;
1001 for (;;)
1002 {
1003 ssize_t n = recv (csock[i], tmp, sizeof (tmp), MSG_DONTWAIT);
1004
1005 if (0 >= n)
1006 break;
1007 }
1008}
1009
1010
1011static void
1013{
1014 unsigned int i;
1015
1016 for (i = 0; i < MAX_CONNS; i++)
1017 drain_conn (i);
1018}
1019
1020
1030static void
1032 unsigned int var,
1033 unsigned int flavour)
1034{
1035 fd_set rs;
1036 fd_set ws;
1037 fd_set es;
1038
1039 switch (flavour % 4)
1040 {
1041 case 1:
1042 FD_ZERO (&rs);
1043 FD_ZERO (&ws);
1044 FD_ZERO (&es);
1045 break;
1046 case 2:
1047 /* Every descriptor number reported ready. MHD only tests the bits of
1048 the sockets it owns, so this is an application that lies about
1049 readiness, not an invalid descriptor. */
1050 memset (&rs, 0xFF, sizeof (rs));
1051 memset (&ws, 0xFF, sizeof (ws));
1052 memset (&es, 0xFF, sizeof (es));
1053 break;
1054 case 3:
1055 rs = g_rs;
1056 FD_ZERO (&ws);
1057 FD_ZERO (&es);
1058 break;
1059 default:
1060 rs = g_rs;
1061 ws = g_ws;
1062 es = g_es;
1063 break;
1064 }
1065 switch (var % 4)
1066 {
1067 case 0:
1068 stat_rfs_v1++;
1069 (void) (MHD_run_from_select) (d, &rs, &ws, &es);
1070 break;
1071 case 1:
1072 stat_rfs_v2++;
1073 (void) MHD_run_from_select2 (d, &rs, &ws, &es,
1074 (g_setsize > (unsigned int) FD_SETSIZE)
1075 ? (unsigned int) FD_SETSIZE : g_setsize);
1076 break;
1077 case 2:
1078 stat_run++;
1079 (void) MHD_run (d);
1080 break;
1081 default:
1082 stat_run_wait++;
1083 (void) MHD_run_wait (d, 0);
1084 break;
1085 }
1086 deadline_valid = 0;
1087 if (cfg.auto_drain)
1088 drain_all ();
1089 if (cfg.timeout_every_run)
1090 check_timeouts (d);
1091}
1092
1093
1094/* ------------------------------------------------------------------ */
1095/* Connections */
1096/* ------------------------------------------------------------------ */
1097
1098static int
1100{
1101 int sv[2];
1102 struct sockaddr_in sa;
1103 unsigned int idx;
1104
1105 if (nconns >= MAX_CONNS)
1106 return -1;
1107 idx = nconns;
1108 if (0 != socketpair (AF_UNIX, SOCK_STREAM, 0, sv))
1109 return -1;
1110 if (cfg.small_sockbuf)
1111 {
1112 int bs = 2048;
1113
1114 (void) setsockopt (sv[0], SOL_SOCKET, SO_RCVBUF, &bs, sizeof (bs));
1115 (void) setsockopt (sv[1], SOL_SOCKET, SO_SNDBUF, &bs, sizeof (bs));
1116 }
1117 memset (&sa, 0, sizeof (sa));
1118 sa.sin_family = AF_INET;
1119 sa.sin_port = htons (44444);
1120 sa.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
1121 csock[idx] = sv[0];
1122 if (MHD_YES != MHD_add_connection (d,
1123 (MHD_socket) sv[1],
1124 (const struct sockaddr *) &sa,
1125 (socklen_t) sizeof (sa)))
1126 {
1127 /* MHD has already closed sv[1] in that case. */
1128 (void) close (sv[0]);
1129 csock[idx] = -1;
1130 return -1;
1131 }
1132 nconns++;
1133 cur_conn = idx;
1134 return (int) idx;
1135}
1136
1137
1159static int
1161{
1162 int sv[2];
1163 struct sockaddr_in sa;
1164
1165 if (0 != socketpair (AF_UNIX, SOCK_STREAM, 0, sv))
1166 return -1;
1167 memset (&sa, 0, sizeof (sa));
1168 sa.sin_family = AF_INET;
1169 sa.sin_port = htons (44444);
1170 sa.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
1171 if (MHD_YES != MHD_add_connection (d,
1172 (MHD_socket) sv[1],
1173 (const struct sockaddr *) &sa,
1174 (socklen_t) sizeof (sa)))
1175 {
1176 /* MHD has closed sv[1] already. */
1177 (void) close (sv[0]);
1178 return -1;
1179 }
1181 return sv[0];
1182}
1183
1184
1192static void
1193close_conn (unsigned int i,
1194 int graceful)
1195{
1196 if ( (i >= MAX_CONNS) ||
1197 (0 > csock[i]) )
1198 return;
1199 if (graceful)
1200 (void) shutdown (csock[i], SHUT_WR);
1201 (void) close (csock[i]);
1202 csock[i] = -1;
1203}
1204
1205
1206static void
1207send_bytes (unsigned int i,
1208 const void *buf,
1209 size_t len)
1210{
1211 const char *p = (const char *) buf;
1212 size_t off = 0;
1213 unsigned int tries = 0;
1214
1215 if ( (i >= MAX_CONNS) ||
1216 (0 > csock[i]) ||
1217 (0 == len) )
1218 return;
1219 while ( (off < len) &&
1220 (tries < 4) )
1221 {
1222 ssize_t s = send (csock[i], p + off, len - off, MSG_DONTWAIT);
1223
1224 if (0 < s)
1225 {
1226 off += (size_t) s;
1227 continue;
1228 }
1229 tries++;
1230 /* The peer's receive buffer is full because MHD has not run yet, or
1231 MHD is gone. One round is enough to tell the two apart; the rest
1232 of the fragment is simply dropped, which is a split point like any
1233 other. */
1234 if (NULL != cur_daemon)
1235 (void) MHD_run (cur_daemon);
1236 if ( (0 > s) &&
1237 (EAGAIN != errno) &&
1238 (EWOULDBLOCK != errno) &&
1239 (EINTR != errno) )
1240 break;
1241 }
1242}
1243
1244
1250static const char *const frag_tbl[16] = {
1251 "GET / HTTP/1.1\r\nHost: x\r\n\r\n",
1252 "GET /a HTTP/1.1\r\nHost: x\r\n",
1253 "\r\n",
1254 "POST /p HTTP/1.1\r\nHost: x\r\nContent-Length: 10\r\n\r\n",
1255 "0123456789",
1256 "POST /c HTTP/1.1\r\nHost: x\r\nTransfer-Encoding: chunked\r\n\r\n",
1257 "5\r\nabcde\r\n",
1258 "0\r\n\r\n",
1259 "GET / HTTP/1.1\r\nHost: x\r\nConnection: close\r\n\r\n",
1260 "GET / HTTP/1.0\r\n\r\n",
1261 "HEAD / HTTP/1.1\r\nHost: x\r\n\r\n",
1262 "GET / HTTP/1.1\r\nHost: x\r\n\r\nGET /2 HTTP/1.1\r\nHost: x\r\n\r\n",
1263 "G",
1264 "ET / HTTP/1.1\r\n",
1265 "Host: x\r\n\r\n",
1266 "\r\n\r\n"
1267};
1268
1269
1270/* ------------------------------------------------------------------ */
1271/* The operation program */
1272/* ------------------------------------------------------------------ */
1273
1293
1294
1302static void
1304{
1305 struct timeval tv;
1306
1307 if ( (! cfg.allow_real_wait) ||
1308 (1 != cfg.timeout_s) ||
1309 (0 >= expiry_budget) )
1310 return;
1311 expiry_budget--;
1313 tv.tv_sec = 1;
1314 tv.tv_usec = 50000;
1315 (void) select (0, NULL, NULL, NULL, &tv);
1316 clk_ms += 1050;
1317 /* Everything parked has to come back first, or the expiry cannot be
1318 observed for it at all. */
1319 (void) resume_all ();
1320 (void) MHD_run (d);
1321 (void) MHD_run (d);
1322}
1323
1324
1325static void
1327{
1328 MHD_socket ls;
1329
1330 stat_quiesce++;
1331 ls = MHD_quiesce_daemon (d);
1332 if (MHD_INVALID_SOCKET == ls)
1333 return;
1335 {
1336 /* MHD_quiesce_daemon() is documented to hand the socket over once
1337 and to answer MHD_INVALID_SOCKET afterwards. */
1338 (void) close (ls);
1340 "MHD_quiesce_daemon() handed out the listening socket twice");
1341 return;
1342 }
1343 quiesced_fd = ls;
1344}
1345
1346
1347static void
1349 unsigned int arg)
1350{
1351 const union MHD_DaemonInfo *di;
1352 const union MHD_ConnectionInfo *ci;
1353 volatile size_t sink = 0;
1354 unsigned int i = arg % MAX_LIVE;
1355
1357 if (NULL != di)
1358 sink += (size_t) di->num_connections;
1360 if (NULL != di)
1361 sink += (size_t) di->flags;
1362 if (NULL != lives[i].mc)
1363 {
1364 ci = MHD_get_connection_info (lives[i].mc,
1366 if (NULL != ci)
1367 sink += (size_t) ci->suspended;
1368 ci = MHD_get_connection_info (lives[i].mc,
1370 if (NULL != ci)
1371 sink += (NULL != ci->daemon) ? 1u : 0u;
1372 }
1373 (void) sink;
1374}
1375
1376
1377static void
1378op_connopt (unsigned int i,
1379 unsigned int val)
1380{
1381 if ( (i >= MAX_LIVE) ||
1382 (NULL == lives[i].mc) )
1383 return;
1384 /* Note that MHD skips the whole update while connection->suspended is
1385 set, so this is not necessarily the timeout the connection ends up
1386 with -- which is why the oracle reads it back instead. */
1387 (void) MHD_set_connection_option (lives[i].mc,
1389 val);
1390}
1391
1392
1393/* ------------------------------------------------------------------ */
1394/* The fuzz target */
1395/* ------------------------------------------------------------------ */
1396
1397int
1399 size_t size)
1400{
1401 struct MHD_Daemon *d;
1402 struct MHD_OptionItem opts[8];
1403 unsigned int nopt = 0;
1404 unsigned int flags;
1405 size_t pos;
1406 unsigned int nops = 0;
1407 unsigned int i;
1408 int queued_fd = -1;
1410 /* Must happen before the first write() into a socketpair. The built-in
1411 driver also does this, but that code is compiled out under
1412 -DFUZZ_NO_MAIN, which is exactly the build every external fuzzing
1413 engine uses; the call is idempotent. See fuzz_ignore_sigpipe() in
1414 fuzz_common.h for why the process dies without it. */
1416
1417 if (size < 5)
1418 return 0;
1419
1420 if (! big_body_ready)
1421 {
1422 big_body_ready = 1;
1423 for (i = 0; i < BIG_BODY_LEN; i++)
1424 big_body[i] = (char) ('a' + (i % 26));
1425 }
1426 if (! expiry_budget_read)
1427 {
1428 const char *e = getenv ("MHD_FUZZ_EXPIRY_BUDGET");
1429
1431 if (NULL != e)
1432 expiry_budget = atoi (e);
1433 }
1434
1435 memset (&cfg, 0, sizeof (cfg));
1436 /* Must never carry over: everything these point at belonged to the
1437 previous iteration's daemon and is long gone. */
1438 memset (lives, 0, sizeof (lives));
1439 for (i = 0; i < MAX_CONNS; i++)
1440 csock[i] = -1;
1441 nconns = 0;
1442 cur_conn = 0;
1443 tearing_down = 0;
1445 clk_ms = 0;
1446 deadline_ms = 0;
1447 deadline_valid = 0;
1448 g_have_sets = 0;
1450 g_setsize = (unsigned int) FD_SETSIZE;
1451
1452 cfg.timeout_s = timeout_tbl[data[0] & 0x03];
1453 cfg.listen_sock = (0 != (data[0] & 0x04));
1454 cfg.app_fd_setsize = (0 != (data[0] & 0x08));
1455 cfg.mem_limit = mem_limit_tbl[(data[0] >> 4) & 0x03];
1456 cfg.conn_limit = (0 != (data[0] & 0x40));
1457 cfg.small_sockbuf = (0 != (data[0] & 0x80));
1458
1459 cfg.resp_kind = (unsigned int) (data[1] & 0x03);
1460 cfg.error_reply = (0 != (data[1] & 0x04));
1461 cfg.susp_park = (0 != (data[1] & 0x08));
1462 cfg.susp_now = (0 != (data[1] & 0x10));
1463 cfg.hnd_connopt = (0 != (data[1] & 0x20));
1464 cfg.hnd_info = (0 != (data[1] & 0x40));
1465 cfg.auto_drain = (0 != (data[1] & 0x80));
1466
1467 cfg.fdset_var = (unsigned int) (data[2] & 0x07);
1468 cfg.run_var = (unsigned int) ((data[2] >> 3) & 0x03);
1469 cfg.honour_timeout = (0 != (data[2] & 0x20));
1470 cfg.timeout_every_run = (0 != (data[2] & 0x40));
1471 cfg.quiesce_end = (0 != (data[2] & 0x80));
1472
1473 cfg.nconn_up_front = 1u + (unsigned int) (data[3] & 0x03);
1474 cfg.check_always = (0 != (data[3] & 0x04));
1475 cfg.allow_real_wait = (0 != (data[3] & 0x08));
1476 cfg.stop_with_queued = (0 != (data[3] & 0x10));
1477 cfg.clock_seed = data[4];
1478
1479 if (0 != cfg.mem_limit)
1480 {
1482 opts[nopt].value = (intptr_t) cfg.mem_limit;
1483 opts[nopt].ptr_value = NULL;
1484 nopt++;
1485 }
1487 opts[nopt].value = (intptr_t) cfg.timeout_s;
1488 opts[nopt].ptr_value = NULL;
1489 nopt++;
1490 if (cfg.conn_limit)
1491 {
1493 opts[nopt].value = (intptr_t) 2;
1494 opts[nopt].ptr_value = NULL;
1495 nopt++;
1496 }
1497 if (cfg.app_fd_setsize)
1498 {
1499 opts[nopt].option = MHD_OPTION_APP_FD_SETSIZE;
1500 opts[nopt].value = (intptr_t) FD_SETSIZE;
1501 opts[nopt].ptr_value = NULL;
1502 nopt++;
1503 }
1504 opts[nopt].option = MHD_OPTION_END;
1505 opts[nopt].value = 0;
1506 opts[nopt].ptr_value = NULL;
1507
1508 /* MHD_ALLOW_SUSPEND_RESUME is always on: it is what this harness is
1509 about, and it implies MHD_USE_ITC, whose descriptor is one more thing
1510 MHD_get_fdset*() has to report correctly. */
1512 if (! cfg.listen_sock)
1513 flags |= MHD_USE_NO_LISTEN_SOCKET;
1514 if (fuzz_verbose)
1515 flags |= MHD_USE_ERROR_LOG;
1516
1518 d = MHD_start_daemon (flags,
1519 0,
1520 NULL, NULL,
1521 &ahc, NULL,
1522 MHD_OPTION_ARRAY, opts,
1523 /* through the varargs rather than the option
1524 array: storing a function pointer in the
1525 array's intptr_t member is not strictly
1526 conforming C */
1529 if ( (NULL == d) &&
1530 cfg.listen_sock)
1531 {
1532 /* No networking available: fall back to the socketpair-only daemon
1533 rather than losing the whole iteration. */
1534 cfg.listen_sock = 0;
1535 flags |= MHD_USE_NO_LISTEN_SOCKET;
1536 d = MHD_start_daemon (flags,
1537 0,
1538 NULL, NULL,
1539 &ahc, NULL,
1540 MHD_OPTION_ARRAY, opts,
1543 }
1544 if (NULL == d)
1545 return 0;
1546 cur_daemon = d;
1547 stat_daemons++;
1548 if (! stats_registered)
1549 {
1550 stats_registered = 1;
1551 (void) atexit (&print_stats);
1552 }
1553
1554 for (i = 0; i < cfg.nconn_up_front; i++)
1555 if (0 > new_connection (d))
1556 break;
1557
1558 pos = 5;
1559 while ( (pos < size) &&
1560 (nops < MAX_OPS) )
1561 {
1562 const unsigned int b = data[pos++];
1563 const unsigned int opc = b >> 4;
1564 const unsigned int arg = b & 0x0F;
1565
1566 nops++;
1567 switch (opc)
1568 {
1569 case OP_SEND_FRAG:
1570 {
1571 const char *f = frag_tbl[arg];
1572
1573 send_bytes (cur_conn, f, strlen (f));
1574 break;
1575 }
1576 case OP_SEND_RAW:
1577 {
1578 size_t l;
1579
1580 if (pos >= size)
1581 break;
1582 l = data[pos++];
1583 if (l > size - pos)
1584 l = size - pos;
1585 send_bytes (cur_conn, data + pos, l);
1586 pos += l;
1587 break;
1588 }
1589 case OP_FDSET:
1590 op_fdset (d, (0 != (arg & 0x08)) ? cfg.fdset_var : arg);
1591 break;
1592 case OP_RUN:
1593 op_run (d, arg & 0x03, (arg >> 2) & 0x03);
1594 break;
1595 case OP_TIMEOUT:
1596 check_timeouts (d);
1597 if ( (cfg.honour_timeout) &&
1598 (deadline_valid) &&
1599 (deadline_ms <= clk_ms) )
1600 op_run (d, cfg.run_var, 0);
1601 break;
1602 case OP_CLOCK:
1603 if (15 == arg)
1604 wait_for_expiry (d);
1605 else
1606 {
1607 clk_ms += (uint64_t) (1u + arg) * (uint64_t) (1u + cfg.clock_seed % 64u)
1608 ;
1609 if ( (cfg.honour_timeout) &&
1610 (deadline_valid) &&
1611 (deadline_ms <= clk_ms) )
1612 op_run (d, cfg.run_var, 0);
1613 }
1614 break;
1615 case OP_SUSPEND:
1616 do_suspend (arg % MAX_LIVE);
1617 break;
1618 case OP_RESUME:
1619 do_resume (arg % MAX_LIVE);
1620 break;
1621 case OP_NEWCONN:
1622 (void) new_connection (d);
1623 break;
1624 case OP_CLOSECONN:
1625 close_conn ((0 != (arg & 0x08)) ? cur_conn : (arg % MAX_CONNS),
1626 (0 != (arg & 0x04)));
1627 break;
1628 case OP_SWITCH:
1629 if ( (arg % MAX_CONNS) < nconns)
1630 cur_conn = arg % MAX_CONNS;
1631 break;
1632 case OP_QUIESCE:
1633 op_quiesce (d);
1634 break;
1635 case OP_DRAIN:
1636 if (0 != (arg & 0x08))
1637 drain_all ();
1638 else
1639 drain_conn (arg % MAX_CONNS);
1640 break;
1641 case OP_CONNOPT:
1642 op_connopt (arg % MAX_LIVE, (arg >> 3) & 0x01);
1643 break;
1644 case OP_INFO:
1645 op_info (d, arg);
1646 break;
1647 default:
1648 op_poll ();
1649 break;
1650 }
1651 if (cfg.check_always)
1652 check_timeouts (d);
1653 }
1654
1655 /* ---- teardown ---- */
1656 tearing_down = 1;
1657 queued_fd = -1;
1658 for (i = 0; i < MAX_CONNS; i++)
1659 close_conn (i, 1);
1660 /* A connection left suspended makes MHD_stop_daemon() MHD_PANIC()
1661 ("called while we have suspended connections"), and
1662 MHD_resume_connection() alone is not enough: it only raises a flag,
1663 and the connection leaves the suspended list in MHD_run(). So flush
1664 and run until nothing is parked any more. tearing_down keeps the
1665 handler from parking anything new, which is what bounds this loop;
1666 the cap is only a backstop. */
1667 for (i = 0; i < MAX_CONNS + 2u; i++)
1668 {
1669 if (! resume_all ())
1670 break;
1671 (void) MHD_run (d);
1672 }
1673 (void) MHD_run (d);
1674 if (cfg.quiesce_end)
1675 op_quiesce (d);
1676 /* Last, so that no run can drain the list again. */
1677 if (cfg.stop_with_queued)
1678 queued_fd = queue_unprocessed_conn (d);
1679 MHD_stop_daemon (d);
1680 cur_daemon = NULL;
1681 if (0 <= queued_fd)
1682 {
1683 (void) close (queued_fd);
1684 queued_fd = -1;
1685 }
1687 {
1688 (void) close (quiesced_fd);
1690 }
1691 for (i = 0; i < MAX_CONNS; i++)
1692 close_conn (i, 0);
1693 memset (lives, 0, sizeof (lives));
1694 return 0;
1695}
1696
1697
1698/* ------------------------------------------------------------------ */
1699/* Structure-aware schedule generator */
1700/* ------------------------------------------------------------------ */
1701
1702struct sbuf
1703{
1704 uint8_t *p;
1705 size_t len;
1706 size_t cap;
1707};
1708
1709
1710static void
1711sb_byte (struct sbuf *b,
1712 uint8_t v)
1713{
1714 if (b->len < b->cap)
1715 b->p[b->len++] = v;
1716}
1717
1718
1719static void
1720sb_op (struct sbuf *b,
1721 unsigned int opc,
1722 unsigned int arg)
1723{
1724 sb_byte (b, (uint8_t) ((opc << 4) | (arg & 0x0F)));
1725}
1726
1727
1733static const unsigned char gen_frag_bias[] = {
1734 0, 0, 0, 3, 4, 5, 6, 7, 8, 9, 10, 11, 11, 1, 2, 12, 13, 14, 15
1735};
1736
1737
1738static unsigned int
1739gen_frag (struct fuzz_rng *rng)
1740{
1741 if (fuzz_chance (rng, 4))
1742 return fuzz_below (rng, 16);
1743 return gen_frag_bias[fuzz_below (rng,
1744 (uint32_t) sizeof (gen_frag_bias))];
1745}
1746
1747
1752static void
1754 struct sbuf *b)
1755{
1756 unsigned int v;
1757 unsigned int fl;
1758
1759 if (! fuzz_chance (rng, 5))
1760 sb_op (b, OP_FDSET, fuzz_below (rng, 16));
1761 if (! fuzz_chance (rng, 3))
1762 sb_op (b, OP_POLL, 0);
1763 if (fuzz_chance (rng, 3))
1764 sb_op (b, OP_TIMEOUT, fuzz_below (rng, 16));
1765 v = fuzz_below (rng, 4);
1766 /* Half the runs use the descriptor sets MHD actually asked for, so
1767 that requests make progress; the other half are the adversarial
1768 ones (all-zero, all-ones, read set only). */
1769 fl = fuzz_chance (rng, 2) ? 0u : fuzz_below (rng, 4);
1770 sb_op (b, OP_RUN, v | (fl << 2));
1771}
1772
1773
1774static size_t
1776 uint8_t *buf,
1777 size_t cap)
1778{
1779 struct sbuf b;
1780 unsigned int nrounds;
1781 unsigned int i;
1782 unsigned int r;
1783
1784 b.p = buf;
1785 b.len = 0;
1786 b.cap = cap;
1787
1788 /* --- configuration block --- */
1789 sb_byte (&b, fuzz_byte (rng));
1790 sb_byte (&b, fuzz_byte (rng));
1791 sb_byte (&b, fuzz_byte (rng));
1792 sb_byte (&b, fuzz_byte (rng));
1793 sb_byte (&b, fuzz_byte (rng));
1794
1795 nrounds = 3u + fuzz_below (rng, 40);
1796 for (i = 0; i < nrounds; i++)
1797 {
1798 switch (fuzz_below (rng, 24))
1799 {
1800 case 0:
1801 case 1:
1802 case 2:
1803 case 3:
1804 case 4:
1805 case 5:
1806 /* feed the current connection */
1807 sb_op (&b, OP_SEND_FRAG, gen_frag (rng));
1808 gen_loop_round (rng, &b);
1809 break;
1810 case 6:
1811 /* raw literal, so that a mutator has somewhere to put bytes */
1812 {
1813 unsigned int n = fuzz_below (rng, 24);
1814 unsigned int k;
1815
1816 sb_op (&b, OP_SEND_RAW, 0);
1817 sb_byte (&b, (uint8_t) n);
1818 for (k = 0; k < n; k++)
1819 sb_byte (&b, fuzz_byte (rng));
1820 break;
1821 }
1822 case 7:
1823 case 8:
1824 gen_loop_round (rng, &b);
1825 break;
1826 case 9:
1827 r = fuzz_below (rng, 16);
1828 sb_op (&b, OP_SUSPEND, r);
1829 break;
1830 case 10:
1831 r = fuzz_below (rng, 16);
1832 sb_op (&b, OP_RESUME, r);
1833 break;
1834 case 11:
1835 sb_op (&b, OP_NEWCONN, 0);
1836 sb_op (&b, OP_SEND_FRAG, gen_frag (rng));
1837 break;
1838 case 12:
1839 r = fuzz_below (rng, 16);
1840 sb_op (&b, OP_CLOSECONN, r);
1841 break;
1842 case 13:
1843 r = fuzz_below (rng, 16);
1844 sb_op (&b, OP_SWITCH, r);
1845 break;
1846 case 14:
1847 sb_op (&b, OP_QUIESCE, 0);
1848 break;
1849 case 15:
1850 r = fuzz_below (rng, 16);
1851 sb_op (&b, OP_DRAIN, r);
1852 break;
1853 case 16:
1854 r = fuzz_below (rng, 16);
1855 sb_op (&b, OP_CONNOPT, r);
1856 break;
1857 case 17:
1858 r = fuzz_below (rng, 16);
1859 sb_op (&b, OP_INFO, r);
1860 break;
1861 case 18:
1862 r = fuzz_below (rng, 16);
1863 sb_op (&b, OP_TIMEOUT, r);
1864 break;
1865 case 19:
1866 /* an artificial clock jump, occasionally the real one that lets a
1867 connection time out */
1868 r = fuzz_chance (rng, 40) ? 15u : fuzz_below (rng, 15);
1869 sb_op (&b, OP_CLOCK, r);
1870 break;
1871 case 20:
1872 /* a run without ever asking MHD what it wanted */
1873 r = fuzz_below (rng, 16);
1874 sb_op (&b, OP_RUN, r);
1875 break;
1876 case 21:
1877 sb_op (&b, OP_POLL, 0);
1878 break;
1879 default:
1880 sb_op (&b, OP_SEND_FRAG, gen_frag (rng));
1881 break;
1882 }
1883 }
1884 /* Make sure most schedules end with the daemon actually run, so that
1885 the interesting states are reached rather than only set up. */
1886 gen_loop_round (rng, &b);
1887 return b.len;
1888}
1889
1890
1891/* ------------------------------------------------------------------ */
1892/* Built-in seed corpus */
1893/* ------------------------------------------------------------------ */
1894
1901struct seed_def
1902{
1903 const char *name;
1904 unsigned char cfg[5];
1905 unsigned char ops[24];
1906 unsigned char nops;
1907};
1908
1909#define OPB(o,a) (unsigned char) (((o) << 4) | (a))
1910
1911static const struct seed_def seeds[] = {
1912 /* MHD_get_fdset() (the real v1 entry point) + select() +
1913 MHD_run_from_select() (also v1) */
1914 { "fdset-v1-run-from-select-v1", { 0x00, 0x80, 0x00, 0x00, 0x00 },
1915 { OPB (OP_SEND_FRAG, 0), OPB (OP_FDSET, 0), OPB (OP_POLL, 0),
1916 OPB (OP_RUN, 0), OPB (OP_TIMEOUT, 0), OPB (OP_FDSET, 0),
1917 OPB (OP_POLL, 0), OPB (OP_RUN, 0) }, 8 },
1918
1919 /* MHD_get_fdset2() + MHD_run_from_select2() */
1920 { "fdset2-run-from-select2", { 0x00, 0x80, 0x09, 0x00, 0x00 },
1921 { OPB (OP_SEND_FRAG, 0), OPB (OP_FDSET, 1), OPB (OP_POLL, 0),
1922 OPB (OP_RUN, 1), OPB (OP_TIMEOUT, 0), OPB (OP_FDSET, 1),
1923 OPB (OP_POLL, 0), OPB (OP_RUN, 1) }, 8 },
1924
1925 /* a small FD_SETSIZE limit, which MHD has to refuse descriptors for */
1926 { "fdset2-small-setsize", { 0x00, 0x80, 0x02, 0x00, 0x04 },
1927 { OPB (OP_SEND_FRAG, 0), OPB (OP_FDSET, 2), OPB (OP_RUN, 1),
1928 OPB (OP_FDSET, 2), OPB (OP_TIMEOUT, 0), OPB (OP_RUN, 1) }, 6 },
1929
1930 /* max_fd == NULL and except_fd_set == NULL, both documented shapes */
1931 { "fdset2-null-args", { 0x00, 0x80, 0x00, 0x00, 0x00 },
1932 { OPB (OP_SEND_FRAG, 0), OPB (OP_FDSET, 3), OPB (OP_RUN, 1),
1933 OPB (OP_FDSET, 4), OPB (OP_RUN, 1), OPB (OP_TIMEOUT, 0) }, 6 },
1934
1935 /* MHD_run() and MHD_run_wait() */
1936 { "run-and-run-wait", { 0x00, 0x80, 0x10, 0x00, 0x00 },
1937 { OPB (OP_SEND_FRAG, 0), OPB (OP_RUN, 2), OPB (OP_RUN, 3),
1938 OPB (OP_TIMEOUT, 0), OPB (OP_RUN, 2) }, 5 },
1939
1940 /* descriptor sets MHD never asked for: all-zero and all-ones */
1941 { "run-from-select-bogus-sets", { 0x00, 0x80, 0x00, 0x00, 0x00 },
1942 { OPB (OP_SEND_FRAG, 0), OPB (OP_RUN, 0x04), OPB (OP_RUN, 0x08),
1943 OPB (OP_RUN, 0x05), OPB (OP_RUN, 0x09), OPB (OP_TIMEOUT, 0),
1944 OPB (OP_RUN, 0x0C) }, 7 },
1945
1946 /* stale sets: collect, close the connection, then run with them */
1947 { "run-from-select-stale-sets", { 0x00, 0x80, 0x00, 0x00, 0x00 },
1948 { OPB (OP_SEND_FRAG, 1), OPB (OP_FDSET, 1), OPB (OP_CLOSECONN, 0),
1949 OPB (OP_RUN, 0), OPB (OP_RUN, 1), OPB (OP_TIMEOUT, 0) }, 6 },
1950
1951 /* suspend from the pump loop, across a full fdset/poll/run round */
1952 { "suspend-across-loop", { 0x00, 0x80, 0x00, 0x00, 0x00 },
1953 { OPB (OP_SEND_FRAG, 1), OPB (OP_RUN, 2), OPB (OP_SUSPEND, 0),
1954 OPB (OP_FDSET, 1), OPB (OP_TIMEOUT, 0), OPB (OP_POLL, 0),
1955 OPB (OP_RUN, 1), OPB (OP_RESUME, 0), OPB (OP_RUN, 2) }, 9 },
1956
1957 /* the handler parks the connection; several are parked at once */
1958 { "handler-parks-two-connections", { 0x00, 0x88, 0x00, 0x00, 0x00 },
1959 { OPB (OP_SEND_FRAG, 0), OPB (OP_RUN, 2), OPB (OP_NEWCONN, 0),
1960 OPB (OP_SEND_FRAG, 0), OPB (OP_RUN, 2), OPB (OP_TIMEOUT, 0),
1961 OPB (OP_RESUME, 0), OPB (OP_RUN, 2) }, 8 },
1962
1963 /* suspend while a resume is still pending (cancels the resume) */
1964 { "suspend-cancels-pending-resume", { 0x00, 0x80, 0x00, 0x00, 0x00 },
1965 { OPB (OP_SEND_FRAG, 1), OPB (OP_RUN, 2), OPB (OP_SUSPEND, 0),
1966 OPB (OP_RESUME, 0), OPB (OP_SUSPEND, 0), OPB (OP_TIMEOUT, 0),
1967 OPB (OP_RESUME, 0), OPB (OP_RUN, 2) }, 8 },
1968
1969 /* quiesce a daemon that really has a listening socket, then check that
1970 it is gone from the descriptor sets */
1971 { "quiesce-listening-daemon", { 0x04, 0x80, 0x00, 0x00, 0x00 },
1972 { OPB (OP_SEND_FRAG, 0), OPB (OP_FDSET, 1), OPB (OP_RUN, 1),
1973 OPB (OP_QUIESCE, 0), OPB (OP_FDSET, 1), OPB (OP_TIMEOUT, 0),
1974 OPB (OP_RUN, 1), OPB (OP_QUIESCE, 0) }, 8 },
1975
1976 /* a large response on a small socket buffer: the connection stays
1977 blocked on write over many rounds */
1978 { "blocked-write-scheduling", { 0x80, 0x02, 0x00, 0x00, 0x00 },
1979 { OPB (OP_SEND_FRAG, 0), OPB (OP_FDSET, 1), OPB (OP_POLL, 0),
1980 OPB (OP_RUN, 1), OPB (OP_FDSET, 1), OPB (OP_POLL, 0),
1981 OPB (OP_RUN, 1), OPB (OP_DRAIN, 8), OPB (OP_FDSET, 1),
1982 OPB (OP_RUN, 1), OPB (OP_TIMEOUT, 0) }, 11 },
1983
1984 /* a real connection-timeout expiry */
1985 { "connection-timeout-expiry", { 0x00, 0x80, 0x00, 0x08, 0x00 },
1986 { OPB (OP_SEND_FRAG, 1), OPB (OP_RUN, 2), OPB (OP_TIMEOUT, 0),
1987 OPB (OP_CLOCK, 15), OPB (OP_TIMEOUT, 0), OPB (OP_RUN, 2) }, 6 },
1988
1989 /* per-connection timeouts, which move the connection to the "manual"
1990 timeout list that MHD_get_timeout*() has to scan separately */
1991 { "manual-timeout-list", { 0x00, 0x80, 0x00, 0x03, 0x00 },
1992 { OPB (OP_SEND_FRAG, 1), OPB (OP_CONNOPT, 8), OPB (OP_TIMEOUT, 0),
1993 OPB (OP_CONNOPT, 1), OPB (OP_TIMEOUT, 0), OPB (OP_RUN, 2),
1994 OPB (OP_TIMEOUT, 0) }, 7 },
1995
1996 /* pipelined requests plus a chunked upload, driven one run at a time */
1997 { "pipelined-and-chunked", { 0x00, 0x81, 0x00, 0x00, 0x00 },
1998 { OPB (OP_SEND_FRAG, 11), OPB (OP_RUN, 2), OPB (OP_SEND_FRAG, 5),
1999 OPB (OP_RUN, 2), OPB (OP_SEND_FRAG, 6), OPB (OP_RUN, 2),
2000 OPB (OP_SEND_FRAG, 7), OPB (OP_RUN, 2), OPB (OP_TIMEOUT, 0) }, 9 },
2001
2002 /* never poll, never ask for descriptors, only run from empty sets */
2003 { "never-poll", { 0x00, 0x80, 0x00, 0x00, 0x00 },
2004 { OPB (OP_SEND_FRAG, 0), OPB (OP_RUN, 0x04), OPB (OP_RUN, 0x04),
2005 OPB (OP_RUN, 0x04), OPB (OP_RUN, 0x04), OPB (OP_TIMEOUT, 0) }, 6 },
2006
2007 /* abrupt close in the middle of a request, with the descriptor sets
2008 collected before it */
2009 { "abrupt-close-mid-request", { 0x00, 0x80, 0x00, 0x00, 0x00 },
2010 { OPB (OP_SEND_FRAG, 3), OPB (OP_FDSET, 1), OPB (OP_CLOSECONN, 0),
2011 OPB (OP_RUN, 1), OPB (OP_TIMEOUT, 0), OPB (OP_RUN, 2) }, 6 },
2012
2013 /* connection limit of two, so that MHD_add_connection() starts to fail */
2014 { "connection-limit", { 0x40, 0x80, 0x00, 0x03, 0x00 },
2015 { OPB (OP_NEWCONN, 0), OPB (OP_NEWCONN, 0), OPB (OP_SEND_FRAG, 0),
2016 OPB (OP_RUN, 2), OPB (OP_TIMEOUT, 0) }, 5 },
2017
2018 /* no connection timeout at all: MHD_get_timeout*() must report that an
2019 indefinite wait is fine */
2020 { "no-timeout-configured", { 0x02, 0x80, 0x00, 0x04, 0x00 },
2021 { OPB (OP_SEND_FRAG, 1), OPB (OP_TIMEOUT, 0), OPB (OP_RUN, 2),
2022 OPB (OP_TIMEOUT, 0), OPB (OP_SEND_FRAG, 2), OPB (OP_RUN, 2) }, 6 },
2023
2024 /* byte 3 bit 4: after the program has run and everything has been torn
2025 down, hand MHD one more connection and stop it without another run,
2026 so that it has to dispose of a connection it never started. That is
2027 new_connection_close_() in daemon.c, which nothing else reaches. */
2028 { "stop-with-queued-connection", { 0x00, 0x80, 0x00, 0x10, 0x00 },
2029 { OPB (OP_SEND_FRAG, 0), OPB (OP_RUN, 2), OPB (OP_TIMEOUT, 0) }, 3 },
2030
2031 /* the same after MHD_quiesce_daemon() on a daemon that really has a
2032 listening socket, which is the other order the two can happen in */
2033 { "stop-with-queued-after-quiesce", { 0x04, 0x80, 0x80, 0x10, 0x00 },
2034 { OPB (OP_SEND_FRAG, 0), OPB (OP_RUN, 2) }, 2 }
2035};
2036
2037static uint8_t seed_render_buf[64];
2038
2039
2040static size_t
2042{
2043 return sizeof (seeds) / sizeof (seeds[0]);
2044}
2045
2046
2047static const uint8_t *
2048fuzz_seed_get (size_t idx,
2049 size_t *len)
2050{
2051 const struct seed_def *sd = &seeds[idx];
2052 size_t n = sd->nops;
2053
2054 if (n > sizeof (sd->ops))
2055 n = sizeof (sd->ops);
2056 if (n + sizeof (sd->cfg) > sizeof (seed_render_buf))
2057 n = sizeof (seed_render_buf) - sizeof (sd->cfg);
2058 memcpy (seed_render_buf, sd->cfg, sizeof (sd->cfg));
2059 memcpy (seed_render_buf + sizeof (sd->cfg), sd->ops, n);
2060 *len = sizeof (sd->cfg) + n;
2061 return seed_render_buf;
2062}
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 drain_all(void)
static int csock[MAX_CONNS]
static void close_conn(unsigned int i, int graceful)
static size_t fuzz_seed_count(void)
static fd_set g_rs
static const struct seed_def seeds[]
static void op_info(struct MHD_Daemon *d, unsigned int arg)
static void op_fdset(struct MHD_Daemon *d, unsigned int var)
static int stats_registered
static void notify_conn_cb(void *cls, struct MHD_Connection *connection, void **socket_context, enum MHD_ConnectionNotificationCode toe)
static unsigned long stat_daemons
static int resume_all(void)
#define MAX_OPS
static const unsigned int timeout_tbl[]
static const unsigned int setsize_tbl[]
static fd_set g_ws
static int tearing_down
static void check_timeouts(struct MHD_Daemon *d)
static unsigned long stat_quiesce
static const unsigned char gen_frag_bias[]
static void sb_byte(struct sbuf *b, uint8_t v)
static unsigned long stat_suspend
static unsigned long stat_fdset_v2
static int new_connection(struct MHD_Daemon *d)
static MHD_socket g_max_fd
static unsigned long stat_expiry_waits
#define FUZZ_HARNESS_NAME
static unsigned long stat_run
static void check_fdsets(unsigned int setsize, MHD_socket max_fd, int had_max, int with_es)
static void drain_conn(unsigned int i)
static const uint8_t * fuzz_seed_get(size_t idx, size_t *len)
static unsigned long stat_fdset_v1
static void wait_for_expiry(struct MHD_Daemon *d)
static unsigned long stat_rfs_v2
static struct MHD_Response * make_response(void)
static void do_resume(unsigned int i)
static uint64_t clk_ms
static void sb_op(struct sbuf *b, unsigned int opc, unsigned int arg)
static void op_poll(void)
static unsigned int gen_frag(struct fuzz_rng *rng)
static int g_have_sets
static unsigned long stat_run_wait
static MHD_socket quiesced_fd
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
static void op_quiesce(struct MHD_Daemon *d)
static unsigned int cur_conn
@ OP_QUIESCE
@ OP_CLOCK
@ OP_INFO
@ OP_FDSET
@ OP_SWITCH
@ OP_SEND_RAW
@ OP_SEND_FRAG
@ OP_POLL
@ OP_SUSPEND
@ OP_RESUME
@ OP_CLOSECONN
@ OP_NEWCONN
@ OP_RUN
@ OP_CONNOPT
@ OP_DRAIN
@ OP_TIMEOUT
static int slot_of(const struct MHD_Connection *c)
static unsigned long stat_rfs_v1
static unsigned long stat_timeouts
#define BIG_BODY_LEN
static char big_body[BIG_BODY_LEN]
static void send_bytes(unsigned int i, const void *buf, size_t len)
static int expiry_budget_read
static struct MHD_Daemon * cur_daemon
static size_t fuzz_generate(struct fuzz_rng *rng, uint8_t *buf, size_t cap)
static void print_stats(void)
static uint64_t deadline_ms
#define DRAIN_BUF
static void panic_cb(void *cls, const char *file, unsigned int line, const char *reason)
static const char *const frag_tbl[16]
static int deadline_valid
static struct live_conn lives[MAX_LIVE]
static void op_run(struct MHD_Daemon *d, unsigned int var, unsigned int flavour)
static uint8_t seed_render_buf[64]
static unsigned long stat_resume
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_handler_calls
static int queue_unprocessed_conn(struct MHD_Daemon *d)
static fd_set g_es
static int scan_live_timeouts(uint64_t *max_ms)
static unsigned long stat_queued_at_stop
#define MAX_LIVE
#define OPB(o, a)
#define MAX_CONNS
static void op_connopt(unsigned int i, unsigned int val)
static void crc_free(void *cls)
static unsigned int nconns
static int mhd_thinks_suspended(unsigned int i)
static struct el_cfg cfg
static const size_t mem_limit_tbl[]
static int expiry_budget
static int big_body_ready
static unsigned int g_setsize
static ssize_t crc_cb(void *cls, uint64_t pos, char *buf, size_t max)
static void gen_loop_round(struct fuzz_rng *rng, struct sbuf *b)
static void do_suspend(unsigned int i)
_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_get_timeout64(struct MHD_Daemon *daemon, uint64_t *timeout)
Definition daemon.c:4306
_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
_MHD_EXTERN void MHD_set_panic_func(MHD_PanicCallback cb, void *cls)
Definition mhd_panic.c:94
MHD_ConnectionNotificationCode
@ MHD_CONNECTION_NOTIFY_STARTED
@ MHD_CONNECTION_INFO_CONNECTION_TIMEOUT
@ MHD_CONNECTION_INFO_CONNECTION_SUSPENDED
@ MHD_CONNECTION_INFO_DAEMON
@ MHD_CONNECTION_INFO_CONNECTION_FD
_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_EXTERN struct MHD_Response * MHD_create_response_from_callback(uint64_t size, size_t block_size, MHD_ContentReaderCallback crc, void *crc_cls, MHD_ContentReaderFreeCallback crfc)
Definition response.c:992
_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_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 const union MHD_ConnectionInfo * MHD_get_connection_info(struct MHD_Connection *connection, enum MHD_ConnectionInfoType info_type,...)
#define NULL
public interface to libmicrohttpd
int MHD_socket
Definition microhttpd.h:206
@ MHD_OPTION_APP_FD_SETSIZE
@ MHD_OPTION_NOTIFY_CONNECTION
@ MHD_OPTION_CONNECTION_LIMIT
@ MHD_OPTION_CONNECTION_MEMORY_LIMIT
@ MHD_OPTION_ARRAY
@ MHD_OPTION_CONNECTION_TIMEOUT
@ MHD_OPTION_END
#define MHD_SIZE_UNKNOWN
Definition microhttpd.h:183
MHD_Result
Definition microhttpd.h:163
@ MHD_YES
Definition microhttpd.h:172
@ MHD_NO
Definition microhttpd.h:167
#define MHD_CONTENT_READER_END_OF_STREAM
Definition microhttpd.h:186
#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_FLAGS
@ MHD_DAEMON_INFO_CURRENT_CONNECTIONS
@ MHD_ALLOW_SUSPEND_RESUME
@ MHD_USE_ERROR_LOG
@ MHD_USE_NO_LISTEN_SOCKET
@ MHD_RF_NONE
_MHD_EXTERN void MHD_suspend_connection(struct MHD_Connection *connection)
Definition daemon.c:3402
@ MHD_CONNECTION_OPTION_TIMEOUT
enum MHD_OPTION option
unsigned int connection_timeout
struct MHD_Daemon * daemon
MHD_socket connect_fd
enum MHD_FLAG flags
unsigned int num_connections