GNU libmicrohttpd 1.0.9
Loading...
Searching...
No Matches
fuzz_request.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
75/* Must precede every #include: memfd_create() and MFD_CLOEXEC are only
76 declared by <sys/mman.h> under _GNU_SOURCE, and the OSS-Fuzz build
77 compiles this file with nothing but -DFUZZ_NO_MAIN (the library gets
78 _GNU_SOURCE from configure, the hand-compiled harnesses do not).
79 Without this the fd-backed response kinds silently fall back to a
80 buffer response and MHD_create_response_from_fd*() is never reached
81 -- a failure mode with no error message at all. */
82#ifndef _GNU_SOURCE
83#define _GNU_SOURCE 1
84#endif
85
86#define FUZZ_HARNESS_NAME "fuzz_request"
87#include "fuzz_common.h"
88
89#include <microhttpd.h>
90#include <sys/socket.h>
91#include <netinet/in.h>
92#include <sys/select.h>
93
94/* MHD_create_response_from_data() and MHD_create_response_from_buffer()
95 with an explicit MHD_ResponseMemoryMode are deprecated but are still
96 shipped API and are therefore still worth fuzzing; the deprecation
97 warning would otherwise drown the build output. */
98#if defined(__GNUC__) || defined(__clang__)
99#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
100#endif
101
102/* memfd_create() gives a file descriptor backed by anonymous memory,
103 which is what makes the MHD_create_response_from_fd*() variants
104 affordable at fuzzing rates: no filesystem is touched. Where it is
105 unavailable the fd-backed response kinds fall back to a buffer
106 response (see make_response()). */
107#if defined(__linux__)
108#include <sys/mman.h>
109#if defined(MFD_CLOEXEC)
110#define FUZZ_HAVE_MEMFD 1
111#endif
112#endif
113
114#define MAX_SEGMENTS 96
115#define MAX_CONNECTIONS 8
116#define RESP_BUF_SIZE 16384
117#define GEN_BUF_SIZE 8192
118#define MAX_EXPECT_BODY 4096
119
121#define RESP_KIND_COUNT 16
123#define DAUTH_VARIANT_COUNT 10
125#define MAX_DIGEST_BIN 64
126
127#define DIGEST_REALM "TestRealm"
128#define DIGEST_USER "user"
129#define DIGEST_PASS "pass"
130
131/* ------------------------------------------------------------------ */
132/* Per-iteration state */
133/* ------------------------------------------------------------------ */
134
135struct fuzz_cfg
136{
137 size_t mem_limit;
138 int discipline;
139 unsigned int insanity;
140 unsigned int nonce_nc_size;
141 enum MHD_DigestAuthMultiAlgo3 algo;
142 enum MHD_DigestAuthMultiQOP qop;
143 int do_digest;
144 int do_basic;
145 int do_postproc;
146 int do_iterate;
147 int chunked_reply;
148 int error_reply;
149
150 /* ---- configuration bytes 4-9; all zero is the plainest setting ---- */
151 unsigned int resp_kind;
152 unsigned int resp_nhdr;
153 int resp_footer;
154 int resp_hdr_api;
155 unsigned int dauth_variant;
156 unsigned int fail_variant;
157 int basic_v1;
158 int calc_helpers;
159 unsigned int loop_mode;
160 int use_timeouts;
161 int conn_lookup;
162 int conn_setvalue;
163 int conn_info;
164 int conn_option;
165 int quiesce;
166 unsigned int suspend_mode;
167 int allow_upgrade;
168 unsigned int upgrade_action;
169 uint8_t hdr_seed;
170 uint8_t crc_seed;
171};
172
173static struct fuzz_cfg cfg;
174
176static char nonce_val[192];
177static size_t nonce_len;
178
181static size_t expect_body_len;
182static int oracle_on;
183
185static unsigned long stat_daemons;
186static unsigned long stat_handler_calls;
187static unsigned long stat_final_calls;
188static unsigned long stat_body_bytes;
189static unsigned long stat_challenges;
190static unsigned long stat_auth_ok;
192
193
194static void
196{
197 if (! fuzz_verbose)
198 return;
199 fprintf (stderr,
200 "%s: daemons=%lu handler calls=%lu (final=%lu) body bytes=%lu "
201 "401 challenges=%lu authenticated=%lu\n",
205}
206
207
210static size_t resp_len;
211
212static const size_t mem_limit_tbl[] = {
213 0 /* MHD default */, 128, 192, 256, 320, 384, 512, 768, 1024, 1400, 1500,
214 2048, 4096, 32768
215};
216
217static const int discipline_tbl[] = { -3, -2, -1, 0, 1, 2 };
218
226static int min_discipline = -3;
228
235static int forced_shape = -1;
237
246static size_t min_mem_limit;
248
255
256static const unsigned int nnc_tbl[] = { 4, 1, 8, 64 };
257
259static const char digest_rnd[32] =
260 "\x01\x23\x45\x67\x89\xab\xcd\xef\x01\x23\x45\x67\x89\xab\xcd\xef"
261 "\xfe\xdc\xba\x98\x76\x54\x32\x10\xfe\xdc\xba\x98\x76\x54\x32\x10";
262
263
264/* ------------------------------------------------------------------ */
265/* Configuration byte 4: response construction */
266/* ------------------------------------------------------------------ */
267
268/* Defined with the access handler further down; decorate_response()
269 hands it to MHD_get_response_headers(). */
270static enum MHD_Result
271kv_iter (void *cls,
272 enum MHD_ValueKind kind,
273 const char *key,
274 const char *value);
275
277static struct MHD_Daemon *cur_daemon;
278
293
301static int tearing_down;
302
303
309static void
311{
312 unsigned int i;
313
314 for (i = 0; i < MAX_CONNECTIONS; i++)
315 {
316 if (NULL == pending_resume[i])
317 {
318 pending_resume[i] = c;
319 return;
320 }
321 }
322}
323
324
326static void
328{
329 unsigned int i;
330
331 for (i = 0; i < MAX_CONNECTIONS; i++)
332 if (pending_resume[i] == c)
333 pending_resume[i] = NULL;
334}
335
336
343static int
345{
346 unsigned int i;
347 int any = 0;
348
349 for (i = 0; i < MAX_CONNECTIONS; i++)
350 {
351 struct MHD_Connection *c = pending_resume[i];
352
353 if (NULL == c)
354 continue;
355 /* Clear first: MHD_resume_connection() can run the handler, which
356 may suspend the very same connection again. */
357 pending_resume[i] = NULL;
359 any = 1;
360 }
361 return any;
362}
363
364
366static const char resp_body[] = "hello world, this is the response body";
367#define RESP_BODY_LEN (sizeof (resp_body) - 1)
368
389static const char *const hdr_names[] = {
390 "X-Fuzz", "Content-Type", "Cache-Control", "Accept-Ranges",
391 /* "\x01" is split from the rest so that the hex escape stops after
392 one digit instead of swallowing the following "Bad". */
393 "Connection", "Set-Cookie", "X-\x01" "Bad", "", "Trailer", "Date",
394 "X-Fuzz", "Server", "Location", "ETag", "Vary", "Age"
395};
396
397static const char *const hdr_values[] = {
398 "1", "text/plain", "no-cache", "bytes", "keep-alive", "a=b",
399 "x\r\nInjected: yes", "", "X-Fuzz", "Thu, 01 Jan 1970 00:00:00 GMT",
400 "\x7f", "mhd", "/", "\"tag\"", "*", "0"
401};
402
403#define HDR_COUNT (sizeof (hdr_names) / sizeof (hdr_names[0]))
404
405
411struct crc_state
412{
413 uint64_t total;
414 int calls;
415 int err_at;
416 unsigned int pattern;
417};
418
419
420static ssize_t
421crc_cb (void *cls,
422 uint64_t pos,
423 char *buf,
424 size_t max)
425{
426 struct crc_state *st = (struct crc_state *) cls;
427 size_t n;
428 size_t i;
429
430 st->calls++;
431 /* Deliberately reachable: an application content reader is allowed to
432 fail half way through a response body, and how MHD terminates the
433 connection in that case (especially a chunked one that can no
434 longer be framed correctly) is worth exercising. */
435 if ( (0 <= st->err_at) &&
436 (st->calls > st->err_at) )
438 if (pos >= st->total)
440 n = (size_t) (st->total - pos);
441 if (n > max)
442 n = max;
443 if (0 == n)
445 for (i = 0; i < n; i++)
446 buf[i] = (char) ('a' + (int) ((pos + i + st->pattern) % 26));
447 return (ssize_t) n;
448}
449
450
451static void
452crc_free (void *cls)
453{
454 free (cls);
455}
456
457
469static void
470upgrade_cb (void *cls,
471 struct MHD_Connection *connection,
472 void *req_cls,
473 const char *extra_in,
474 size_t extra_in_size,
475 MHD_socket sock,
476 struct MHD_UpgradeResponseHandle *urh)
477{
478 volatile size_t sink = 0;
479
480 (void) cls;
481 (void) connection;
482 (void) req_cls;
483 (void) sock;
484 if ( (NULL != extra_in) &&
485 (0 != extra_in_size) )
486 sink += (size_t) (unsigned char) extra_in[extra_in_size - 1];
487 (void) sink;
488 if (0 != (cfg.upgrade_action & 0x01u))
490 if (0 != (cfg.upgrade_action & 0x02u))
493}
494
495
503static int
505{
506#ifdef FUZZ_HAVE_MEMFD
507 int fd;
508
509 fd = memfd_create ("mhd-fuzz", MFD_CLOEXEC);
510 if (0 > fd)
511 return -1;
512 if (RESP_BODY_LEN != (size_t) write (fd, resp_body, RESP_BODY_LEN))
513 {
514 (void) close (fd);
515 return -1;
516 }
517 return fd;
518#else
519 return -1;
520#endif
521}
522
523
528static int
530{
531 int pf[2];
532
533 if (0 != pipe (pf))
534 return -1;
535 if (RESP_BODY_LEN != (size_t) write (pf[1], resp_body, RESP_BODY_LEN))
536 {
537 (void) close (pf[0]);
538 (void) close (pf[1]);
539 return -1;
540 }
541 (void) close (pf[1]);
542 return pf[0];
543}
544
545
554static void
556{
557 unsigned int i;
558
559 for (i = 0; i < cfg.resp_nhdr; i++)
560 {
561 unsigned int k = (cfg.hdr_seed + i) % HDR_COUNT;
562
564 }
565 if (cfg.resp_footer)
566 {
567 unsigned int k = (unsigned int) (cfg.hdr_seed + 1u) % HDR_COUNT;
568
570 }
571 if (cfg.resp_hdr_api)
572 {
573 unsigned int k = cfg.hdr_seed % HDR_COUNT;
574 volatile size_t sink = 0;
575 const char *v;
576
578 if (NULL != v)
579 sink += strlen (v);
580 (void) sink;
583 (void) MHD_set_response_options (r,
584 (enum MHD_ResponseFlags)
585 (((cfg.hdr_seed & 0x01) ?
587 | ((cfg.hdr_seed & 0x02) ?
589 | ((cfg.hdr_seed & 0x04) ?
591 : 0)
592 | ((cfg.hdr_seed & 0x08) ?
594 MHD_RO_END);
595 }
596}
597
598
609static struct MHD_Response *
611{
612 struct MHD_Response *r = NULL;
613 struct crc_state *st;
614 void *heap;
615 int fd;
616
617 switch (cfg.resp_kind)
618 {
619 case 0:
620 /* The two responses the harness used before byte 4 existed. Bit
621 0x20 of byte 1 (cfg.chunked_reply) selects between them and has
622 no other effect, so keeping the pair here is what keeps that bit
623 meaningful. */
624 if (cfg.chunked_reply)
625 r = MHD_create_response_from_buffer_copy (11, "hello world");
626 else
628 break;
629 case 1:
631 break;
632 case 2:
634 break;
635 case 3:
637 (void *) (intptr_t) resp_body,
639 break;
640 case 4:
641 heap = malloc (RESP_BODY_LEN);
642 if (NULL == heap)
643 return NULL;
644 memcpy (heap, resp_body, RESP_BODY_LEN);
646 heap,
648 if (NULL == r)
649 free (heap);
650 break;
651 case 5:
653 (void *) (intptr_t) resp_body,
655 break;
656 case 6:
657 heap = malloc (RESP_BODY_LEN);
658 if (NULL == heap)
659 return NULL;
660 memcpy (heap, resp_body, RESP_BODY_LEN);
662 heap,
663 &free);
664 if (NULL == r)
665 free (heap);
666 break;
667 case 7:
669 (void *) (intptr_t) resp_body,
670 0 /* must_free */,
671 1 /* must_copy */);
672 break;
673 case 8:
674 heap = malloc (RESP_BODY_LEN);
675 if (NULL == heap)
676 return NULL;
677 memcpy (heap, resp_body, RESP_BODY_LEN);
679 heap,
680 1 /* must_free */,
681 0 /* must_copy */);
682 if (NULL == r)
683 free (heap);
684 break;
685 case 9:
686 case 10:
687 st = (struct crc_state *) calloc (1, sizeof (struct crc_state));
688 if (NULL == st)
689 return NULL;
690 st->total = (uint64_t) (1u + (cfg.crc_seed & 0x7Fu));
691 st->pattern = cfg.crc_seed;
692 /* Injecting a reader error is only interesting some of the time;
693 otherwise the body would rarely complete. */
694 st->err_at = (0 != (cfg.crc_seed & 0x80u))
695 ? (int) (1u + (cfg.crc_seed & 0x03u))
696 : -1;
698 (9 == cfg.resp_kind) ? st->total : MHD_SIZE_UNKNOWN,
699 (size_t) (16u + (cfg.crc_seed & 0x3Fu)),
700 &crc_cb,
701 st,
702 &crc_free);
703 if (NULL == r)
704 free (st);
705 break;
706 case 11:
707 case 12:
708 case 13:
709 fd = make_memfd ();
710 if (0 > fd)
711 {
713 break;
714 }
715 if (11 == cfg.resp_kind)
717 else if (12 == cfg.resp_kind)
718 /* Parenthesised so that the deprecated *function* is called and
719 not the same-named macro that redirects to the 64 bit variant;
720 reaching the v1 entry point is the whole point here. */
722 RESP_BODY_LEN / 2,
723 fd,
724 (off_t) (RESP_BODY_LEN / 4));
725 else
727 if (NULL == r)
728 (void) close (fd);
729 break;
730 case 14:
731 fd = make_pipe_fd ();
732 if (0 > fd)
733 {
735 break;
736 }
738 if (NULL == r)
739 (void) close (fd);
740 break;
741 case 15:
742 default:
743 {
744 static struct MHD_IoVec iov[3];
745
746 /* Static data, so no free callback is needed; MHD copies the
747 array itself (see the documentation of the @a iov parameter). */
748 iov[0].iov_base = resp_body;
749 iov[0].iov_len = RESP_BODY_LEN / 2;
750 iov[1].iov_base = resp_body + (RESP_BODY_LEN / 2);
751 iov[1].iov_len = RESP_BODY_LEN - (RESP_BODY_LEN / 2);
752 iov[2].iov_base = resp_body;
753 iov[2].iov_len = (size_t) (cfg.crc_seed % (RESP_BODY_LEN + 1));
755 (0 != iov[2].iov_len) ? 3u : 2u,
756 NULL,
757 NULL);
758 }
759 break;
760 }
761 if (NULL != r)
763 return r;
764}
765
766
767/* ------------------------------------------------------------------ */
768/* Access handler */
769/* ------------------------------------------------------------------ */
770
771struct hstate
772{
773 size_t body_off;
774 struct MHD_PostProcessor *pp;
775 int pp_tried;
776};
777
778
779static enum MHD_Result
781 enum MHD_ValueKind kind,
782 const char *key,
783 const char *value)
784{
785 volatile size_t sink = 0;
786
787 (void) cls;
788 (void) kind;
789 if (NULL != key)
790 sink += strlen (key);
791 if (NULL != value)
792 sink += strlen (value);
793 (void) sink;
794 return MHD_YES;
795}
796
797
804static enum MHD_Result
806 enum MHD_ValueKind kind,
807 const char *key,
808 size_t key_size,
809 const char *value,
810 size_t value_size)
811{
812 volatile size_t sink = 0;
813
814 (void) cls;
815 (void) kind;
816 if ( (NULL != key) && (0 != key_size) )
817 sink += (size_t) (unsigned char) key[key_size - 1];
818 if ( (NULL != value) && (0 != value_size) )
819 sink += (size_t) (unsigned char) value[value_size - 1];
820 (void) sink;
821 return MHD_YES;
822}
823
824
825static enum MHD_Result
827 enum MHD_ValueKind kind,
828 const char *key,
829 const char *filename,
830 const char *content_type,
831 const char *transfer_encoding,
832 const char *data,
833 uint64_t off,
834 size_t size)
835{
836 volatile size_t sink = 0;
837
838 (void) cls;
839 (void) kind;
840 (void) off;
841 if (NULL != key)
842 sink += strlen (key);
843 if (NULL != filename)
844 sink += strlen (filename);
845 if (NULL != content_type)
846 sink += strlen (content_type);
847 if (NULL != transfer_encoding)
848 sink += strlen (transfer_encoding);
849 if ( (NULL != data) && (0 != size) )
850 sink += (size_t) (unsigned char) data[size - 1];
851 (void) sink;
852 return MHD_YES;
853}
854
855
864static void
865oracle_check_body (struct hstate *hs,
866 const char *data,
867 size_t size)
868{
869 if ( (! oracle_on) ||
870 (0 == size) )
871 return;
872 if ( (hs->body_off + size > expect_body_len) ||
873 (0 != memcmp (expect_body + hs->body_off, data, size)) )
875 "request body desync: the bytes MHD delivered to the application "
876 "differ from the body encoded in the generated request "
877 "(chunked/Content-Length framing bug, cf. HTTP request smuggling)");
878 hs->body_off += size;
879}
880
881
882/* ------------------------------------------------------------------ */
883/* Configuration byte 5: authentication entry points */
884/* ------------------------------------------------------------------ */
885
892
903
905#define DAUTH_CHALLENGE 0
906#define DAUTH_PASSED 1
907#define DAUTH_STALE 2
908
909
923static void
925{
926 enum MHD_DigestAuthAlgo3 a = algo3_tbl[cfg.hdr_seed
927 % (sizeof (algo3_tbl)
928 / sizeof (algo3_tbl[0]))];
929 size_t hs;
930 size_t claim;
931 void *bin;
932 char *hex;
933
935 if ( (0 == hs) ||
936 (hs > MAX_DIGEST_BIN) )
937 return;
938 claim = hs - (size_t) (cfg.crc_seed % (unsigned int) (hs + 1u));
939
940 bin = malloc (claim);
941 if (NULL != bin)
942 {
947 bin,
948 claim);
952 bin,
953 claim);
954 free (bin);
955 }
956 /* The hex form needs 2*hs+1 bytes; ask for less about half the time. */
957 claim = (2u * hs + 1u)
958 - (size_t) (cfg.hdr_seed % (unsigned int) (2u * hs + 2u));
959 hex = (char *) malloc (claim);
960 if (NULL != hex)
961 {
965 hex,
966 claim);
967 free (hex);
968 }
969}
970
971
984static int
986{
987 unsigned int variant = cfg.dauth_variant;
988 enum MHD_DigestAuthResult dres;
989 uint8_t ud[MAX_DIGEST_BIN];
990 size_t hs;
991 int r1;
992 volatile size_t sink = 0;
993
994 switch (variant)
995 {
996 case 1:
1001 0);
1002 if (MHD_YES == r1)
1003 return DAUTH_PASSED;
1005 case 2:
1010 0,
1011 algo1_tbl[cfg.hdr_seed & 0x03]);
1012 if (MHD_YES == r1)
1013 return DAUTH_PASSED;
1015 case 3:
1016 /* MHD_digest_auth_check_digest() is MD5-only by definition. */
1021 break;
1025 ud,
1026 0);
1027 if (MHD_YES == r1)
1028 return DAUTH_PASSED;
1030 case 4:
1031 case 5:
1032 {
1033 /* The pre-computed-digest entry points are stricter than the
1034 password-based ones: the digest and the algorithm have to
1035 agree, and naming more than one base hashing algorithm is an
1036 MHD_PANIC("API violation") rather than an error return. So the
1037 algorithm is chosen first here and the digest, its length and
1038 the algorithm argument are all derived from that one choice.
1039
1040 MHD_DIGEST_ALG_AUTO is deliberately not used for variant 4:
1041 MHD_digest_auth_check_digest2() maps it to
1042 MULT_ALGO3_ANY_NON_SESSION, which names three base algorithms
1043 and therefore trips that very panic. See README section 6. */
1044 static const enum MHD_DigestAuthAlgo3 digest_algos[] = {
1051 };
1052 enum MHD_DigestAuthAlgo3 a3;
1053
1054 if (4 == variant)
1055 /* check_digest2() only knows MD5 and SHA-256. */
1056 a3 = (0 != (cfg.hdr_seed & 0x01))
1059 else
1060 a3 = digest_algos[cfg.hdr_seed
1061 % (sizeof (digest_algos)
1062 / sizeof (digest_algos[0]))];
1063 hs = MHD_digest_get_hash_size (a3);
1064 if ( (0 == hs) ||
1065 (hs > sizeof (ud)) ||
1070 ud,
1071 hs)) )
1072 break;
1073 if (4 == variant)
1074 {
1078 ud,
1079 hs,
1080 0,
1084 if (MHD_YES == r1)
1085 return DAUTH_PASSED;
1087 }
1091 ud,
1092 hs,
1093 0,
1094 0,
1095 cfg.qop,
1096 /* Exactly one base algorithm.
1097 The MULT_ALGO3_* constants are
1098 numerically identical to their
1099 ALGO3_* counterparts. */
1100 (enum MHD_DigestAuthMultiAlgo3) a3);
1101 if (MHD_DAUTH_OK == dres)
1102 return DAUTH_PASSED;
1104 }
1105 case 6:
1106 {
1108
1109 if (NULL != u)
1110 {
1111 sink += strlen (u);
1112 MHD_free (u);
1113 }
1114 }
1115 break;
1116 case 7:
1117 {
1118 struct MHD_DigestAuthUsernameInfo *ui;
1119
1120 ui = MHD_digest_auth_get_username3 (connection);
1121 if (NULL != ui)
1122 {
1123 if (NULL != ui->username)
1124 sink += ui->username_len;
1125 if (NULL != ui->userhash_hex)
1126 sink += ui->userhash_hex_len;
1127 MHD_free (ui);
1128 }
1129 }
1130 break;
1131 case 8:
1132 {
1133 struct MHD_DigestAuthInfo *ai;
1134
1135 ai = MHD_digest_auth_get_request_info3 (connection);
1136 if (NULL != ai)
1137 {
1138 if (NULL != ai->username)
1139 sink += ai->username_len;
1140 if (NULL != ai->realm)
1141 sink += ai->realm_len;
1142 if (NULL != ai->opaque)
1143 sink += ai->opaque_len;
1144 if (NULL != ai->userhash_hex)
1145 sink += ai->userhash_hex_len;
1146 sink += ai->cnonce_len;
1147 sink += (size_t) ai->nc;
1148 MHD_free (ai);
1149 }
1150 }
1151 break;
1152 default:
1153 break;
1154 }
1155 (void) sink;
1156
1157 /* Variant 0, the query variants and every early break end up here. */
1158 dres = MHD_digest_auth_check3 (connection,
1162 0 /* daemon default nonce timeout */,
1163 0 /* daemon default max_nc */,
1164 cfg.qop,
1166 if (MHD_DAUTH_OK == dres)
1167 return DAUTH_PASSED;
1169}
1170
1171
1177static enum MHD_Result
1179 int stale)
1180{
1181 struct MHD_Response *resp;
1182 enum MHD_Result ret;
1183
1185 if (NULL == resp)
1186 return MHD_NO;
1187 switch (cfg.fail_variant)
1188 {
1189 case 1:
1190 ret = MHD_queue_auth_fail_response (connection,
1192 "0123456789abcdef",
1193 resp,
1194 stale ? MHD_YES : MHD_NO);
1195 break;
1196 case 2:
1197 ret = MHD_queue_auth_fail_response2 (connection,
1199 "0123456789abcdef",
1200 resp,
1201 stale ? MHD_YES : MHD_NO,
1202 algo1_tbl[cfg.hdr_seed & 0x03]);
1203 break;
1204 case 3:
1205 if (0 != (cfg.hdr_seed & 0x01))
1208 (0 != (cfg.hdr_seed
1209 & 0x02))
1210 ? MHD_YES : MHD_NO,
1211 resp);
1212 else
1213 ret = MHD_queue_basic_auth_fail_response (connection,
1215 resp);
1216 break;
1217 case 0:
1218 default:
1219 ret = MHD_queue_auth_required_response3 (connection,
1221 "0123456789abcdef" /* opaque */,
1222 "/",
1223 resp,
1224 stale ? MHD_YES : MHD_NO,
1225 cfg.qop,
1226 cfg.algo,
1227 MHD_YES /* userhash */,
1228 MHD_NO);
1229 break;
1230 }
1231 MHD_destroy_response (resp);
1232 return ret;
1233}
1234
1235
1236/* ------------------------------------------------------------------ */
1237/* Configuration byte 6: connection introspection */
1238/* ------------------------------------------------------------------ */
1239
1246static void
1248{
1249 volatile size_t sink = 0;
1250
1251 if (cfg.conn_lookup)
1252 {
1253 static const char *const keys[] = {
1254 "Host", "Content-Length", "Transfer-Encoding", "Authorization",
1255 "Cookie", "", "X-Fuzz", "connection"
1256 };
1257 const char *k = keys[cfg.hdr_seed % (sizeof (keys) / sizeof (keys[0]))];
1258 const char *v;
1259 const char *vp = NULL;
1260 size_t vlen = 0;
1261 const char *uri = NULL;
1262 size_t ulen = 0;
1263
1264 v = MHD_lookup_connection_value (connection, MHD_HEADER_KIND, k);
1265 if (NULL != v)
1266 sink += strlen (v);
1267 if (MHD_YES == MHD_lookup_connection_value_n (connection,
1269 k,
1270 strlen (k),
1271 &vp,
1272 &vlen))
1273 sink += vlen;
1274 if (MHD_YES == MHD_get_connection_URI_path_n (connection, &uri, &ulen))
1275 sink += ulen;
1276 }
1277 if (cfg.conn_setvalue)
1278 (void) MHD_set_connection_value (connection,
1280 "X-Fuzz-Added",
1281 "1");
1282 if (cfg.conn_info)
1283 {
1284 static const enum MHD_ConnectionInfoType cinfo[] = {
1293 };
1294 static const enum MHD_DaemonInfoType dinfo[] = {
1299 };
1300 unsigned int i;
1301
1302 for (i = 0; i < sizeof (cinfo) / sizeof (cinfo[0]); i++)
1303 if (NULL != MHD_get_connection_info (connection, cinfo[i]))
1304 sink++;
1305 for (i = 0; i < sizeof (dinfo) / sizeof (dinfo[0]); i++)
1306 if ( (NULL != cur_daemon) &&
1307 (NULL != MHD_get_daemon_info (cur_daemon, dinfo[i])) )
1308 sink++;
1309 }
1310 if (cfg.conn_option)
1311 (void) MHD_set_connection_option (connection,
1313 (unsigned int) (cfg.crc_seed & 0x0F));
1314 if (cfg.conn_info)
1315 {
1316 /* Constant-output accessors. They cannot fail on attacker input,
1317 but they are cheap and leaving them uncovered makes the coverage
1318 report harder to read.
1319 MHD_fini() is deliberately NOT called anywhere: it tears down
1320 process-global library state, so a single call would break every
1321 later iteration in the same process. */
1322 const char *v = MHD_get_version ();
1323
1324 if (NULL != v)
1325 sink += strlen (v);
1326 sink += (size_t) MHD_get_version_bin ();
1327 }
1328 (void) sink;
1329}
1330
1331
1343static void
1344suspend_maybe (struct MHD_Connection *connection)
1345{
1346 if ( (0 == cfg.suspend_mode) ||
1348 return;
1349 MHD_suspend_connection (connection);
1350 if (1 == cfg.suspend_mode)
1351 {
1352 MHD_resume_connection (connection);
1353 return;
1354 }
1355 pending_resume_add (connection);
1356}
1357
1358
1359static enum MHD_Result
1360ahc (void *cls,
1361 struct MHD_Connection *connection,
1362 const char *url,
1363 const char *method,
1364 const char *version,
1365 const char *upload_data,
1366 size_t *upload_data_size,
1367 void **req_cls)
1368{
1369 struct hstate *hs = (struct hstate *) *req_cls;
1370 struct MHD_Response *resp;
1371 enum MHD_Result ret;
1372 volatile size_t sink = 0;
1373 const char *conn_hdr = NULL;
1374 char conn_lc[128];
1375
1376 conn_lc[0] = '\0';
1377 (void) cls;
1378 if (NULL == hs)
1379 {
1380 hs = (struct hstate *) calloc (1, sizeof (struct hstate));
1381 if (NULL == hs)
1382 return MHD_NO;
1383 *req_cls = hs;
1384 return MHD_YES;
1385 }
1386
1388 /* Touch the parsed request line the way a real application would. */
1389 if (NULL != url)
1390 sink += strlen (url);
1391 if (NULL != method)
1392 sink += strlen (method);
1393 if (NULL != version)
1394 sink += strlen (version);
1395 (void) sink;
1396
1397 if (0 != *upload_data_size)
1398 {
1399 stat_body_bytes += (unsigned long) *upload_data_size;
1400 oracle_check_body (hs, upload_data, *upload_data_size);
1401 if (cfg.do_postproc)
1402 {
1403 if ( (NULL == hs->pp) &&
1404 (! hs->pp_tried) )
1405 {
1406 hs->pp_tried = 1;
1407 hs->pp = MHD_create_post_processor (connection,
1408 1024,
1409 &post_iter,
1410 NULL);
1411 }
1412 if (NULL != hs->pp)
1413 (void) MHD_post_process (hs->pp, upload_data, *upload_data_size);
1414 }
1415 *upload_data_size = 0;
1416 return MHD_YES;
1417 }
1418
1420 if (cfg.do_iterate)
1421 {
1422 const enum MHD_ValueKind kinds = MHD_HEADER_KIND
1426
1427 /* Alternate between the NUL-terminated iterator and the
1428 length-aware one, which are separate code paths in MHD. */
1429 if (0 != (cfg.hdr_seed & 0x10))
1430 (void) MHD_get_connection_values_n (connection, kinds, &kv_iter_n, NULL);
1431 else
1432 (void) MHD_get_connection_values (connection, kinds, &kv_iter, NULL);
1433 }
1434
1435 if (oracle_on &&
1436 (hs->body_off != expect_body_len) )
1438 "request body truncated: MHD completed the request but delivered "
1439 "fewer body bytes than the generated request contained");
1440
1441 exercise_connection_api (connection);
1442
1443 /* Lower-cased copy of the request's "Connection" header, used by the
1444 upgrade check further down. MHD's own token matcher is internal,
1445 so a plain case-folded substring test is used here; it only has to
1446 be good enough to keep the harness from queueing an upgrade
1447 response on a connection that cannot be upgraded. */
1448 if (cfg.allow_upgrade)
1449 {
1450 conn_hdr = MHD_lookup_connection_value (connection,
1453 if (NULL != conn_hdr)
1454 {
1455 size_t i;
1456
1457 for (i = 0; (i + 1 < sizeof (conn_lc)) && ('\0' != conn_hdr[i]); i++)
1458 conn_lc[i] = (char) (( ('A' <= conn_hdr[i]) && ('Z' >= conn_hdr[i]))
1459 ? (conn_hdr[i] - 'A' + 'a') : conn_hdr[i]);
1460 conn_lc[i] = '\0';
1461 }
1462 }
1463
1464 if (cfg.do_basic)
1465 {
1466 if (cfg.basic_v1)
1467 {
1468 /* The v1 getter returns two separate malloc()ed strings and is
1469 the one that historically had to be freed by hand. */
1470 char *pass = NULL;
1471 char *user;
1472
1473 user = MHD_basic_auth_get_username_password (connection, &pass);
1474 if (NULL != user)
1475 sink += strlen (user);
1476 if (NULL != pass)
1477 sink += strlen (pass);
1478 if (NULL != user)
1479 MHD_free (user);
1480 if (NULL != pass)
1481 MHD_free (pass);
1482 }
1483 else
1484 {
1485 struct MHD_BasicAuthInfo *bai;
1486
1487 bai = MHD_basic_auth_get_username_password3 (connection);
1488 if (NULL != bai)
1489 {
1490 if (NULL != bai->username)
1491 sink += bai->username_len;
1492 if (NULL != bai->password)
1493 sink += bai->password_len;
1494 MHD_free (bai);
1495 }
1496 }
1497 }
1498
1499 if (cfg.calc_helpers)
1501
1502 if (cfg.do_digest)
1503 {
1504 int dres = run_digest_check (connection);
1505
1506 if (DAUTH_PASSED == dres)
1507 stat_auth_ok++;
1508 else
1509 {
1511 return queue_auth_challenge (connection, DAUTH_STALE == dres);
1512 }
1513 }
1514
1515 /* Only answer 101 for a request that really is an upgrade request:
1516 HTTP/1.1, an "Upgrade" header, and a "Connection" header naming the
1517 upgrade token (RFC 9110 section 7.8). This is the check a real
1518 application performs, and all three inputs come off the wire, so
1519 the path stays fully attacker-driven.
1520
1521 It is deliberately NOT enough to predict whether MHD will accept
1522 the upgrade, and it is not meant to be: MHD may have decided during
1523 header parsing that the connection must close, and does not expose
1524 that decision. That used to trip
1525 mhd_assert (NULL == r->upgrade_handler ||
1526 MHD_CONN_MUST_UPGRADE == c->keepalive)
1527 in build_header_response() -- finding K7, see README section 6,
1528 reproducer corpus/known-findings/K7-upgrade-after-must-close.bin --
1529 and MHD_queue_response() now returns MHD_NO for it instead, which
1530 the code below already handles. Do not "fix" this by tightening
1531 the condition here: the check above is what an application can
1532 actually do, and the harness has to behave like one. */
1533 if (cfg.allow_upgrade &&
1534 (NULL != version) &&
1535 (0 == strcmp (version, MHD_HTTP_VERSION_1_1)) &&
1536 (NULL != MHD_lookup_connection_value (connection,
1539 (NULL != conn_hdr) &&
1540 (NULL != strstr (conn_lc, "upgrade")) &&
1541 (NULL == strstr (conn_lc, "close")))
1542 {
1544 if (NULL != resp)
1545 {
1546 /* Mandatory: MHD_create_response_for_upgrade() documents that the
1547 upgrade headers are the application's job, and
1548 MHD_response_execute_upgrade_() asserts that "Upgrade" is
1549 present before it hands the socket over. Omitting it is an
1550 application-contract violation, not something worth fuzzing. */
1551 (void) MHD_add_response_header (resp,
1553 "fuzz-protocol");
1554 decorate_response (resp);
1555 ret = MHD_queue_response (connection,
1557 resp);
1558 MHD_destroy_response (resp);
1559 return ret;
1560 }
1561 }
1562
1563 resp = make_response ();
1564 if (NULL == resp)
1565 return MHD_NO;
1566 ret = MHD_queue_response (connection,
1567 cfg.error_reply
1569 resp);
1570 MHD_destroy_response (resp);
1571 /* Only after a response was actually accepted. Returning MHD_NO
1572 tells MHD to terminate the connection, and terminating a connection
1573 that this callback has just suspended trips
1574 mhd_assert (! connection->suspended) in MHD_connection_close_().
1575 That is the harness contradicting itself, not an MHD defect: the
1576 two requests are mutually exclusive by construction. (MHD_NO is
1577 reachable here for real -- MHD_queue_response() rejects, for
1578 instance, a response carrying MHD_RF_HEAD_ONLY_RESPONSE on a GET,
1579 which decorate_response() can set.) */
1580 if (MHD_YES == ret)
1581 suspend_maybe (connection);
1582 return ret;
1583}
1584
1585
1586static void
1587completed_cb (void *cls,
1588 struct MHD_Connection *connection,
1589 void **req_cls,
1591{
1592 struct hstate *hs = (struct hstate *) *req_cls;
1593
1594 (void) cls;
1595 (void) toe;
1596 /* MHD is done with this connection, so the deferred resume of
1597 suspend mode 2 must not fire for it any more. */
1598 pending_resume_drop (connection);
1599 if (NULL == hs)
1600 return;
1601 if (NULL != hs->pp)
1602 (void) MHD_destroy_post_processor (hs->pp);
1603 free (hs);
1604 *req_cls = NULL;
1605}
1606
1607
1608static void
1609panic_cb (void *cls,
1610 const char *file,
1611 unsigned int line,
1612 const char *reason)
1613{
1614 char msg[512];
1615
1616 (void) cls;
1617 (void) snprintf (msg, sizeof (msg),
1618 "MHD_PANIC() reached from network input at %s:%u: %s",
1619 (NULL != file) ? file : "?",
1620 line,
1621 (NULL != reason) ? reason : "?");
1622 fuzz_report_finding (msg);
1623}
1624
1625
1626/* ------------------------------------------------------------------ */
1627/* Driving the daemon */
1628/* ------------------------------------------------------------------ */
1629
1634static void
1636{
1637 for (;;)
1638 {
1639 ssize_t n;
1640 char tmp[4096];
1641
1642 n = recv (sock, tmp, sizeof (tmp), MSG_DONTWAIT);
1643 if (0 >= n)
1644 break;
1645 if (resp_len + (size_t) n < RESP_BUF_SIZE)
1646 {
1647 memcpy (resp_buf + resp_len, tmp, (size_t) n);
1648 resp_len += (size_t) n;
1649 resp_buf[resp_len] = '\0';
1650 }
1651 }
1652 if (0 != resp_len)
1653 {
1654 const char *p = resp_buf;
1655
1656 while (NULL != (p = strstr (p, "nonce=\"")))
1657 {
1658 const char *s = p + 7;
1659 const char *e = strchr (s, '"');
1660
1661 p = s;
1662 if (NULL == e)
1663 break;
1664 if ((size_t) (e - s) < sizeof (nonce_val))
1665 {
1666 memcpy (nonce_val, s, (size_t) (e - s));
1667 nonce_len = (size_t) (e - s);
1668 nonce_val[nonce_len] = '\0';
1669 }
1670 }
1671 }
1672}
1673
1674
1691static void
1693{
1694 fd_set rs;
1695 fd_set ws;
1696 fd_set es;
1698 struct timeval tv;
1699
1700 if (0 == cfg.loop_mode)
1701 {
1702 (void) MHD_run (d);
1703 return;
1704 }
1705 FD_ZERO (&rs);
1706 FD_ZERO (&ws);
1707 FD_ZERO (&es);
1708 if (1 == cfg.loop_mode)
1709 {
1710 /* Parenthesised so that the real v1 function is called:
1711 microhttpd.h also defines MHD_get_fdset as a macro that forwards
1712 to MHD_get_fdset2 with FD_SETSIZE, so the unparenthesised name
1713 would never reach the v1 entry point at all. Same trick for
1714 MHD_run_from_select below. */
1715 if (MHD_YES != (MHD_get_fdset) (d, &rs, &ws, &es, &max_fd))
1716 {
1717 (void) MHD_run (d);
1718 return;
1719 }
1720 }
1721 else
1722 {
1723 if (MHD_YES != MHD_get_fdset2 (d, &rs, &ws, &es, &max_fd,
1724 (unsigned int) FD_SETSIZE))
1725 {
1726 (void) MHD_run (d);
1727 return;
1728 }
1729 }
1730 if (cfg.use_timeouts)
1731 {
1733 uint64_t t64 = 0;
1734 volatile int64_t sink;
1735
1736 (void) MHD_get_timeout (d, &tl);
1737 (void) MHD_get_timeout64 (d, &t64);
1738 sink = MHD_get_timeout64s (d);
1739 sink += (int64_t) MHD_get_timeout_i (d);
1740 (void) sink;
1741 }
1742 tv.tv_sec = 0;
1743 tv.tv_usec = 0;
1744 if (MHD_INVALID_SOCKET != max_fd)
1745 (void) select ((int) max_fd + 1, &rs, &ws, &es, &tv);
1746 if (1 == cfg.loop_mode)
1747 (void) (MHD_run_from_select) (d, &rs, &ws, &es);
1748 else
1749 (void) MHD_run_from_select2 (d, &rs, &ws, &es, (unsigned int) FD_SETSIZE);
1750}
1751
1752
1753static void
1754pump (struct MHD_Daemon *d,
1755 int sock,
1756 unsigned int rounds)
1757{
1758 unsigned int i;
1759
1760 for (i = 0; i < rounds; i++)
1761 {
1762 /* Deferred resume of suspend mode 2. Each slot is cleared before
1763 its connection is resumed, which keeps this correct even if the
1764 resume makes MHD complete (and forget) the connection, or run the
1765 handler again so that it suspends the same one anew. */
1766 (void) pending_resume_flush ();
1767 run_once (d);
1768 drain_and_harvest (sock);
1769 }
1770}
1771
1772
1779static size_t
1780expand_nonce (const uint8_t *in,
1781 size_t in_len,
1782 uint8_t *out,
1783 size_t out_cap)
1784{
1785 static const char tok[] = "%%NONCE%%";
1786 const size_t tok_len = sizeof (tok) - 1;
1787 size_t i = 0;
1788 size_t o = 0;
1789
1790 while (i < in_len)
1791 {
1792 if ( (i + tok_len <= in_len) &&
1793 (0 == memcmp (in + i, tok, tok_len)) )
1794 {
1795 if (o + nonce_len > out_cap)
1796 break;
1797 memcpy (out + o, nonce_val, nonce_len);
1798 o += nonce_len;
1799 i += tok_len;
1800 continue;
1801 }
1802 if (o >= out_cap)
1803 break;
1804 out[o++] = in[i++];
1805 }
1806 return o;
1807}
1808
1809
1810static void
1812 int sock,
1813 const uint8_t *data,
1814 size_t len)
1815{
1816 size_t off = 0;
1817 unsigned int stall = 0;
1818
1819 while ( (off < len) &&
1820 (stall < 64) )
1821 {
1822 ssize_t s = send (sock, data + off, len - off, MSG_DONTWAIT);
1823
1824 if (0 < s)
1825 {
1826 off += (size_t) s;
1827 stall = 0;
1828 continue;
1829 }
1830 stall++;
1831 pump (d, sock, 2);
1832 if ( (0 > s) &&
1833 (EAGAIN != errno) &&
1834 (EWOULDBLOCK != errno) &&
1835 (EINTR != errno) )
1836 break;
1837 }
1838}
1839
1840
1841static int
1843 int *sock)
1844{
1845 int sv[2];
1846 struct sockaddr_in sa;
1847
1848 if (0 != socketpair (AF_UNIX, SOCK_STREAM, 0, sv))
1849 return -1;
1850 memset (&sa, 0, sizeof (sa));
1851 sa.sin_family = AF_INET;
1852 sa.sin_port = htons (44444);
1853 sa.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
1854 if (MHD_YES != MHD_add_connection (d,
1855 (MHD_socket) sv[1],
1856 (const struct sockaddr *) &sa,
1857 (socklen_t) sizeof (sa)))
1858 {
1859 /* MHD has already closed sv[1] in that case */
1860 (void) close (sv[0]);
1861 return -1;
1862 }
1863 *sock = sv[0];
1864 return 0;
1865}
1866
1867
1868static void
1870 int *sock)
1871{
1872 if (0 > *sock)
1873 return;
1874 (void) shutdown (*sock, SHUT_WR);
1875 pump (d, *sock, 4);
1876 (void) close (*sock);
1877 *sock = -1;
1878}
1879
1880
1881/* ------------------------------------------------------------------ */
1882/* The fuzz target */
1883/* ------------------------------------------------------------------ */
1884
1885int
1887 size_t size)
1888{
1889 struct MHD_Daemon *d;
1890 struct MHD_OptionItem opts[8];
1891 unsigned int nopt = 0;
1892 unsigned int flags;
1893 int sock = -1;
1894 size_t pos;
1895 unsigned int nseg = 0;
1896 unsigned int nconn = 1;
1897 static uint8_t xbuf[FUZZ_MAX_INPUT + 4096];
1898
1899 /* Must happen before the first write() into the socketpair. The
1900 built-in driver also does this from fuzz_install_handlers(), but
1901 that is compiled out under -DFUZZ_NO_MAIN, which is exactly the
1902 build every external fuzzing engine uses; the call is idempotent,
1903 so doing it from both places is harmless. See fuzz_ignore_sigpipe()
1904 in fuzz_common.h for why the process dies without it. */
1906
1907 /* The ten configuration bytes are mandatory. Anything shorter has
1908 no segment stream either, so there is nothing to fuzz. */
1909 if (size < 10)
1910 return 0;
1911
1912 nonce_len = 0;
1913 nonce_val[0] = '\0';
1914 resp_len = 0;
1915 resp_buf[0] = '\0';
1916
1917 memset (&cfg, 0, sizeof (cfg));
1918 cfg.mem_limit =
1919 mem_limit_tbl[data[0] % (sizeof (mem_limit_tbl)
1920 / sizeof (mem_limit_tbl[0]))];
1921 cfg.do_digest = (0 != (data[1] & 0x01));
1922 cfg.do_basic = (0 != (data[1] & 0x02));
1923 cfg.do_postproc = (0 != (data[1] & 0x04));
1924 cfg.do_iterate = (0 != (data[1] & 0x08));
1925 /* Bits 0x10 and 0x80 of byte 1 are unused. 0x10 used to gate the
1926 configuration bytes 4-9 back when they were optional; it is left
1927 free rather than reassigned so that the meaning of the corpus
1928 files written while it was a gate does not silently change. */
1929 cfg.chunked_reply = (0 != (data[1] & 0x20));
1930 cfg.error_reply = (0 != (data[1] & 0x40));
1931 if (! min_mem_limit_read)
1932 {
1933 const char *e = getenv ("MHD_FUZZ_MIN_MEM_LIMIT");
1934
1936 if (NULL != e)
1937 min_mem_limit = (size_t) strtoul (e, NULL, 10);
1938 }
1939 if ( (0 != cfg.mem_limit) &&
1940 (cfg.mem_limit < min_mem_limit) )
1941 cfg.mem_limit = min_mem_limit;
1942 if (! min_discipline_read)
1943 {
1944 const char *e = getenv ("MHD_FUZZ_MIN_DISCIPLINE");
1945
1947 if (NULL != e)
1948 min_discipline = atoi (e);
1949 }
1950 cfg.discipline =
1951 discipline_tbl[(data[2] & 0x0F) % (sizeof (discipline_tbl)
1952 / sizeof (discipline_tbl[0]))];
1953 if (cfg.discipline < min_discipline)
1954 cfg.discipline = min_discipline;
1955 cfg.insanity = (unsigned int) MHD_DSC_SANE;
1956 cfg.algo = algo_tbl[data[3] & 0x03];
1957 cfg.qop = (0 != (data[3] & 0x04))
1960 cfg.nonce_nc_size = nnc_tbl[(data[3] >> 4) & 0x03];
1961
1962 cfg.resp_kind = (unsigned int) (data[4] & 0x0F);
1963 cfg.resp_nhdr = (unsigned int) ((data[4] >> 4) & 0x03);
1964 cfg.resp_footer = (0 != (data[4] & 0x40));
1965 cfg.resp_hdr_api = (0 != (data[4] & 0x80));
1966
1967 cfg.dauth_variant = (unsigned int) (data[5] & 0x0F) % DAUTH_VARIANT_COUNT;
1968 cfg.fail_variant = (unsigned int) ((data[5] >> 4) & 0x03);
1969 cfg.basic_v1 = (0 != (data[5] & 0x40));
1970 cfg.calc_helpers = (0 != (data[5] & 0x80));
1971
1972 cfg.loop_mode = (unsigned int) (data[6] & 0x03);
1973 cfg.use_timeouts = (0 != (data[6] & 0x04));
1974 cfg.conn_lookup = (0 != (data[6] & 0x08));
1975 cfg.conn_setvalue = (0 != (data[6] & 0x10));
1976 cfg.conn_info = (0 != (data[6] & 0x20));
1977 cfg.conn_option = (0 != (data[6] & 0x40));
1978 cfg.quiesce = (0 != (data[6] & 0x80));
1979
1980 /* Only modes 0-2 exist; 3 folds onto the immediate-resume mode
1981 rather than being a fourth, unimplemented behaviour. */
1982 cfg.suspend_mode = (unsigned int) (data[7] & 0x03);
1983 if (3 == cfg.suspend_mode)
1984 cfg.suspend_mode = 1;
1985 cfg.allow_upgrade =
1986 (0 != (data[7] & 0x04)) &&
1988 cfg.upgrade_action = (unsigned int) ((data[7] >> 3) & 0x03);
1989
1990 cfg.hdr_seed = data[8];
1991 cfg.crc_seed = data[9];
1992
1993 oracle_on = 0;
1994 expect_body_len = 0;
1995 /* Must never carry over: the connections these pointed at belonged to
1996 the previous iteration's daemon and are long gone. */
1997 memset (pending_resume, 0, sizeof (pending_resume));
1998 tearing_down = 0;
1999
2000 if (0 != cfg.mem_limit)
2001 {
2003 opts[nopt].value = (intptr_t) cfg.mem_limit;
2004 opts[nopt].ptr_value = NULL;
2005 nopt++;
2006 }
2008 opts[nopt].value = (intptr_t) cfg.discipline;
2009 opts[nopt].ptr_value = NULL;
2010 nopt++;
2012 opts[nopt].value = (intptr_t) cfg.insanity;
2013 opts[nopt].ptr_value = NULL;
2014 nopt++;
2015 opts[nopt].option = MHD_OPTION_NONCE_NC_SIZE;
2016 opts[nopt].value = (intptr_t) cfg.nonce_nc_size;
2017 opts[nopt].ptr_value = NULL;
2018 nopt++;
2020 opts[nopt].value = (intptr_t) sizeof (digest_rnd);
2021 opts[nopt].ptr_value = (void *) (intptr_t) digest_rnd;
2022 nopt++;
2023 opts[nopt].option = MHD_OPTION_END;
2024 opts[nopt].value = 0;
2025 opts[nopt].ptr_value = NULL;
2026
2028 if (fuzz_verbose)
2029 flags |= MHD_USE_ERROR_LOG;
2030 if (0 != cfg.suspend_mode)
2031 flags |= MHD_ALLOW_SUSPEND_RESUME;
2032 if (cfg.allow_upgrade)
2033 flags |= MHD_ALLOW_UPGRADE;
2034
2036 d = MHD_start_daemon (flags,
2037 0,
2038 NULL, NULL,
2039 &ahc, NULL,
2040 MHD_OPTION_ARRAY, opts,
2041 /* passed through the varargs rather than through
2042 the option array: storing a function pointer in
2043 the array's intptr_t member is not strictly
2044 conforming C */
2047 if (NULL == d)
2048 return 0;
2049 cur_daemon = d;
2050 stat_daemons++;
2051 if (! stats_registered)
2052 {
2053 stats_registered = 1;
2054 (void) atexit (&print_stats);
2055 }
2056
2057 if (0 != new_connection (d, &sock))
2058 {
2059 MHD_stop_daemon (d);
2060 cur_daemon = NULL;
2061 return 0;
2062 }
2063
2064 pos = 10u;
2065 while ( (pos + 2 <= size) &&
2066 (nseg < MAX_SEGMENTS) )
2067 {
2068 unsigned int hdr = (unsigned int) data[pos]
2069 | ((unsigned int) data[pos + 1] << 8);
2070 unsigned int op = hdr >> 14;
2071 size_t slen = (size_t) (hdr & 0x3FFF);
2072 size_t elen;
2073
2074 pos += 2;
2075 nseg++;
2076 if (slen > size - pos)
2077 slen = size - pos;
2078
2079 if (1 == op)
2080 {
2081 /* Ground-truth declaration, not wire data. */
2082 if (fuzz_pristine && (slen <= MAX_EXPECT_BODY))
2083 {
2084 memcpy (expect_body, data + pos, slen);
2085 expect_body_len = slen;
2086 oracle_on = 1;
2087 }
2088 pos += slen;
2089 continue;
2090 }
2091 if ( (3 == op) &&
2092 (nconn < MAX_CONNECTIONS) )
2093 {
2094 close_connection (d, &sock);
2095 if (0 != new_connection (d, &sock))
2096 break;
2097 nconn++;
2098 }
2099
2100 elen = expand_nonce (data + pos, slen, xbuf, sizeof (xbuf));
2101 pos += slen;
2102 if (0 != elen)
2103 send_all (d, sock, xbuf, elen);
2104 pump (d, sock, (2 == op) ? 12u : 3u);
2105 if (0 == slen)
2106 pump (d, sock, 4);
2107 }
2108
2109 close_connection (d, &sock);
2110 pump (d, sock, 4);
2111 /* A connection left suspended makes MHD_stop_daemon() MHD_PANIC()
2112 ("called while we have suspended connections"), and
2113 MHD_resume_connection() alone is not enough: it only raises a flag,
2114 and the connection is taken off the daemon's suspended list by
2115 MHD_run(). So flush and run until nothing is parked any more.
2116 tearing_down stops the handler from parking anything new, which is
2117 what bounds this loop; the cap is only a backstop. */
2118 tearing_down = 1;
2119 {
2120 unsigned int i;
2121
2122 for (i = 0; i < MAX_CONNECTIONS + 2u; i++)
2123 {
2124 if (! pending_resume_flush ())
2125 break;
2126 (void) MHD_run (d);
2127 }
2128 }
2129 if (cfg.quiesce)
2130 (void) MHD_quiesce_daemon (d);
2131 MHD_stop_daemon (d);
2132 cur_daemon = NULL;
2133 return 0;
2134}
2135
2136
2137/* ------------------------------------------------------------------ */
2138/* Structure-aware HTTP request generator */
2139/* ------------------------------------------------------------------ */
2140
2141struct sbuf
2142{
2143 uint8_t *p;
2144 size_t len;
2145 size_t cap;
2146};
2147
2148
2149static void
2150sb_raw (struct sbuf *b,
2151 const void *v,
2152 size_t n)
2153{
2154 if (b->len + n > b->cap)
2155 n = b->cap - b->len;
2156 memcpy (b->p + b->len, v, n);
2157 b->len += n;
2158}
2159
2160
2161static void
2162sb_str (struct sbuf *b,
2163 const char *s)
2164{
2165 sb_raw (b, s, strlen (s));
2166}
2167
2168
2169static void
2170sb_u64 (struct sbuf *b,
2171 uint64_t v,
2172 int hex,
2173 unsigned int min_digits)
2174{
2175 char tmp[32];
2176 unsigned int n = 0;
2177
2178 do
2179 {
2180 unsigned int dig = (unsigned int) (v % (hex ? 16u : 10u));
2181 tmp[n++] = (char) ((dig < 10) ? ('0' + dig) : ('a' + dig - 10));
2182 v /= (hex ? 16u : 10u);
2183 }
2184 while ( (0 != v) && (n < sizeof (tmp)) );
2185 while ( (n < min_digits) && (n < sizeof (tmp)) )
2186 tmp[n++] = '0';
2187 while (0 != n)
2188 {
2189 char c = tmp[--n];
2190 sb_raw (b, &c, 1);
2191 }
2192}
2193
2194
2195static const char *const gen_methods[] = {
2196 "GET", "POST", "PUT", "HEAD", "DELETE", "OPTIONS", "TRACE", "PATCH",
2197 "CONNECT", "BREW", "get", "M-SEARCH", "\tGET", "GET "
2198};
2199
2200static const char *const gen_versions[] = {
2201 "HTTP/1.1", "HTTP/1.0", "HTTP/1.1", "HTTP/1.1", "HTTP/0.9", "HTTP/1.2",
2202 "HTTP/2.0", "http/1.1", "HTTP/1.", "HTTP/11"
2203};
2204
2205static const char *const gen_targets[] = {
2206 "/", "/a", "/a/b/c", "/a?x=1", "/a?x=1&y=2", "/?novalue", "/?a=1&b",
2207 "/?a&b&c", "/%41%42", "/a%00b", "/%zz", "/a?%41=%42", "*",
2208 "http://example.org/a", "/a?x=1&novalue", "/..%2f..%2fetc",
2209 "/a?=", "/a?&", "/very/long/path/that/keeps/going/and/going/and/going"
2210};
2211
2212static const char *const gen_hdr_names[] = {
2213 "Host", "Accept", "User-Agent", "Connection", "Cookie", "Content-Type",
2214 "Expect", "TE", "Trailer", "X-Custom", "Referer", "Accept-Encoding",
2215 "content-length", "transfer-encoding", "X-A-Very-Long-Header-Name-Indeed"
2216};
2217
2218static const char *const gen_hdr_values[] = {
2219 "example.org", "*/*", "fuzz/1.0", "keep-alive", "close",
2220 "a=1; b=2; c", "100-continue", "trailers", "X-Trail", "value",
2221 "", " ", "\ttabbed", "a, b, c", "chunked", "identity, chunked"
2222};
2223
2225static const char *const gen_algos[] = {
2226 "MD5", "SHA-256", "SHA-512-256", "MD5-sess", "SHA-256-sess",
2227 "SHA-512-256-sess", "sha-256", "SHA256", "BOGUS", "", "\"SHA-256\"",
2228 "MD5 ", "SHA-1", "xyzzy", "SHA-512", "\"BOGUS\"", "0"
2229};
2230
2231static const char *const gen_ctypes[] = {
2232 "application/x-www-form-urlencoded",
2233 "multipart/form-data; boundary=--abc",
2234 "multipart/form-data; boundary=\"XY\"",
2235 "multipart/form-data",
2236 "text/plain"
2237};
2238
2239
2245static void
2246gen_query (struct fuzz_rng *rng,
2247 struct sbuf *b,
2248 int force_trailing_novalue)
2249{
2250 unsigned int n = fuzz_below (rng, 4);
2251 unsigned int i;
2252
2253 if ( (0 == n) && (! force_trailing_novalue) )
2254 return;
2255 sb_str (b, "?");
2256 for (i = 0; i < n; i++)
2257 {
2258 if (0 != i)
2259 sb_str (b, "&");
2260 sb_str (b, "k");
2261 sb_u64 (b, i, 0, 1);
2262 if (! fuzz_chance (rng, 3))
2263 {
2264 sb_str (b, "=");
2265 sb_str (b, fuzz_chance (rng, 4) ? "%41%42" : "v");
2266 }
2267 }
2268 if (force_trailing_novalue)
2269 {
2270 if (0 != n)
2271 sb_str (b, "&");
2272 sb_str (b, "novalue");
2273 }
2274}
2275
2276
2277static void
2279 struct sbuf *b,
2280 unsigned int n)
2281{
2282 unsigned int i;
2283
2284 for (i = 0; i < n; i++)
2285 {
2287 (uint32_t) (sizeof (gen_hdr_names)
2288 / sizeof (char *)))]);
2289 sb_str (b, fuzz_chance (rng, 8) ? " :" : ":");
2290 if (! fuzz_chance (rng, 6))
2291 sb_str (b, " ");
2293 (uint32_t) (sizeof (gen_hdr_values)
2294 / sizeof (char *)))]);
2295 if (fuzz_chance (rng, 12))
2296 sb_str (b, "\r\n\tfolded-continuation");
2297 if (fuzz_chance (rng, 20))
2298 sb_str (b, "\n"); /* bare LF */
2299 else if (fuzz_chance (rng, 25))
2300 sb_str (b, "\r"); /* bare CR */
2301 else
2302 sb_str (b, "\r\n");
2303 }
2304}
2305
2306
2313static void
2315 struct sbuf *b,
2316 int oracle)
2317{
2318 unsigned int nchunks = 1 + fuzz_below (rng, 4);
2319 unsigned int i;
2320
2321 if (oracle)
2322 expect_body_len = 0;
2323 for (i = 0; i < nchunks; i++)
2324 {
2325 unsigned int clen = 1 + fuzz_below (rng, 24);
2326 unsigned int j;
2327
2328 sb_u64 (b, clen, 1, fuzz_chance (rng, 4) ? 4 : 1);
2329 /* chunk extension -- the parsing of the terminating CRLF of this
2330 very line was broken (bug #3) */
2331 if (! fuzz_chance (rng, 2))
2332 {
2333 switch (fuzz_below (rng, 5))
2334 {
2335 case 0:
2336 sb_str (b, ";ext");
2337 break;
2338 case 1:
2339 sb_str (b, ";ext=val");
2340 break;
2341 case 2:
2342 sb_str (b, ";ext=\"quoted value\"");
2343 break;
2344 case 3:
2345 sb_str (b, ";a=1;b=2;c");
2346 break;
2347 default:
2348 sb_str (b, ";x=\"a;b\"");
2349 break;
2350 }
2351 }
2352 sb_str (b, "\r\n");
2353 for (j = 0; j < clen; j++)
2354 {
2355 char c = (char) ('A' + ((i * 7 + j) % 26));
2356
2357 sb_raw (b, &c, 1);
2358 if (oracle && (expect_body_len < MAX_EXPECT_BODY))
2359 expect_body[expect_body_len++] = (uint8_t) c;
2360 }
2361 sb_str (b, "\r\n");
2362 }
2363 sb_str (b, "0");
2364 if (fuzz_chance (rng, 4))
2365 sb_str (b, ";final=\"x\"");
2366 sb_str (b, "\r\n");
2367 if (fuzz_chance (rng, 3))
2368 sb_str (b, "X-Trailer: value\r\n");
2369 sb_str (b, "\r\n");
2370}
2371
2372
2373static void
2375 struct sbuf *b,
2376 const char *uri,
2377 int use_nonce_token,
2378 int overlong_response,
2379 int prefer_valid)
2380{
2381 unsigned int nresp;
2382 unsigned int i;
2383
2384 sb_str (b, "Authorization: Digest ");
2385 sb_str (b, "username=\"");
2386 if (fuzz_chance (rng, prefer_valid ? 20 : 6))
2387 {
2388 /* userhash notation: a long hex string is accepted here too */
2389 unsigned int n = 2 * (16 + fuzz_below (rng, 48));
2390 for (i = 0; i < n; i++)
2391 sb_str (b, "a");
2392 }
2393 else
2394 sb_str (b, DIGEST_USER);
2395 sb_str (b, "\", ");
2396 if (fuzz_chance (rng, prefer_valid ? 25 : 8))
2397 sb_str (b, "userhash=true, ");
2398 sb_str (b, "realm=\"");
2399 sb_str (b, fuzz_chance (rng, prefer_valid ? 25 : 8)
2400 ? "OtherRealm" : DIGEST_REALM);
2401 sb_str (b, "\", nonce=\"");
2402 if (use_nonce_token)
2403 sb_str (b, "%%NONCE%%");
2404 else
2405 {
2406 unsigned int n = 8 + fuzz_below (rng, 80);
2407 for (i = 0; i < n; i++)
2408 sb_str (b, "0");
2409 }
2410 sb_str (b, "\", uri=\"");
2411 sb_str (b, uri);
2412 sb_str (b, "\", qop=");
2413 sb_str (b, fuzz_chance (rng, prefer_valid ? 25 : 8) ? "auth-int" : "auth");
2414 sb_str (b, ", nc=");
2415 sb_u64 (b, 1 + fuzz_below (rng, 3), 1, 8);
2416 sb_str (b, ", cnonce=\"deadbeef\", algorithm=");
2417 if (prefer_valid && (! fuzz_chance (rng, 5)))
2418 sb_str (b, "SHA-256"); /* must match the algorithm of the challenge */
2419 else
2420 sb_str (b, gen_algos[fuzz_below (rng,
2421 (uint32_t) (sizeof (gen_algos)
2422 / sizeof (char *)))]);
2423 sb_str (b, ", opaque=\"0123456789abcdef\", response=\"");
2424 if (overlong_response)
2425 nresp = 66 + 2 * fuzz_below (rng, 32); /* 66 .. 128 hex digits */
2426 else
2427 nresp = 2 * (1 + fuzz_below (rng, 66));
2428 for (i = 0; i < nresp; i++)
2429 {
2430 char c = "0123456789abcdef"[fuzz_below (rng, 16)];
2431
2432 sb_raw (b, &c, 1);
2433 }
2434 sb_str (b, "\"\r\n");
2435}
2436
2437
2451
2452
2459static int
2461 struct sbuf *b,
2462 enum gen_shape shape,
2463 int second_of_pair)
2464{
2465 switch (shape)
2466 {
2467 case SHAPE_NOHDR_QARG:
2468 /* No header lines at all + a trailing query argument without '=';
2469 combined with a small connection memory pool this is the shape
2470 that reaches the read-buffer "shift back" computation. */
2471 sb_str (b, "GET /");
2472 gen_query (rng, b, 1);
2473 sb_str (b, " ");
2474 sb_str (b, fuzz_chance (rng, 2) ? "HTTP/1.0" : "HTTP/1.1");
2475 sb_str (b, "\r\n\r\n");
2476 return 0;
2477
2478 case SHAPE_CL_BODY:
2479 {
2480 unsigned int blen = fuzz_below (rng, 64);
2481 unsigned int i;
2482
2483 sb_str (b, "POST /a HTTP/1.1\r\nHost: x\r\nContent-Length: ");
2484 sb_u64 (b, blen, 0, 1);
2485 sb_str (b, "\r\n\r\n");
2486 expect_body_len = 0;
2487 for (i = 0; i < blen; i++)
2488 {
2489 char c = (char) ('a' + (i % 26));
2490
2491 sb_raw (b, &c, 1);
2493 expect_body[expect_body_len++] = (uint8_t) c;
2494 }
2495 return 1;
2496 }
2497
2498 case SHAPE_CHUNKED:
2499 sb_str (b, "POST /a HTTP/1.1\r\nHost: x\r\n"
2500 "Transfer-Encoding: chunked\r\n\r\n");
2501 gen_chunked_body (rng, b, 1);
2502 return 1;
2503
2505 sb_str (b, "GET /a HTTP/1.1\r\nHost: x\r\n");
2506 gen_digest_header (rng, b, "/a", 0, fuzz_chance (rng, 2), 0);
2507 sb_str (b, "\r\n");
2508 return 0;
2509
2511 if (! second_of_pair)
2512 sb_str (b, "GET /a HTTP/1.1\r\nHost: x\r\n\r\n");
2513 else
2514 {
2515 sb_str (b, "GET /a HTTP/1.1\r\nHost: x\r\n");
2516 gen_digest_header (rng, b, "/a", 1, 1, 1);
2517 sb_str (b, "\r\n");
2518 }
2519 return 0;
2520
2521 case SHAPE_BASIC:
2522 sb_str (b, "GET /a HTTP/1.1\r\nHost: x\r\nAuthorization: Basic ");
2523 {
2524 unsigned int n = fuzz_below (rng, 40);
2525 unsigned int i;
2526
2527 for (i = 0; i < n; i++)
2528 {
2529 char c =
2530 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="
2531 [fuzz_below (rng, 65)];
2532
2533 sb_raw (b, &c, 1);
2534 }
2535 }
2536 sb_str (b, "\r\n\r\n");
2537 return 0;
2538
2539 case SHAPE_POST_FORM:
2540 {
2541 const char *ct = gen_ctypes[fuzz_below (rng,
2542 (uint32_t) (sizeof (gen_ctypes)
2543 / sizeof (char *)))];
2544
2545 sb_str (b, "POST /a HTTP/1.1\r\nHost: x\r\nContent-Type: ");
2546 sb_str (b, ct);
2547 sb_str (b, "\r\nTransfer-Encoding: chunked\r\n\r\n");
2548 gen_chunked_body (rng, b, 0);
2549 return 0;
2550 }
2551
2552 case SHAPE_WEIRD:
2553 sb_str (b, gen_methods[fuzz_below (rng,
2554 (uint32_t) (sizeof (gen_methods)
2555 / sizeof (char *)))]);
2556 sb_str (b, " ");
2557 sb_str (b, gen_targets[fuzz_below (rng,
2558 (uint32_t) (sizeof (gen_targets)
2559 / sizeof (char *)))]);
2560 sb_str (b, " ");
2562 (uint32_t) (sizeof (gen_versions)
2563 / sizeof (char *)))]);
2564 sb_str (b, fuzz_chance (rng, 8) ? "\n" : "\r\n");
2565 gen_headers (rng, b, fuzz_below (rng, 6));
2566 sb_str (b, "\r\n");
2567 return 0;
2568
2569 case SHAPE_PLAIN:
2570 case SHAPE_COUNT:
2571 default:
2572 sb_str (b, gen_methods[fuzz_below (rng, 3)]);
2573 sb_str (b, " /");
2574 gen_query (rng, b, fuzz_chance (rng, 3));
2575 sb_str (b, " HTTP/1.1\r\n");
2576 gen_headers (rng, b, fuzz_below (rng, 4));
2577 sb_str (b, "\r\n");
2578 return 0;
2579 }
2580}
2581
2582
2587static void
2589 struct sbuf *out,
2590 const uint8_t *body,
2591 size_t body_len,
2592 int new_conn_first)
2593{
2594 size_t off = 0;
2595 int first = 1;
2596
2597 while (off < body_len)
2598 {
2599 size_t chunk;
2600 unsigned int op;
2601 uint8_t hdr[2];
2602 unsigned int hv;
2603
2604 switch (fuzz_below (rng, 6))
2605 {
2606 case 0:
2607 chunk = 1;
2608 break;
2609 case 1:
2610 chunk = 2 + fuzz_below (rng, 6);
2611 break;
2612 case 2:
2613 chunk = 8 + fuzz_below (rng, 40);
2614 break;
2615 default:
2616 chunk = body_len - off;
2617 break;
2618 }
2619 if (chunk > body_len - off)
2620 chunk = body_len - off;
2621 if (chunk > 0x3FFF)
2622 chunk = 0x3FFF;
2623 op = (first && new_conn_first) ? 3u : (fuzz_chance (rng, 4) ? 2u : 0u);
2624 hv = (op << 14) | (unsigned int) chunk;
2625 hdr[0] = (uint8_t) (hv & 0xFF);
2626 hdr[1] = (uint8_t) (hv >> 8);
2627 if (out->len + 2 + chunk > out->cap)
2628 return;
2629 sb_raw (out, hdr, 2);
2630 sb_raw (out, body + off, chunk);
2631 off += chunk;
2632 first = 0;
2633 }
2634}
2635
2636
2637static size_t
2639 uint8_t *buf,
2640 size_t cap)
2641{
2642 struct sbuf out;
2643 uint8_t req[GEN_BUF_SIZE];
2644 struct sbuf rb;
2645 enum gen_shape shape;
2646 uint8_t cfg_bytes[4];
2647 int oracle;
2648 unsigned int nreq;
2649 unsigned int i;
2650
2651 expect_body_len = 0;
2652
2653 out.p = buf;
2654 out.len = 0;
2655 out.cap = cap;
2656
2657 if (! forced_shape_read)
2658 {
2659 const char *e = getenv ("MHD_FUZZ_SHAPE");
2660
2662 if (NULL != e)
2663 forced_shape = atoi (e);
2664 }
2665 if (0 <= forced_shape)
2666 shape = (enum gen_shape) (forced_shape % (int) SHAPE_COUNT);
2667 else
2668 shape = (enum gen_shape) fuzz_below (rng, (uint32_t) SHAPE_COUNT);
2669
2670 /* --- configuration bytes --- */
2671 cfg_bytes[0] = fuzz_byte (rng);
2672 cfg_bytes[1] = fuzz_byte (rng);
2673 cfg_bytes[2] = fuzz_byte (rng);
2674 cfg_bytes[3] = fuzz_byte (rng);
2675 switch (shape)
2676 {
2677 case SHAPE_NOHDR_QARG:
2678 /* small connection memory pool: indices 1..9 of mem_limit_tbl */
2679 cfg_bytes[0] = (uint8_t) (1 + fuzz_below (rng, 9));
2680 break;
2683 cfg_bytes[0] = (uint8_t) (fuzz_chance (rng, 2) ? 0 : 12);
2684 cfg_bytes[1] = (uint8_t) ((cfg_bytes[1] | 0x01u) & ~0x04u);
2685 /* SHA-256 challenge: 32 byte digest -> 128 hex chars pass the
2686 'response' length check while hash1_bin[] is only 32 bytes */
2687 cfg_bytes[3] = (uint8_t) ((cfg_bytes[3] & 0xF0u) | 0x00u);
2688 break;
2689 case SHAPE_POST_FORM:
2690 cfg_bytes[1] |= 0x04u;
2691 break;
2692 case SHAPE_CHUNKED:
2693 case SHAPE_CL_BODY:
2694 cfg_bytes[1] &= (uint8_t) ~0x04u; /* keep the body oracle clean */
2695 break;
2696 default:
2697 break;
2698 }
2699 cfg_bytes[1] |= 0x08u; /* always iterate the values */
2700 sb_raw (&out, cfg_bytes, 4);
2701
2702 nreq = (SHAPE_DIGEST_REPLAY == shape) ? 2u : (1u + (fuzz_chance (rng, 6)
2703 ? 1u : 0u));
2704 oracle = 0;
2705 for (i = 0; i < nreq; i++)
2706 {
2707 rb.p = req;
2708 rb.len = 0;
2709 rb.cap = sizeof (req);
2710 oracle = gen_one_request (rng, &rb, shape, (int) i);
2711 if (oracle && (1 == nreq) && (expect_body_len <= MAX_EXPECT_BODY))
2712 {
2713 /* Declare the ground truth for the body oracle (op 1). */
2714 unsigned int hv = (1u << 14) | (unsigned int) expect_body_len;
2715 uint8_t hdr[2];
2716
2717 hdr[0] = (uint8_t) (hv & 0xFF);
2718 hdr[1] = (uint8_t) (hv >> 8);
2719 sb_raw (&out, hdr, 2);
2721 }
2722 emit_segments (rng, &out, req, rb.len,
2723 (0 != i) || fuzz_chance (rng, 8));
2724 }
2725 (void) oracle;
2726 return out.len;
2727}
2728
2729
2730/* ------------------------------------------------------------------ */
2731/* Built-in seed corpus */
2732/* ------------------------------------------------------------------ */
2733
2739struct seed_part
2740{
2741 unsigned int op;
2742 const char *txt;
2743};
2744
2745struct seed_def
2746{
2747 const char *name;
2748 unsigned char cfg[4];
2749 struct seed_part parts[4];
2750 /* Configuration bytes 4-9. Declared after @e parts so that a seed
2751 which only cares about the wire data can leave the member out of
2752 its brace initialiser and get the all-zero (plainest) setting. */
2753 unsigned char ext[6];
2754};
2755
2756#define P_END { 0, NULL }
2757
2758static const struct seed_def seeds[] = {
2759 /* There is deliberately no bare "GET / HTTP/1.1" seed: every seed
2760 below opens with a request at least as simple, so a plain one adds
2761 no coverage of its own (libFuzzer -merge=1 drops it). */
2762 { "content-length-body", { 0x00, 0x08, 0x03, 0x00 },
2763 { { 1, "hello" },
2764 { 0, "POST /a HTTP/1.1\r\nHost: x\r\nContent-Length: 5\r\n\r\nhello" },
2765 P_END, P_END } },
2766
2767 /* chunk extensions: the terminating CRLF of the chunk-size line used
2768 to be left in the stream (bug #3, commit c13f4c64) */
2769 { "chunked-with-extensions", { 0x00, 0x08, 0x03, 0x00 },
2770 { { 1, "ABCDE" },
2771 { 0, "POST /a HTTP/1.1\r\nHost: x\r\nTransfer-Encoding: chunked\r\n"
2772 "\r\n2;ext=val\r\nAB\r\n3;a=\"b;c\"\r\nCDE\r\n0\r\n\r\n" },
2773 P_END, P_END } },
2774
2775 { "chunked-split", { 0x00, 0x08, 0x03, 0x00 },
2776 { { 1, "ABCDEF" },
2777 { 0, "POST /a HTTP/1.1\r\nHost: x\r\nTransfer-Encoding: chunked\r\n"
2778 "\r\n3\r\nABC\r\n3;x=\"y\"" },
2779 { 2, "\r\nDEF\r\n0\r\n\r\n" },
2780 P_END } },
2781
2782 /* small connection memory pool + trailing query argument without '='
2783 and no header lines at all (bug #2, commit 29eaa56b) */
2784 { "small-pool-trailing-query-arg", { 0x06, 0x08, 0x03, 0x00 },
2785 { { 0, "GET /?novalue HTTP/1.0\r\n\r\n" }, P_END, P_END, P_END } },
2786
2787 { "small-pool-trailing-query-arg-2", { 0x03, 0x08, 0x03, 0x00 },
2788 { { 0, "GET /?a=1&b HTTP/1.0\r\n\r\n" }, P_END, P_END, P_END } },
2789
2790 /* unrecognised 'algorithm' token -> MHD_DIGEST_AUTH_ALGO3_INVALID
2791 (bug #1, commit bd49ce93) */
2792 { "digest-unknown-algorithm", { 0x00, 0x09, 0x03, 0x00 },
2793 { { 0, "GET /a HTTP/1.1\r\nHost: x\r\nAuthorization: Digest "
2794 "username=\"user\", realm=\"TestRealm\", nonce=\"0000\", "
2795 "uri=\"/a\", algorithm=BOGUS, response=\"00\"\r\n\r\n" },
2796 P_END, P_END, P_END } },
2797
2798 /* full digest handshake: harvest the nonce from the 401 and replay it
2799 with a 128 hex digit 'response' value (bug #4, commit 5a73c1ae) */
2800 { "digest-overlong-response", { 0x00, 0x09, 0x03, 0x00 },
2801 { { 0, "GET /a HTTP/1.1\r\nHost: x\r\n\r\n" },
2802 { 3, "GET /a HTTP/1.1\r\nHost: x\r\nAuthorization: Digest "
2803 "username=\"user\", realm=\"TestRealm\", nonce=\"%%NONCE%%\", "
2804 "uri=\"/a\", qop=auth, nc=00000001, cnonce=\"deadbeef\", "
2805 "algorithm=SHA-256, response=\""
2806 "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
2807 "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
2808 "\"\r\n\r\n" },
2809 P_END, P_END } },
2810
2811 { "digest-userhash", { 0x00, 0x09, 0x03, 0x00 },
2812 { { 0, "GET /a HTTP/1.1\r\nHost: x\r\n\r\n" },
2813 { 3, "GET /a HTTP/1.1\r\nHost: x\r\nAuthorization: Digest "
2814 "username=\""
2815 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
2816 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
2817 "\", userhash=true, realm=\"TestRealm\", nonce=\"%%NONCE%%\", "
2818 "uri=\"/a\", qop=auth, nc=00000001, cnonce=\"deadbeef\", "
2819 "algorithm=SHA-256, response=\"0123456789abcdef\"\r\n\r\n" },
2820 P_END, P_END } },
2821
2822 { "basic-auth", { 0x00, 0x0a, 0x03, 0x00 },
2823 { { 0, "GET /a HTTP/1.1\r\nHost: x\r\n"
2824 "Authorization: Basic dXNlcjpwYXNz\r\n\r\n" },
2825 P_END, P_END, P_END } },
2826
2827 { "multipart-post", { 0x00, 0x0c, 0x03, 0x00 },
2828 { { 0, "POST /a HTTP/1.1\r\nHost: x\r\n"
2829 "Content-Type: multipart/form-data; boundary=--abc\r\n"
2830 "Transfer-Encoding: chunked\r\n\r\n"
2831 "52\r\n----abc\r\nContent-Disposition: form-data; name=\"k\"\r\n\r\n"
2832 "value\r\n----abc--\r\n\r\n0\r\n\r\n" },
2833 P_END, P_END, P_END } },
2834
2835 { "urlencoded-post", { 0x00, 0x0c, 0x03, 0x00 },
2836 { { 1, "a=1&b=%41&c" },
2837 { 0, "POST /a HTTP/1.1\r\nHost: x\r\n"
2838 "Content-Type: application/x-www-form-urlencoded\r\n"
2839 "Content-Length: 11\r\n\r\na=1&b=%41&c" },
2840 P_END, P_END } },
2841
2842 { "folded-header", { 0x00, 0x08, 0x00, 0x00 },
2843 { { 0, "GET /a HTTP/1.1\r\nHost: x\r\nX-Fold: a\r\n\tb\r\n\r\n" },
2844 P_END, P_END, P_END } },
2845
2846 { "pipelined", { 0x00, 0x08, 0x03, 0x00 },
2847 { { 0, "GET /a HTTP/1.1\r\nHost: x\r\n\r\n"
2848 "GET /b?q HTTP/1.1\r\nHost: x\r\n\r\n" },
2849 P_END, P_END, P_END } },
2850
2851 /* ------------- seeds driving configuration bytes 4-9 -------------
2852 The seeds above leave those bytes zero and only exercise the
2853 parser. Without at least one seed per area below, the rest of the
2854 API stays practically unreachable: libFuzzer would have to guess
2855 six configuration bytes before anything new runs. Each seed turns
2856 on exactly one area so that a minimiser keeps them distinguishable.
2857 ext[] is { response, auth, event-loop/introspection, suspend and
2858 upgrade, header seed, content-reader seed }. */
2859
2860 /* Chunked reply produced by a content-reader callback, with response
2861 headers and a trailer -- the reply-side counterpart of the chunk
2862 parsing that commit c13f4c64 fixed on the request side. */
2863 { "ext-callback-chunked", { 0x00, 0x08, 0x03, 0x00 },
2864 { { 0, "GET / HTTP/1.1\r\nHost: x\r\n\r\n" }, P_END, P_END, P_END },
2865 { 0x0A | 0x20 | 0x40, 0x00, 0x00, 0x00, 0x01, 0x20 } },
2866
2867 /* Known-length callback body, with a reader error injected part way
2868 through (crc_seed bit 0x80). */
2869 { "ext-callback-error", { 0x00, 0x08, 0x03, 0x00 },
2870 { { 0, "GET / HTTP/1.1\r\nHost: x\r\n\r\n" }, P_END, P_END, P_END },
2871 { 0x09, 0x00, 0x00, 0x00, 0x02, 0x82 } },
2872
2873 { "ext-fd-response", { 0x00, 0x08, 0x03, 0x00 },
2874 { { 0, "GET / HTTP/1.1\r\nHost: x\r\n\r\n" }, P_END, P_END, P_END },
2875 { 0x0B, 0x00, 0x00, 0x00, 0x00, 0x10 } },
2876
2877 { "ext-fd-at-offset-response", { 0x00, 0x08, 0x03, 0x00 },
2878 { { 0, "GET / HTTP/1.1\r\nHost: x\r\n\r\n" }, P_END, P_END, P_END },
2879 { 0x0C, 0x00, 0x00, 0x00, 0x00, 0x10 } },
2880
2881 { "ext-pipe-response", { 0x00, 0x08, 0x03, 0x00 },
2882 { { 0, "GET / HTTP/1.1\r\nHost: x\r\n\r\n" }, P_END, P_END, P_END },
2883 { 0x0E, 0x00, 0x00, 0x00, 0x00, 0x10 } },
2884
2885 { "ext-iovec-response", { 0x00, 0x08, 0x03, 0x00 },
2886 { { 0, "GET / HTTP/1.1\r\nHost: x\r\n\r\n" }, P_END, P_END, P_END },
2887 { 0x0F, 0x00, 0x00, 0x00, 0x00, 0x07 } },
2888
2889 /* Empty response plus the header manipulation API (get/del/options). */
2890 { "ext-empty-response-hdrapi", { 0x00, 0x08, 0x03, 0x00 },
2891 { { 0, "GET / HTTP/1.1\r\nHost: x\r\n\r\n" }, P_END, P_END, P_END },
2892 { 0x02 | 0x30 | 0x40 | 0x80, 0x00, 0x00, 0x00, 0x05, 0x00 } },
2893
2894 /* Digest through the pre-computed-userdigest entry point. */
2895 { "ext-digest-check-digest3", { 0x00, 0x09, 0x03, 0x00 },
2896 { { 0, "GET / HTTP/1.1\r\nHost: x\r\n\r\n" },
2897 { 0, "GET / HTTP/1.1\r\nHost: x\r\nAuthorization: Digest "
2898 "username=\"user\", realm=\"TestRealm\", nonce=\"%%NONCE%%\", "
2899 "uri=\"/\", response=\"00000000000000000000000000000000\"\r\n\r\n" },
2900 P_END, P_END },
2901 { 0x00, 0x05 | 0x80, 0x00, 0x00, 0x01, 0x00 } },
2902
2903 /* The v1 wrappers and the request-info query. */
2904 { "ext-digest-v1-wrappers", { 0x00, 0x09, 0x03, 0x00 },
2905 { { 0, "GET / HTTP/1.1\r\nHost: x\r\n\r\n" }, P_END, P_END, P_END },
2906 { 0x00, 0x01 | 0x10, 0x00, 0x00, 0x00, 0x00 } },
2907
2908 { "ext-digest-request-info", { 0x00, 0x09, 0x03, 0x00 },
2909 { { 0, "GET / HTTP/1.1\r\nHost: x\r\n\r\n" }, P_END, P_END, P_END },
2910 { 0x00, 0x08 | 0x20, 0x00, 0x00, 0x03, 0x00 } },
2911
2912 /* Application-driven event loop: MHD_get_fdset2() + select() +
2913 MHD_run_from_select2(), with the timeout accessors. */
2914 { "ext-external-event-loop", { 0x00, 0x08, 0x03, 0x00 },
2915 { { 0, "GET / HTTP/1.1\r\nHost: x\r\n\r\n" }, P_END, P_END, P_END },
2916 { 0x01, 0x00, 0x02 | 0x04, 0x00, 0x00, 0x00 } },
2917
2918 /* The v1 fdset/run_from_select pair. */
2919 { "ext-external-event-loop-v1", { 0x00, 0x08, 0x03, 0x00 },
2920 { { 0, "GET / HTTP/1.1\r\nHost: x\r\n\r\n" }, P_END, P_END, P_END },
2921 { 0x01, 0x00, 0x01 | 0x04, 0x00, 0x00, 0x00 } },
2922
2923 /* Every connection-introspection accessor at once. */
2924 { "ext-connection-api", { 0x00, 0x08, 0x03, 0x00 },
2925 { { 0, "GET /path?a=1 HTTP/1.1\r\nHost: x\r\nCookie: a=b\r\n\r\n" },
2926 P_END, P_END, P_END },
2927 { 0x01, 0x00, 0x08 | 0x10 | 0x20 | 0x40 | 0x80, 0x00, 0x10, 0x00 } },
2928
2929 /* Suspend the connection and resume it from the pump loop. */
2930 { "ext-suspend-resume", { 0x00, 0x08, 0x03, 0x00 },
2931 { { 0, "GET / HTTP/1.1\r\nHost: x\r\n\r\n" },
2932 { 2, "" }, P_END, P_END },
2933 { 0x01, 0x00, 0x00, 0x02, 0x00, 0x00 } },
2934
2935 /* Two connections parked at the same time (op 3 opens the second
2936 without waiting for the first). The bookkeeping behind suspend
2937 mode 2 has to be a set for this: while it was a single slot the
2938 first connection was forgotten and stayed suspended, and
2939 MHD_stop_daemon() answered with
2940 MHD_PANIC ("called while we have suspended connections"). */
2941 { "ext-suspend-two-connections", { 0x00, 0x08, 0x03, 0x00 },
2942 { { 0, "GET /a HTTP/1.1\r\nHost: x\r\n\r\n" },
2943 { 3, "GET /b HTTP/1.1\r\nHost: x\r\n\r\n" },
2944 { 2, "" }, P_END },
2945 { 0x01, 0x00, 0x00, 0x02, 0x00, 0x00 } },
2946
2947 /* HTTP "Upgrade": the client asks for it and the handler answers 101
2948 with an upgrade response, after which the socket is handed over. */
2949 { "ext-upgrade", { 0x00, 0x08, 0x03, 0x00 },
2950 { { 0, "GET / HTTP/1.1\r\nHost: x\r\nConnection: Upgrade\r\n"
2951 "Upgrade: fuzz-protocol\r\n\r\n" },
2952 { 2, "" }, P_END, P_END },
2953 { 0x01, 0x00, 0x00, 0x04 | 0x08, 0x00, 0x00 } },
2954
2955 /* The remaining response constructors. One seed each, because the
2956 constructor is picked by the low nibble of ext[0] and a seed that
2957 does not name it leaves that entry point unreachable until the
2958 fuzzer happens to mutate the nibble. */
2959 { "ext-buffer-persistent", { 0x00, 0x08, 0x03, 0x00 },
2960 { { 0, "GET / HTTP/1.1\r\nHost: x\r\n\r\n" }, P_END, P_END, P_END },
2961 { 0x03, 0x00, 0x00, 0x00, 0x00, 0x00 } },
2962
2963 { "ext-buffer-free-callback", { 0x00, 0x08, 0x03, 0x00 },
2964 { { 0, "GET / HTTP/1.1\r\nHost: x\r\n\r\n" }, P_END, P_END, P_END },
2965 { 0x06, 0x00, 0x00, 0x00, 0x00, 0x00 } },
2966
2967 { "ext-from-data", { 0x00, 0x08, 0x03, 0x00 },
2968 { { 0, "GET / HTTP/1.1\r\nHost: x\r\n\r\n" }, P_END, P_END, P_END },
2969 { 0x07, 0x00, 0x00, 0x00, 0x00, 0x00 } },
2970
2971 { "ext-fd64-response", { 0x00, 0x08, 0x03, 0x00 },
2972 { { 0, "GET / HTTP/1.1\r\nHost: x\r\n\r\n" }, P_END, P_END, P_END },
2973 { 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00 } },
2974
2975 /* The v1 basic-auth getter, which returns two separate malloc()ed
2976 strings rather than one struct. */
2977 { "ext-basic-auth-v1", { 0x00, 0x0a, 0x03, 0x00 },
2978 { { 0, "GET / HTTP/1.1\r\nHost: x\r\n"
2979 "Authorization: Basic dXNlcjpwYXNz\r\n\r\n" },
2980 P_END, P_END, P_END },
2981 { 0x01, 0x40, 0x00, 0x00, 0x00, 0x00 } },
2982
2983 /* Answer the challenge with the basic-auth responses instead of the
2984 digest ones (fail_variant 3). */
2985 { "ext-basic-auth-challenge", { 0x00, 0x09, 0x03, 0x00 },
2986 { { 0, "GET / HTTP/1.1\r\nHost: x\r\n\r\n" }, P_END, P_END, P_END },
2987 { 0x01, 0x30, 0x00, 0x00, 0x00, 0x00 } },
2988
2989 { "ext-basic-auth-challenge-utf8", { 0x00, 0x09, 0x03, 0x00 },
2990 { { 0, "GET / HTTP/1.1\r\nHost: x\r\n\r\n" }, P_END, P_END, P_END },
2991 { 0x01, 0x30, 0x00, 0x00, 0x03, 0x00 } },
2992
2993 /* The MD5-only and algorithm-parametrised pre-computed digest
2994 wrappers. */
2995 { "ext-digest-check-digest-v1", { 0x00, 0x09, 0x03, 0x00 },
2996 { { 0, "GET / HTTP/1.1\r\nHost: x\r\n\r\n" }, P_END, P_END, P_END },
2997 { 0x01, 0x03, 0x00, 0x00, 0x00, 0x00 } },
2998
2999 { "ext-digest-check-digest2", { 0x00, 0x09, 0x03, 0x00 },
3000 { { 0, "GET / HTTP/1.1\r\nHost: x\r\n\r\n" }, P_END, P_END, P_END },
3001 { 0x01, 0x04, 0x00, 0x00, 0x01, 0x00 } },
3002
3003 /* The username queries. */
3004 { "ext-digest-get-username-v1", { 0x00, 0x09, 0x03, 0x00 },
3005 { { 0, "GET / HTTP/1.1\r\nHost: x\r\n"
3006 "Authorization: Digest username=\"user\", realm=\"TestRealm\"\r\n\r\n" }
3007 ,
3008 P_END, P_END, P_END },
3009 { 0x01, 0x06, 0x00, 0x00, 0x00, 0x00 } },
3010
3011 { "ext-digest-get-username3", { 0x00, 0x09, 0x03, 0x00 },
3012 { { 0, "GET / HTTP/1.1\r\nHost: x\r\n"
3013 "Authorization: Digest userhash=true, username=\"aabb\", "
3014 "realm=\"TestRealm\"\r\n\r\n" },
3015 P_END, P_END, P_END },
3016 { 0x01, 0x07, 0x00, 0x00, 0x00, 0x00 } }
3017};
3018
3019static uint8_t seed_render_buf[4096];
3020
3021
3022static size_t
3024{
3025 return sizeof (seeds) / sizeof (seeds[0]);
3026}
3027
3028
3029static const uint8_t *
3030fuzz_seed_get (size_t idx,
3031 size_t *len)
3032{
3033 const struct seed_def *sd = &seeds[idx];
3034 struct sbuf b;
3035 unsigned int i;
3036
3037 b.p = seed_render_buf;
3038 b.len = 0;
3039 b.cap = sizeof (seed_render_buf);
3040 sb_raw (&b, sd->cfg, 4);
3041 sb_raw (&b, sd->ext, sizeof (sd->ext));
3042 for (i = 0; i < sizeof (sd->parts) / sizeof (sd->parts[0]); i++)
3043 {
3044 size_t n;
3045 unsigned int hv;
3046 uint8_t hdr[2];
3047
3048 if (NULL == sd->parts[i].txt)
3049 break;
3050 n = strlen (sd->parts[i].txt);
3051 if (n > 0x3FFF)
3052 n = 0x3FFF;
3053 hv = (sd->parts[i].op << 14) | (unsigned int) n;
3054 hdr[0] = (uint8_t) (hv & 0xFF);
3055 hdr[1] = (uint8_t) (hv >> 8);
3056 sb_raw (&b, hdr, 2);
3057 sb_raw (&b, sd->parts[i].txt, n);
3058 }
3059 *len = b.len;
3060 return seed_render_buf;
3061}
Shared, header-only fuzzing driver for the MHD in-process fuzzers.
static FUZZ_UNUSED int fuzz_pristine
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)
#define FUZZ_MAX_INPUT
Definition fuzz_common.h:73
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 pending_resume_add(struct MHD_Connection *c)
#define MAX_CONNECTIONS
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 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)
#define MAX_DIGEST_BIN
static void send_all(struct MHD_Daemon *d, int sock, const uint8_t *data, size_t len)
#define DAUTH_PASSED
static int gen_one_request(struct fuzz_rng *rng, struct sbuf *b, enum gen_shape shape, int second_of_pair)
static const struct seed_def seeds[]
#define DIGEST_PASS
static size_t resp_len
static int stats_registered
static void sb_u64(struct sbuf *b, uint64_t v, int hex, unsigned int min_digits)
static const char *const gen_targets[]
static unsigned long stat_daemons
static void gen_query(struct fuzz_rng *rng, struct sbuf *b, int force_trailing_novalue)
static int tearing_down
static int oracle_on
static enum MHD_DigestAuthAlgorithm algo1_tbl[]
static enum MHD_Result queue_auth_challenge(struct MHD_Connection *connection, int stale)
static int new_connection(struct MHD_Daemon *d, int *sock)
static enum MHD_Result kv_iter(void *cls, enum MHD_ValueKind kind, const char *key, const char *value)
static char resp_buf[RESP_BUF_SIZE]
static int make_memfd(void)
static void sb_str(struct sbuf *b, const char *s)
static const char *const gen_ctypes[]
#define RESP_BODY_LEN
#define MAX_SEGMENTS
#define DAUTH_STALE
static void decorate_response(struct MHD_Response *r)
static uint8_t expect_body[MAX_EXPECT_BODY]
static void pending_resume_drop(const struct MHD_Connection *c)
#define P_END
static int min_discipline_read
#define FUZZ_HARNESS_NAME
static int run_digest_check(struct MHD_Connection *connection)
static void drain_and_harvest(int sock)
#define GEN_BUF_SIZE
static void upgrade_cb(void *cls, struct MHD_Connection *connection, void *req_cls, const char *extra_in, size_t extra_in_size, MHD_socket sock, struct MHD_UpgradeResponseHandle *urh)
static const uint8_t * fuzz_seed_get(size_t idx, size_t *len)
static enum MHD_DigestAuthAlgo3 algo3_tbl[]
static int min_mem_limit_read
gen_shape
@ SHAPE_PLAIN
@ SHAPE_DIGEST_SIMPLE
@ SHAPE_COUNT
@ SHAPE_CL_BODY
@ SHAPE_BASIC
@ SHAPE_POST_FORM
@ SHAPE_CHUNKED
@ SHAPE_WEIRD
@ SHAPE_NOHDR_QARG
@ SHAPE_DIGEST_REPLAY
static const char *const hdr_names[]
static size_t expect_body_len
static struct MHD_Response * make_response(void)
static const char *const gen_methods[]
static int min_discipline
#define MAX_EXPECT_BODY
static const char *const gen_hdr_names[]
static const unsigned int nnc_tbl[]
static void oracle_check_body(struct hstate *hs, const char *data, size_t size)
static void exercise_connection_api(struct MHD_Connection *connection)
static unsigned long stat_body_bytes
static unsigned long stat_auth_ok
static size_t nonce_len
#define DAUTH_CHALLENGE
static enum MHD_Result post_iter(void *cls, enum MHD_ValueKind kind, const char *key, const char *filename, const char *content_type, const char *transfer_encoding, const char *data, uint64_t off, size_t size)
static const char *const gen_algos[]
static void sb_raw(struct sbuf *b, const void *v, size_t n)
#define DIGEST_USER
static struct MHD_Connection * pending_resume[MAX_CONNECTIONS]
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
#define DAUTH_VARIANT_COUNT
static int forced_shape
#define RESP_BUF_SIZE
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 void gen_headers(struct fuzz_rng *rng, struct sbuf *b, unsigned int n)
static void gen_chunked_body(struct fuzz_rng *rng, struct sbuf *b, int oracle)
#define DIGEST_REALM
static void panic_cb(void *cls, const char *file, unsigned int line, const char *reason)
static size_t min_mem_limit
static struct fuzz_cfg cfg
static uint8_t seed_render_buf[4096]
static enum MHD_DigestAuthMultiAlgo3 algo_tbl[]
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 make_pipe_fd(void)
static unsigned long stat_challenges
static char nonce_val[192]
#define HDR_COUNT
static void close_connection(struct MHD_Daemon *d, int *sock)
static void call_calc_helpers(void)
static const char *const hdr_values[]
static int forced_shape_read
static void crc_free(void *cls)
static const char digest_rnd[32]
static int pending_resume_flush(void)
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[]
static void gen_digest_header(struct fuzz_rng *rng, struct sbuf *b, const char *uri, int use_nonce_token, int overlong_response, int prefer_valid)
static enum MHD_Result kv_iter_n(void *cls, enum MHD_ValueKind kind, const char *key, size_t key_size, const char *value, size_t value_size)
static size_t expand_nonce(const uint8_t *in, size_t in_len, uint8_t *out, size_t out_cap)
static unsigned long stat_final_calls
static ssize_t crc_cb(void *cls, uint64_t pos, char *buf, size_t max)
_MHD_EXTERN enum MHD_Result MHD_digest_auth_calc_userdigest(enum MHD_DigestAuthAlgo3 algo3, const char *username, const char *realm, const char *password, void *userdigest_bin, size_t bin_buf_size)
_MHD_EXTERN enum MHD_Result MHD_queue_auth_fail_response(struct MHD_Connection *connection, const char *realm, const char *opaque, struct MHD_Response *response, int signal_stale)
_MHD_EXTERN int MHD_digest_auth_check2(struct MHD_Connection *connection, const char *realm, const char *username, const char *password, unsigned int nonce_timeout, enum MHD_DigestAuthAlgorithm algo)
_MHD_EXTERN enum MHD_Result MHD_queue_basic_auth_fail_response(struct MHD_Connection *connection, const char *realm, struct MHD_Response *response)
Definition basicauth.c:313
_MHD_EXTERN enum MHD_Result MHD_queue_auth_required_response3(struct MHD_Connection *connection, const char *realm, const char *opaque, const char *domain, struct MHD_Response *response, int signal_stale, enum MHD_DigestAuthMultiQOP mqop, enum MHD_DigestAuthMultiAlgo3 algo, int userhash_support, int prefer_utf8)
_MHD_EXTERN int MHD_digest_auth_check_digest2(struct MHD_Connection *connection, const char *realm, const char *username, const uint8_t *digest, size_t digest_size, unsigned int nonce_timeout, enum MHD_DigestAuthAlgorithm algo)
_MHD_EXTERN enum MHD_Result MHD_digest_auth_calc_userhash_hex(enum MHD_DigestAuthAlgo3 algo3, const char *username, const char *realm, char *userhash_hex, size_t hex_buf_size)
_MHD_EXTERN enum MHD_Result MHD_queue_basic_auth_required_response3(struct MHD_Connection *connection, const char *realm, int prefer_utf8, struct MHD_Response *response)
Definition basicauth.c:223
_MHD_EXTERN enum MHD_DigestAuthResult MHD_digest_auth_check_digest3(struct MHD_Connection *connection, const char *realm, const char *username, const void *userdigest, size_t userdigest_size, unsigned int nonce_timeout, uint32_t max_nc, enum MHD_DigestAuthMultiQOP mqop, enum MHD_DigestAuthMultiAlgo3 malgo3)
_MHD_EXTERN char * MHD_basic_auth_get_username_password(struct MHD_Connection *connection, char **password)
Definition basicauth.c:137
_MHD_EXTERN enum MHD_Result MHD_queue_auth_fail_response2(struct MHD_Connection *connection, const char *realm, const char *opaque, struct MHD_Response *response, int signal_stale, enum MHD_DigestAuthAlgorithm algo)
_MHD_EXTERN struct MHD_DigestAuthInfo * MHD_digest_auth_get_request_info3(struct MHD_Connection *connection)
_MHD_EXTERN size_t MHD_digest_get_hash_size(enum MHD_DigestAuthAlgo3 algo3)
Definition digestauth.c:296
_MHD_EXTERN int MHD_digest_auth_check_digest(struct MHD_Connection *connection, const char *realm, const char *username, const uint8_t digest[MHD_MD5_DIGEST_SIZE], unsigned int nonce_timeout)
_MHD_EXTERN char * MHD_digest_auth_get_username(struct MHD_Connection *connection)
_MHD_EXTERN int MHD_digest_auth_check(struct MHD_Connection *connection, const char *realm, const char *username, const char *password, unsigned int nonce_timeout)
#define MHD_INVALID_NONCE
_MHD_EXTERN enum MHD_DigestAuthResult MHD_digest_auth_check3(struct MHD_Connection *connection, const char *realm, const char *username, const char *password, unsigned int nonce_timeout, uint32_t max_nc, enum MHD_DigestAuthMultiQOP mqop, enum MHD_DigestAuthMultiAlgo3 malgo3)
_MHD_EXTERN struct MHD_BasicAuthInfo * MHD_basic_auth_get_username_password3(struct MHD_Connection *connection)
Definition basicauth.c:49
_MHD_EXTERN enum MHD_Result MHD_digest_auth_calc_userhash(enum MHD_DigestAuthAlgo3 algo3, const char *username, const char *realm, void *userhash_bin, size_t bin_buf_size)
_MHD_EXTERN struct MHD_DigestAuthUsernameInfo * MHD_digest_auth_get_username3(struct MHD_Connection *connection)
#define MHD_MD5_DIGEST_SIZE
_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
#define MHD_HTTP_HEADER_CONNECTION
Definition microhttpd.h:590
#define MHD_HTTP_HEADER_UPGRADE
Definition microhttpd.h:656
#define MHD_HTTP_OK
Definition microhttpd.h:350
#define MHD_HTTP_SWITCHING_PROTOCOLS
Definition microhttpd.h:343
#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_EXTERN enum MHD_Result MHD_destroy_post_processor(struct MHD_PostProcessor *pp)
_MHD_EXTERN enum MHD_Result MHD_set_connection_value(struct MHD_Connection *connection, enum MHD_ValueKind kind, const char *key, const char *value)
_MHD_EXTERN const char * MHD_lookup_connection_value(struct MHD_Connection *connection, enum MHD_ValueKind kind, const char *key)
_MHD_EXTERN enum MHD_Result MHD_post_process(struct MHD_PostProcessor *pp, const char *post_data, size_t post_data_len)
_MHD_EXTERN int MHD_get_connection_values_n(struct MHD_Connection *connection, enum MHD_ValueKind kind, MHD_KeyValueIteratorN iterator, void *iterator_cls)
Definition connection.c:913
_MHD_EXTERN int MHD_get_connection_values(struct MHD_Connection *connection, enum MHD_ValueKind kind, MHD_KeyValueIterator iterator, void *iterator_cls)
Definition connection.c:874
_MHD_EXTERN struct MHD_PostProcessor * MHD_create_post_processor(struct MHD_Connection *connection, size_t buffer_size, MHD_PostDataIterator iter, void *iter_cls)
_MHD_EXTERN enum MHD_Result MHD_get_connection_URI_path_n(struct MHD_Connection *connection, const char **uri, size_t *uri_size)
Definition connection.c:836
MHD_ConnectionInfoType
_MHD_EXTERN enum MHD_Result MHD_lookup_connection_value_n(struct MHD_Connection *connection, enum MHD_ValueKind kind, const char *key, size_t key_size, const char **value_ptr, size_t *value_size_ptr)
MHD_RequestTerminationCode
@ MHD_CONNECTION_INFO_CONNECTION_TIMEOUT
@ MHD_CONNECTION_INFO_SOCKET_CONTEXT
@ MHD_CONNECTION_INFO_REQUEST_HEADER_SIZE
@ MHD_CONNECTION_INFO_CONNECTION_SUSPENDED
@ MHD_CONNECTION_INFO_CLIENT_ADDRESS
@ MHD_CONNECTION_INFO_DAEMON
@ MHD_CONNECTION_INFO_HTTP_STATUS
@ MHD_CONNECTION_INFO_CONNECTION_FD
_MHD_EXTERN int MHD_get_response_headers(struct MHD_Response *response, MHD_KeyValueIterator iterator, void *iterator_cls)
Definition response.c:828
_MHD_EXTERN struct MHD_Response * MHD_create_response_from_buffer_with_free_callback(size_t size, void *buffer, MHD_ContentReaderFreeCallback crfc)
Definition response.c:1598
_MHD_EXTERN struct MHD_Response * MHD_create_response_from_iovec(const struct MHD_IoVec *iov, unsigned int iovcnt, MHD_ContentReaderFreeCallback free_cb, void *cls)
Definition response.c:1687
_MHD_EXTERN struct MHD_Response * MHD_create_response_from_data(size_t size, void *data, int must_free, int must_copy)
Definition response.c:1434
_MHD_EXTERN struct MHD_Response * MHD_create_response_from_buffer_copy(size_t size, const void *buffer)
Definition response.c:1545
_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_del_response_header(struct MHD_Response *response, const char *header, const char *content)
Definition response.c:746
_MHD_EXTERN struct MHD_Response * MHD_create_response_from_pipe(int fd)
Definition response.c:1340
_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_from_fd(size_t size, int fd)
Definition response.c:1375
_MHD_EXTERN struct MHD_Response * MHD_create_response_empty(enum MHD_ResponseFlags flags)
Definition response.c:1831
_MHD_EXTERN const char * MHD_get_response_header(struct MHD_Response *response, const char *key)
Definition response.c:860
_MHD_EXTERN struct MHD_Response * MHD_create_response_from_buffer_static(size_t size, const void *buffer)
Definition response.c:1512
_MHD_EXTERN enum MHD_Result MHD_add_response_header(struct MHD_Response *response, const char *header, const char *content)
Definition response.c:625
_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 struct MHD_Response * MHD_create_response_from_fd64(uint64_t size, int fd)
Definition response.c:1405
_MHD_EXTERN enum MHD_Result MHD_add_response_footer(struct MHD_Response *response, const char *footer, const char *content)
Definition response.c:720
@ MHD_RESPMEM_MUST_FREE
@ MHD_RESPMEM_PERSISTENT
@ 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 const union MHD_ConnectionInfo * MHD_get_connection_info(struct MHD_Connection *connection, enum MHD_ConnectionInfoType info_type,...)
_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
_MHD_EXTERN void MHD_free(void *ptr)
Definition daemon.c:213
#define MHD_HTTP_VERSION_1_1
#define NULL
public interface to libmicrohttpd
int MHD_socket
Definition microhttpd.h:206
@ MHD_FEATURE_UPGRADE
@ MHD_OPTION_CLIENT_DISCIPLINE_LVL
@ MHD_OPTION_SERVER_INSANITY
@ MHD_OPTION_DIGEST_AUTH_RANDOM
@ MHD_OPTION_NONCE_NC_SIZE
@ MHD_OPTION_CONNECTION_MEMORY_LIMIT
@ MHD_OPTION_ARRAY
@ MHD_OPTION_END
@ MHD_OPTION_NOTIFY_COMPLETED
#define MHD_SIZE_UNKNOWN
Definition microhttpd.h:183
@ MHD_DSC_SANE
MHD_DigestAuthResult
@ MHD_DAUTH_NONCE_STALE
@ MHD_DAUTH_OK
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
MHD_DigestAuthMultiQOP
@ MHD_DIGEST_AUTH_MULT_QOP_AUTH
@ MHD_DIGEST_AUTH_MULT_QOP_ANY_NON_INT
#define MHD_create_response_from_fd_at_offset(size, fd, offset)
#define MHD_UNSIGNED_LONG_LONG
Definition microhttpd.h:311
_MHD_EXTERN struct MHD_Response * MHD_create_response_for_upgrade(MHD_UpgradeHandler upgrade_handler, void *upgrade_handler_cls)
MHD_DigestAuthAlgo3
@ MHD_DIGEST_AUTH_ALGO3_MD5_SESSION
@ MHD_DIGEST_AUTH_ALGO3_MD5
@ MHD_DIGEST_AUTH_ALGO3_SHA256
@ MHD_DIGEST_AUTH_ALGO3_SHA512_256_SESSION
@ MHD_DIGEST_AUTH_ALGO3_SHA256_SESSION
@ MHD_DIGEST_AUTH_ALGO3_SHA512_256
int fd
_MHD_EXTERN void MHD_resume_connection(struct MHD_Connection *connection)
Definition daemon.c:3445
void * data
#define MHD_INVALID_SOCKET
Definition microhttpd.h:207
#define MHD_CONTENT_READER_END_WITH_ERROR
Definition microhttpd.h:187
@ MHD_UPGRADE_ACTION_CORK_ON
@ MHD_UPGRADE_ACTION_CLOSE
@ MHD_UPGRADE_ACTION_CORK_OFF
MHD_ValueKind
@ MHD_FOOTER_KIND
@ MHD_COOKIE_KIND
@ MHD_HEADER_KIND
@ MHD_GET_ARGUMENT_KIND
MHD_DigestAuthMultiAlgo3
@ MHD_DIGEST_AUTH_MULT_ALGO3_SHA256
@ MHD_DIGEST_AUTH_MULT_ALGO3_SHA512_256
@ MHD_DIGEST_AUTH_MULT_ALGO3_ANY_NON_SESSION
@ MHD_DIGEST_AUTH_MULT_ALGO3_MD5
MHD_DaemonInfoType
@ MHD_DAEMON_INFO_BIND_PORT
@ MHD_DAEMON_INFO_FLAGS
@ MHD_DAEMON_INFO_CURRENT_CONNECTIONS
@ MHD_DAEMON_INFO_LISTEN_FD
@ MHD_ALLOW_SUSPEND_RESUME
@ MHD_ALLOW_UPGRADE
@ MHD_USE_ERROR_LOG
@ MHD_USE_NO_LISTEN_SOCKET
@ MHD_RO_END
MHD_ResponseFlags
@ MHD_RF_SEND_KEEP_ALIVE_HEADER
@ MHD_RF_HTTP_VERSION_1_0_RESPONSE
@ MHD_RF_HEAD_ONLY_RESPONSE
@ MHD_RF_INSANITY_HEADER_CONTENT_LENGTH
@ MHD_RF_NONE
MHD_DigestAuthAlgorithm
@ MHD_DIGEST_ALG_AUTO
@ MHD_DIGEST_ALG_MD5
@ MHD_DIGEST_ALG_SHA256
_MHD_EXTERN enum MHD_Result MHD_set_response_options(struct MHD_Response *response, enum MHD_ResponseFlags flags,...)
Definition response.c:1033
_MHD_EXTERN enum MHD_Result MHD_upgrade_action(struct MHD_UpgradeResponseHandle *urh, enum MHD_UpgradeAction action,...)
_MHD_EXTERN void MHD_suspend_connection(struct MHD_Connection *connection)
Definition daemon.c:3402
@ MHD_CONNECTION_OPTION_TIMEOUT
const void * iov_base
size_t iov_len
enum MHD_OPTION option
struct MHD_Connection * connection