fuzz_memorypool.c (48519B)
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 20 /** 21 * @file fuzz/fuzz_memorypool.c 22 * @brief Direct in-process fuzzer for src/microhttpd/memorypool.c 23 * @author Christian Grothoff 24 * 25 * memorypool.c is the per-connection allocator: a bump allocator that 26 * hands out "normal" blocks from the start of the pool and small, 27 * never-reallocated blocks from its end, plus an in-place resize and a 28 * reset that moves one surviving block back to the beginning. Every 29 * request buffer, every header name and value of every connection lives 30 * in it, so a single mis-computed offset here is a cross-request 31 * information leak or a heap overflow -- and one that ASAN alone cannot 32 * see, because the whole pool is one malloc()ed object. (It does see it 33 * in a build where MHD_ASAN_POISON_ACTIVE is defined, i.e. where 34 * `sanitizer/asan_interface.h` was found and memorypool.c poisons the 35 * unallocated parts of the pool itself. The harness works either way 36 * and knows the red zone size that mode adds between two blocks.) 37 * 38 * The harness therefore keeps its own model of the pool and checks it 39 * against the allocator after every single operation: 40 * 41 * - every returned pointer lies inside the pool, is aligned, and does 42 * not overlap any other live allocation; 43 * - every live allocation carries a position-dependent byte pattern 44 * that is re-verified later; the allocator moves data around in 45 * MHD_pool_reallocate() and MHD_pool_reset(), and the pattern is what 46 * proves the move was correct; 47 * - the sum of the live allocations plus MHD_pool_get_free() never 48 * exceeds the size of the pool, MHD_pool_get_free() never grows 49 * across an allocation and never shrinks across a deallocation; 50 * - the implications the rest of MHD relies on hold: 51 * * MHD_pool_allocate (pool, MHD_pool_get_free (pool), false) must 52 * succeed (response.c:2203 does exactly this), and more generally 53 * an allocation must not fail while get_free() reports room for 54 * its rounded-up size; 55 * * if MHD_pool_is_resizable_inplace() says yes, a reallocation 56 * that fits must succeed *and must not move the block* 57 * (connection.c:729/750/2082/2121 assert precisely that); 58 * * after freeing the number of bytes MHD_pool_try_alloc() asked 59 * for in @a required_bytes, the same call must succeed -- this is 60 * the whole point of that out parameter and the logic of 61 * MHD_connection_alloc_memory_(). 62 * 63 * Documented preconditions are respected, since violating them would 64 * only produce findings about the harness: blocks allocated "from the 65 * end" are never passed to MHD_pool_reallocate(), MHD_pool_reset() is 66 * never asked for more than the pool size nor for more copy_bytes than 67 * the kept block holds, and a block is only ever deallocated with the 68 * size it was allocated with. 69 * 70 * Two *open* defects of memorypool.c are reachable and would otherwise 71 * make every run abort; each is gated behind an environment variable, 72 * documented in full at full_dealloc_enabled and wrap_realloc_enabled 73 * below and in README section 6.1, with a byte-exact reproducer in the 74 * built-in seed corpus. Nothing else is gated. 75 * 76 * Input format: 77 * byte 0 pool size selector (index into fz_pool_sizes[]) 78 * byte 1 flags 79 * 0x01 verify every live block after every operation 80 * 0x02 prefer allocating "from the end" 81 * rest unused 82 * byte 2.. a stream of 3-byte operation records 83 * [0] opcode (see enum fz_op) 84 * [1] block selector / auxiliary nibbles 85 * [2] size selector (see fz_size()) 86 */ 87 88 #define FUZZ_HARNESS_NAME "fuzz_memorypool" 89 #include "fuzz_common.h" 90 91 /* internal.h pulls in MHD_config.h and <microhttpd.h> in the right 92 order; including <microhttpd.h> first would redefine _MHD_EXTERN. */ 93 #include "internal.h" 94 #include "memorypool.h" 95 96 97 /** 98 * Alignment of every block the pool hands out; memorypool.c aligns to 99 * two words "as GNU libc does". 100 */ 101 #define FZ_ALIGN_SIZE (2 * sizeof (void *)) 102 103 /** 104 * Size of the red zone memorypool.c keeps between two blocks. It only 105 * exists in a build with user memory poisoning, i.e. when the pool 106 * itself is instrumented; without it the pool is packed tight. 107 */ 108 #ifdef MHD_ASAN_POISON_ACTIVE 109 #define FZ_RED_ZONE_SIZE FZ_ALIGN_SIZE 110 #else /* ! MHD_ASAN_POISON_ACTIVE */ 111 #define FZ_RED_ZONE_SIZE ((size_t) 0) 112 #endif /* ! MHD_ASAN_POISON_ACTIVE */ 113 114 /** 115 * Maximum number of live allocations the model tracks. An operation 116 * that would exceed this is skipped, so the model never loses a block. 117 */ 118 #define FZ_MAX_BLOCKS 24 119 120 /** 121 * Maximum number of operations executed for one input. 122 */ 123 #define FZ_MAX_OPS 64 124 125 /** 126 * Number of bytes of each allocation that carry the check pattern. 127 * Bounding this keeps the harness fast; the interesting bugs of a bump 128 * allocator are all at the start of a block or at its (ASAN-guarded) 129 * end. 130 */ 131 #define FZ_PAT_MAX 96 132 133 /** 134 * Pool sizes to choose from. MHD uses 128..32768 in practice (the 135 * default is 32768 and MHD_OPTION_CONNECTION_MEMORY_LIMIT can set 136 * anything); everything at or below 32k is malloc()ed by 137 * MHD_pool_create(), so the harness knows the exact usable size and 138 * ASAN's redzone sits right behind the pool. The last two entries are 139 * larger than that on purpose: they take the mmap() path of 140 * MHD_pool_create() and the munmap() path of MHD_pool_destroy(). Both 141 * are powers of two and therefore a multiple of any page size, so the 142 * resulting pool is exactly that large either way and the check below 143 * stays exact. 144 */ 145 static const size_t fz_pool_sizes[] = { 146 1, 15, 16, 17, 32, 48, 64, 96, 128, 129, 192, 256, 320, 384, 512, 147 768, 1024, 1400, 1500, 2048, 4096, 8192, 16384, 32768, 65536, 131072 148 }; 149 150 #define FZ_NUM_POOL_SIZES \ 151 (sizeof (fz_pool_sizes) / sizeof (fz_pool_sizes[0])) 152 153 154 enum fz_op 155 { 156 FZ_OP_ALLOC = 0, /**< MHD_pool_allocate() */ 157 FZ_OP_ALLOC_ALL, /**< allocate exactly MHD_pool_get_free() bytes */ 158 FZ_OP_TRY_ALLOC, /**< MHD_pool_try_alloc() */ 159 FZ_OP_SQUEEZE, /**< try_alloc + shrink + retry */ 160 FZ_OP_REALLOC, /**< MHD_pool_reallocate() of a live block */ 161 FZ_OP_REALLOC_NEW, /**< MHD_pool_reallocate() with old == NULL */ 162 FZ_OP_DEALLOC, /**< MHD_pool_deallocate() */ 163 FZ_OP_RESET, /**< MHD_pool_reset() */ 164 FZ_OP_GET_FREE, /**< MHD_pool_get_free() */ 165 FZ_OP_RESIZABLE, /**< MHD_pool_is_resizable_inplace() */ 166 FZ_OP_VERIFY, /**< re-check every live block */ 167 FZ_OP_RECREATE, /**< MHD_pool_destroy() + MHD_pool_create() */ 168 FZ_OP_COUNT 169 }; 170 171 172 /** 173 * One live allocation. 174 */ 175 struct fz_block 176 { 177 /** 178 * Address returned by the pool. 179 */ 180 uint8_t *ptr; 181 182 /** 183 * Current size of the block, i.e. the number of bytes the harness is 184 * allowed to touch and the size it will pass to the pool again. 185 */ 186 size_t size; 187 188 /** 189 * Seed of the check pattern of this block. 190 */ 191 uint8_t tag; 192 193 /** 194 * True if the block was allocated "from the end" (MHD_pool_allocate() 195 * with @a from_end, or MHD_pool_try_alloc()). Such blocks must never 196 * be passed to MHD_pool_reallocate(). 197 */ 198 bool from_end; 199 }; 200 201 202 /** 203 * The model of one pool. 204 */ 205 struct fz_pool 206 { 207 struct MemoryPool *pool; 208 209 /** 210 * Address of the first byte of the pool, as returned by 211 * MHD_pool_reset() for an empty pool -- that is a real MHD call site 212 * (connection.c:3021 uses the result as the new read buffer). 213 */ 214 uint8_t *base; 215 216 /** 217 * Total size of the pool in bytes. 218 */ 219 size_t cap; 220 221 struct fz_block blk[FZ_MAX_BLOCKS]; 222 223 unsigned int nblk; 224 225 uint8_t next_tag; 226 227 /** 228 * Verify every live block after every operation. 229 */ 230 bool paranoid; 231 }; 232 233 234 /** 235 * F1 -- MHD_pool_deallocate() of a "from the end" block of a pool that 236 * is *exactly* full. This is a live defect in memorypool.c and is 237 * therefore off by default; set MHD_FUZZ_POOL_FULL_DEALLOC=1 to reach 238 * it (seed fuzz_memorypool-15.bin is the byte-exact reproducer). 239 * 240 * MHD_pool_deallocate() decides whether a block came from the front or 241 * from the end with 242 * 243 * if (block_offset <= pool->pos) memorypool.c:666 244 * 245 * which is ambiguous when pool->pos == pool->end, i.e. when the pool has 246 * no free space left: a "from the end" block then starts exactly at 247 * pool->pos and is treated as a front block. With --enable-asserts 248 * 249 * mhd_assert ((block_offset != pool->pos) || (block_size == 0)); :655 250 * mhd_assert (alg_end <= pool->pos); :671 251 * 252 * both fail, i.e. the process aborts; without them the block is simply 253 * not returned to the pool (pool->end is left alone), so the memory is 254 * lost until the pool is reset or destroyed. Reproducer: 255 * 256 * p = MHD_pool_create (128); 257 * e = MHD_pool_allocate (p, 16, true); 258 * (void) MHD_pool_allocate (p, MHD_pool_get_free (p), false); 259 * MHD_pool_deallocate (p, e, 16); <- abort / 16 bytes lost 260 * 261 * Nothing in memorypool.h forbids this; MHD_pool_deallocate() documents 262 * only that "the NULL is tolerated" and has an explicit branch for 263 * blocks allocated from the end. MHD's own call sites never hit it, 264 * because the three MHD_pool_deallocate() calls in connection.c and 265 * response.c all pass the read or the write buffer, which are front 266 * allocations -- so this is a latent defect of a supported branch of the 267 * API rather than something reachable from the network today. A 268 * dispatch on `block_offset < pool->end` would resolve the ambiguity. 269 */ 270 static int full_dealloc_enabled; 271 272 /** 273 * F2 -- MHD_pool_reallocate() reports success for a @a new_size that 274 * wrapped. Also a live defect, off by default; set 275 * MHD_FUZZ_POOL_WRAP_REALLOC=1 to reach it (seed 276 * fuzz_memorypool-16.bin is the byte-exact reproducer). 277 * 278 * Every other entry point of memorypool.c detects a size that is too 279 * close to SIZE_MAX explicitly: 280 * 281 * asize = ROUND_TO_ALIGN_PLUS_RED_ZONE (size); 282 * if ( (0 == asize) && (0 != size) ) 283 * return NULL; // "size too close to SIZE_MAX" 284 * 285 * -- MHD_pool_allocate(), MHD_pool_try_alloc() and the "need to 286 * allocate a new block" tail of MHD_pool_reallocate() all do it. The 287 * in-place branch of MHD_pool_reallocate() instead relies on 288 * 289 * const size_t new_apos = 290 * ROUND_TO_ALIGN_PLUS_RED_ZONE (old_offset + new_size); 291 * if ( (new_apos > pool->end) || 292 * (new_apos < pool->pos) ) // "Value wrap" 293 * return NULL; 294 * 295 * which does not catch the wrap when the wrapped value happens to land 296 * on pool->pos. For a zero-length block sitting at the front boundary 297 * (old_size == 0, old_offset == pool->pos, which is exactly what 298 * MHD_pool_reset (pool, NULL, 0, 0) returns -- see connection.c:3021 -- 299 * or MHD_pool_allocate (pool, 0, false)) and any new_size in 300 * [SIZE_MAX - 14, SIZE_MAX], old_offset + new_size wraps to 301 * old_offset - 1 - k, which rounds back up to exactly old_offset, i.e. 302 * to pool->pos: neither test fires, pool->pos is left alone and the 303 * function returns @a old, i.e. it reports success. 304 * 305 * p = MHD_pool_create (1024); 306 * b = MHD_pool_reset (p, NULL, 0, 0); 307 * r = MHD_pool_reallocate (p, b, 0, SIZE_MAX - 4); 308 * // r == b, not NULL: the caller now believes it owns 309 * // SIZE_MAX - 4 bytes at the start of a 1024 byte pool, 310 * // while MHD_pool_get_free() still reports the full 1024. 311 * 312 * The documented contract is "@return new address of the block, or NULL 313 * if the pool cannot support @a new_size bytes", so this is a plain 314 * contract violation, and one that hands the caller a buffer size the 315 * allocation does not have. No MHD call site can reach it today: every 316 * new_size MHD passes is derived from the pool size (connection.c:1636, 317 * 2031, 2082, 2121, 2170, 6935), so it is a latent defect rather than 318 * something reachable from the network. Checking the addition itself, 319 * e.g. `if (new_size > pool->size - old_offset) return NULL;`, would 320 * close it. 321 */ 322 static int wrap_realloc_enabled; 323 324 static int known_bugs_read; 325 326 /** Statistics, printed at exit with --verbose. */ 327 static unsigned long stat_ops; 328 static unsigned long stat_alloc_ok; 329 static unsigned long stat_alloc_fail; 330 static unsigned long stat_realloc_moved; 331 static unsigned long stat_resets; 332 333 334 static void 335 print_stats (void) 336 { 337 if (! fuzz_verbose) 338 return; 339 fprintf (stderr, 340 "%s: ops=%lu allocations=%lu (failed %lu) " 341 "moved reallocations=%lu resets=%lu\n", 342 FUZZ_HARNESS_NAME, 343 stat_ops, stat_alloc_ok, stat_alloc_fail, 344 stat_realloc_moved, stat_resets); 345 } 346 347 348 /** 349 * Report a finding, prefixed with the name of the operation that was 350 * running. 351 */ 352 static void 353 fz_report (const char *op, 354 const char *what) 355 { 356 char msg[256]; 357 358 (void) snprintf (msg, sizeof (msg), "%s: %s", op, what); 359 fuzz_report_finding (msg); 360 } 361 362 363 /** 364 * The ROUND_TO_ALIGN() of memorypool.c, wrap-around included: a size 365 * that is too close to SIZE_MAX rounds to 0, which is how the allocator 366 * detects it. 367 */ 368 static size_t 369 fz_round (size_t n) 370 { 371 return (n + (FZ_ALIGN_SIZE - 1)) / FZ_ALIGN_SIZE * FZ_ALIGN_SIZE; 372 } 373 374 375 /** 376 * Byte @a idx of the check pattern of a block with seed @a tag. 377 */ 378 static uint8_t 379 fz_pat (uint8_t tag, 380 size_t idx) 381 { 382 return (uint8_t) ((((unsigned int) tag) * 131u) 383 + ((unsigned int) (idx * 17u)) 384 + 0x5Au); 385 } 386 387 388 /** 389 * Write the check pattern into @a ptr[from..to). 390 */ 391 static void 392 fz_fill (uint8_t *ptr, 393 uint8_t tag, 394 size_t from, 395 size_t to) 396 { 397 size_t i; 398 399 if (to > FZ_PAT_MAX) 400 to = FZ_PAT_MAX; 401 for (i = from; i < to; i++) 402 ptr[i] = fz_pat (tag, i); 403 } 404 405 406 /** 407 * Verify that the first bytes of @a ptr still carry the pattern of 408 * @a tag. The expected bytes are built in an exactly sized malloc()ed 409 * buffer, so that a comparison that runs off either end is caught by 410 * ASAN rather than silently passing. 411 */ 412 static void 413 fz_verify (const uint8_t *ptr, 414 size_t size, 415 uint8_t tag, 416 const char *op, 417 const char *what) 418 { 419 size_t n = (size < FZ_PAT_MAX) ? size : (size_t) FZ_PAT_MAX; 420 uint8_t *expect; 421 size_t i; 422 423 if (0 == n) 424 return; 425 expect = (uint8_t *) malloc (n); 426 if (NULL == expect) 427 abort (); 428 for (i = 0; i < n; i++) 429 expect[i] = fz_pat (tag, i); 430 if (0 != memcmp (expect, ptr, n)) 431 fz_report (op, what); 432 free (expect); 433 } 434 435 436 /** 437 * Verify every live block. 438 */ 439 static void 440 fz_verify_all (struct fz_pool *st, 441 const char *op) 442 { 443 unsigned int i; 444 445 for (i = 0; i < st->nblk; i++) 446 fz_verify (st->blk[i].ptr, 447 st->blk[i].size, 448 st->blk[i].tag, 449 op, 450 "the contents of a live allocation changed"); 451 } 452 453 454 /** 455 * Check a pointer the pool just returned: inside the pool, aligned, and 456 * not overlapping any block that is still live. The caller has already 457 * removed the block being replaced (if any) from the model. 458 */ 459 static void 460 fz_check_ptr (struct fz_pool *st, 461 void *p, 462 size_t size, 463 const char *op) 464 { 465 const uint8_t *up = (const uint8_t *) p; 466 size_t off; 467 unsigned int i; 468 469 if ( (up < st->base) || 470 (up > st->base + st->cap) ) 471 { 472 fz_report (op, "returned a pointer outside of the pool"); 473 return; 474 } 475 off = (size_t) (up - st->base); 476 if ( (size > st->cap) || 477 (off + size > st->cap) ) 478 fz_report (op, "returned a block that extends past the end of the pool"); 479 if (0 != (off % FZ_ALIGN_SIZE)) 480 fz_report (op, "returned a misaligned pointer"); 481 if (0 == size) 482 return; 483 for (i = 0; i < st->nblk; i++) 484 { 485 const uint8_t *q = st->blk[i].ptr; 486 487 if (0 == st->blk[i].size) 488 continue; 489 if ( (up < q + st->blk[i].size) && 490 (q < up + size) ) 491 { 492 fz_report (op, "returned a block overlapping another live allocation"); 493 return; 494 } 495 } 496 } 497 498 499 /** 500 * Invariants that must hold after every single operation. 501 */ 502 static void 503 fz_check_invariants (struct fz_pool *st, 504 const char *op) 505 { 506 size_t total = 0; 507 size_t freem; 508 unsigned int i; 509 510 freem = MHD_pool_get_free (st->pool); 511 if (freem > st->cap) 512 fz_report (op, "MHD_pool_get_free() exceeds the size of the pool"); 513 for (i = 0; i < st->nblk; i++) 514 total += st->blk[i].size; 515 if (total > st->cap) 516 fz_report (op, "the live allocations alone exceed the size of the pool"); 517 else if (total + freem > st->cap) 518 fz_report (op, 519 "the live allocations plus the free space exceed the size " 520 "of the pool"); 521 if (st->paranoid) 522 fz_verify_all (st, op); 523 } 524 525 526 static struct fz_block * 527 fz_add (struct fz_pool *st, 528 void *ptr, 529 size_t size, 530 bool from_end) 531 { 532 struct fz_block *b = &st->blk[st->nblk++]; 533 534 b->ptr = (uint8_t *) ptr; 535 b->size = size; 536 b->tag = st->next_tag++; 537 b->from_end = from_end; 538 fz_fill (b->ptr, b->tag, 0, size); 539 return b; 540 } 541 542 543 static void 544 fz_del (struct fz_pool *st, 545 struct fz_block *b) 546 { 547 *b = st->blk[--st->nblk]; 548 } 549 550 551 /** 552 * Pick a live block. 553 * 554 * @param st the model 555 * @param sel selector byte 556 * @param front_only only consider blocks that may be reallocated 557 * @return NULL if there is no such block 558 */ 559 static struct fz_block * 560 fz_pick (struct fz_pool *st, 561 uint8_t sel, 562 bool front_only) 563 { 564 unsigned int i; 565 unsigned int n = 0; 566 unsigned int idx; 567 568 for (i = 0; i < st->nblk; i++) 569 { 570 if ( (! front_only) || 571 (! st->blk[i].from_end) ) 572 n++; 573 } 574 if (0 == n) 575 return NULL; 576 idx = ((unsigned int) sel) % n; 577 for (i = 0; i < st->nblk; i++) 578 { 579 if ( (front_only) && 580 (st->blk[i].from_end) ) 581 continue; 582 if (0 == idx) 583 return &st->blk[i]; 584 idx--; 585 } 586 return NULL; /* unreachable */ 587 } 588 589 590 /** 591 * Turn a selector byte into a size. The four classes matter: tiny 592 * sizes are what headers use, fractions of the pool are what the 593 * request buffers use, sizes around the currently free amount are where 594 * the off-by-one-block bugs live, and sizes close to SIZE_MAX exercise 595 * the "value wrap" guards of the allocator. 596 */ 597 static size_t 598 fz_size (uint8_t sel, 599 size_t cap, 600 size_t freem) 601 { 602 size_t d; 603 604 switch (sel & 0x03) 605 { 606 case 0: 607 return (size_t) (sel >> 2); /* 0 .. 63 */ 608 case 1: 609 return (((size_t) ((sel >> 2) + 1)) * cap) / 64u; 610 case 2: 611 d = (size_t) ((sel >> 2) & 0x07u); 612 if (0 != (sel & 0x20)) 613 return freem + d; 614 return (freem > d) ? (freem - d) : 0; 615 default: 616 return SIZE_MAX - (size_t) (sel >> 2); 617 } 618 } 619 620 621 static void 622 fz_pool_close (struct fz_pool *st) 623 { 624 MHD_pool_destroy (st->pool); 625 st->pool = NULL; 626 st->nblk = 0; 627 } 628 629 630 /** 631 * Create a pool and learn its base address and its exact size. 632 * 633 * @return false if the pool could not be created 634 */ 635 static bool 636 fz_pool_open (struct fz_pool *st, 637 uint8_t sel) 638 { 639 size_t max = fz_pool_sizes[((size_t) sel) % FZ_NUM_POOL_SIZES]; 640 void *base; 641 642 st->nblk = 0; 643 st->pool = MHD_pool_create (max); 644 if (NULL == st->pool) 645 return false; 646 st->cap = MHD_pool_get_free (st->pool) + FZ_RED_ZONE_SIZE; 647 if (st->cap != fz_round (max)) 648 fz_report ("MHD_pool_create", 649 "a fresh pool does not offer the requested size rounded " 650 "up to the alignment"); 651 /* MHD_pool_reset() with nothing to keep returns the start of the 652 pool; connection.c:3021 relies on exactly this call. It is the 653 only way to learn the base address through the public interface. */ 654 base = MHD_pool_reset (st->pool, NULL, 0, 0); 655 if (NULL == base) 656 { 657 fz_report ("MHD_pool_reset", "returned NULL for an empty pool"); 658 fz_pool_close (st); 659 return false; 660 } 661 st->base = (uint8_t *) base; 662 return true; 663 } 664 665 666 /* ------------------------------------------------------------------ */ 667 /* The individual operations */ 668 /* ------------------------------------------------------------------ */ 669 670 /** 671 * MHD_pool_allocate(), from the front or from the end. 672 */ 673 static void 674 fz_op_alloc (struct fz_pool *st, 675 const uint8_t *op, 676 bool prefer_end) 677 { 678 static const char *const nm[2] = { "MHD_pool_allocate", 679 "MHD_pool_allocate (from_end)" }; 680 const bool from_end = (0 != (op[1] & 0x01)) || prefer_end; 681 const char *name = nm[from_end ? 1 : 0]; 682 size_t freem; 683 size_t after; 684 size_t size; 685 void *p; 686 687 if (st->nblk >= FZ_MAX_BLOCKS) 688 return; 689 freem = MHD_pool_get_free (st->pool); 690 size = fz_size (op[2], st->cap, freem); 691 p = MHD_pool_allocate (st->pool, size, from_end); 692 after = MHD_pool_get_free (st->pool); 693 if (NULL == p) 694 { 695 stat_alloc_fail++; 696 if (after != freem) 697 fz_report (name, "a failed allocation changed the amount of free space"); 698 if ( (0 != fz_round (size)) && 699 (fz_round (size) <= freem) ) 700 fz_report (name, 701 "allocation failed although MHD_pool_get_free() reported " 702 "room for it"); 703 return; 704 } 705 stat_alloc_ok++; 706 if (after > freem) 707 fz_report (name, "MHD_pool_get_free() increased across an allocation"); 708 else if (freem - after < size) 709 fz_report (name, 710 "MHD_pool_get_free() dropped by less than the allocated size"); 711 fz_check_ptr (st, p, size, name); 712 (void) fz_add (st, p, size, from_end); 713 } 714 715 716 /** 717 * Allocate exactly MHD_pool_get_free() bytes. response.c:2203 does 718 * this and dereferences the result without a NULL check, so it must 719 * always succeed, and it must leave the pool without any free space. 720 */ 721 static void 722 fz_op_alloc_all (struct fz_pool *st, 723 const uint8_t *op) 724 { 725 static const char name[] = "MHD_pool_allocate (all free)"; 726 const bool from_end = (0 != (op[1] & 0x01)); 727 size_t freem; 728 void *p; 729 730 if (st->nblk >= FZ_MAX_BLOCKS) 731 return; 732 freem = MHD_pool_get_free (st->pool); 733 if (0 == freem) 734 return; 735 p = MHD_pool_allocate (st->pool, freem, from_end); 736 if (NULL == p) 737 { 738 stat_alloc_fail++; 739 fz_report (name, 740 "allocating exactly MHD_pool_get_free() bytes failed"); 741 return; 742 } 743 stat_alloc_ok++; 744 fz_check_ptr (st, p, freem, name); 745 if (0 != MHD_pool_get_free (st->pool)) 746 fz_report (name, 747 "the pool still reports free space after all of it was " 748 "allocated"); 749 (void) fz_add (st, p, freem, from_end); 750 } 751 752 753 /** 754 * MHD_pool_try_alloc(). Checks the contract of @a required_bytes. 755 */ 756 static void 757 fz_op_try_alloc (struct fz_pool *st, 758 const uint8_t *op) 759 { 760 static const char name[] = "MHD_pool_try_alloc"; 761 size_t freem; 762 size_t after; 763 size_t size; 764 size_t need; 765 void *p; 766 767 if (st->nblk >= FZ_MAX_BLOCKS) 768 return; 769 freem = MHD_pool_get_free (st->pool); 770 size = fz_size (op[2], st->cap, freem); 771 need = 0x5A5A5A5Au; /* poor man's "was it written at all" */ 772 p = MHD_pool_try_alloc (st->pool, size, &need); 773 after = MHD_pool_get_free (st->pool); 774 if (NULL == p) 775 { 776 stat_alloc_fail++; 777 if (after != freem) 778 fz_report (name, "a failed allocation changed the amount of free space"); 779 if (0 == need) 780 fz_report (name, 781 "the allocation failed but no additional memory is " 782 "reported to be required"); 783 else if ( (SIZE_MAX != need) && 784 (need > st->cap) ) 785 fz_report (name, 786 "more memory is reported to be required than the pool " 787 "has in total"); 788 if ( (0 != fz_round (size)) && 789 (fz_round (size) <= freem) ) 790 fz_report (name, 791 "allocation failed although MHD_pool_get_free() reported " 792 "room for it"); 793 return; 794 } 795 stat_alloc_ok++; 796 if (0 != need) 797 fz_report (name, 798 "the allocation succeeded but required_bytes was not " 799 "cleared"); 800 if (after > freem) 801 fz_report (name, "MHD_pool_get_free() increased across an allocation"); 802 else if (freem - after < size) 803 fz_report (name, 804 "MHD_pool_get_free() dropped by less than the allocated size"); 805 fz_check_ptr (st, p, size, name); 806 /* try_alloc always allocates "from the end" */ 807 (void) fz_add (st, p, size, true); 808 } 809 810 811 /** 812 * The logic of MHD_connection_alloc_memory_() (connection.c:706): when 813 * MHD_pool_try_alloc() fails it names the number of bytes that have to 814 * be freed in the relocatable area; freeing exactly that many bytes by 815 * shrinking a block that is resizable in-place must make the very same 816 * allocation succeed. If it does not, MHD returns "out of memory" for 817 * a request it has the memory for. 818 */ 819 static void 820 fz_op_squeeze (struct fz_pool *st, 821 const uint8_t *op) 822 { 823 static const char name[] = "MHD_pool_try_alloc (squeeze)"; 824 size_t size; 825 size_t need = 0; 826 size_t need2 = 0; 827 void *p; 828 unsigned int i; 829 struct fz_block *victim = NULL; 830 831 if (st->nblk >= FZ_MAX_BLOCKS) 832 return; 833 size = fz_size (op[2], st->cap, MHD_pool_get_free (st->pool)); 834 p = MHD_pool_try_alloc (st->pool, size, &need); 835 if (NULL != p) 836 { 837 fz_check_ptr (st, p, size, name); 838 (void) fz_add (st, p, size, true); 839 return; 840 } 841 if ( (0 == need) || 842 (SIZE_MAX == need) ) 843 return; 844 for (i = 0; i < st->nblk; i++) 845 { 846 if (st->blk[i].from_end) 847 continue; 848 if (st->blk[i].size < need) 849 continue; 850 if (! MHD_pool_is_resizable_inplace (st->pool, 851 st->blk[i].ptr, 852 st->blk[i].size)) 853 continue; 854 victim = &st->blk[i]; 855 break; 856 } 857 if (NULL == victim) 858 return; 859 { 860 const size_t new_size = victim->size - need; 861 void *shrunk = MHD_pool_reallocate (st->pool, 862 victim->ptr, 863 victim->size, 864 new_size); 865 866 if (NULL == shrunk) 867 { 868 fz_report (name, 869 "shrinking a block that is resizable in-place failed"); 870 return; 871 } 872 if (shrunk != victim->ptr) 873 fz_report (name, 874 "shrinking a block that is resizable in-place moved it"); 875 victim->ptr = (uint8_t *) shrunk; 876 victim->size = new_size; 877 fz_verify (victim->ptr, victim->size, victim->tag, name, 878 "shrinking a block damaged its contents"); 879 } 880 p = MHD_pool_try_alloc (st->pool, size, &need2); 881 if (NULL == p) 882 { 883 fz_report (name, 884 "the allocation still fails after freeing the number of " 885 "bytes that required_bytes asked for"); 886 return; 887 } 888 fz_check_ptr (st, p, size, name); 889 (void) fz_add (st, p, size, true); 890 } 891 892 893 /** 894 * MHD_pool_reallocate() of a live block that was allocated from the 895 * front. Blocks allocated "from the end" are excluded: memorypool.c 896 * documents ("Blocks 'from the end' must not be reallocated") and 897 * asserts that. 898 */ 899 static void 900 fz_op_realloc (struct fz_pool *st, 901 const uint8_t *op) 902 { 903 static const char name[] = "MHD_pool_reallocate"; 904 struct fz_block *b = fz_pick (st, op[1], true); 905 struct fz_block saved; 906 size_t freem; 907 size_t after; 908 size_t new_size; 909 bool inplace; 910 void *p; 911 912 if (NULL == b) 913 return; 914 freem = MHD_pool_get_free (st->pool); 915 new_size = fz_size (op[2], st->cap, freem); 916 if ( (0 == b->size) && 917 (new_size > SIZE_MAX - FZ_ALIGN_SIZE) && 918 (! wrap_realloc_enabled) ) 919 return; /* open finding F2, see above */ 920 inplace = MHD_pool_is_resizable_inplace (st->pool, b->ptr, b->size); 921 saved = *b; 922 p = MHD_pool_reallocate (st->pool, b->ptr, b->size, new_size); 923 after = MHD_pool_get_free (st->pool); 924 if (NULL == p) 925 { 926 /* "old continues to be valid for old_size" */ 927 if (after != freem) 928 fz_report (name, 929 "a failed reallocation changed the amount of free space"); 930 fz_verify (saved.ptr, saved.size, saved.tag, name, 931 "a failed reallocation damaged the old block"); 932 if ( (inplace) && 933 (new_size <= saved.size + freem) ) 934 fz_report (name, 935 "reallocation failed although the block is resizable " 936 "in-place and the pool has the required free space"); 937 return; 938 } 939 if ( (inplace) && 940 (p != saved.ptr) ) 941 fz_report (name, 942 "a block that MHD_pool_is_resizable_inplace() accepted was " 943 "moved"); 944 if ( (new_size >= saved.size) && 945 (after > freem) ) 946 fz_report (name, 947 "MHD_pool_get_free() increased across a growing " 948 "reallocation"); 949 /* the old location is gone as far as the model is concerned */ 950 fz_del (st, b); 951 if (p != saved.ptr) 952 stat_realloc_moved++; 953 fz_check_ptr (st, p, new_size, name); 954 fz_verify ((const uint8_t *) p, 955 (new_size < saved.size) ? new_size : saved.size, 956 saved.tag, 957 name, 958 "reallocation did not preserve the contents of the block"); 959 b = &st->blk[st->nblk++]; 960 b->ptr = (uint8_t *) p; 961 b->size = new_size; 962 b->tag = saved.tag; 963 b->from_end = false; 964 if (new_size > saved.size) 965 fz_fill (b->ptr, b->tag, saved.size, new_size); 966 } 967 968 969 /** 970 * MHD_pool_reallocate() with @a old == NULL, which is the documented 971 * way to obtain a fresh relocatable block (connection.c:2121 uses it 972 * for the very first write buffer). 973 */ 974 static void 975 fz_op_realloc_new (struct fz_pool *st, 976 const uint8_t *op) 977 { 978 static const char name[] = "MHD_pool_reallocate (fresh)"; 979 size_t freem; 980 size_t size; 981 void *p; 982 983 if (st->nblk >= FZ_MAX_BLOCKS) 984 return; 985 freem = MHD_pool_get_free (st->pool); 986 size = fz_size (op[2], st->cap, freem); 987 p = MHD_pool_reallocate (st->pool, NULL, 0, size); 988 if (NULL == p) 989 { 990 stat_alloc_fail++; 991 if ( (0 != fz_round (size)) && 992 (fz_round (size) <= freem) ) 993 fz_report (name, 994 "allocation failed although MHD_pool_get_free() reported " 995 "room for it"); 996 return; 997 } 998 stat_alloc_ok++; 999 if (MHD_pool_get_free (st->pool) > freem) 1000 fz_report (name, "MHD_pool_get_free() increased across an allocation"); 1001 fz_check_ptr (st, p, size, name); 1002 (void) fz_add (st, p, size, false); 1003 } 1004 1005 1006 /** 1007 * MHD_pool_deallocate() of a live block, or of NULL ("the NULL is 1008 * tolerated"). A block is always deallocated with the size it 1009 * currently has: memorypool.c asserts that the block lies inside the 1010 * allocated area, and splitting a block is explicitly disallowed. 1011 */ 1012 static void 1013 fz_op_dealloc (struct fz_pool *st, 1014 const uint8_t *op) 1015 { 1016 static const char name[] = "MHD_pool_deallocate"; 1017 struct fz_block *b = fz_pick (st, op[1], false); 1018 size_t freem; 1019 size_t after; 1020 1021 freem = MHD_pool_get_free (st->pool); 1022 if ( (NULL != b) && 1023 (b->from_end) && 1024 (0 != b->size) && 1025 (0 == freem) && 1026 (! full_dealloc_enabled) ) 1027 { 1028 /* See the comment on full_dealloc_enabled: this is an open finding, 1029 not a precondition of MHD_pool_deallocate(). The lowest-addressed 1030 live "from the end" block of non-zero size starts exactly at 1031 pool->end, which for a full pool is also pool->pos -- the 1032 ambiguous case. Any block above it is unaffected, so only that 1033 one block is spared. Zero-sized blocks do not count: they consume 1034 nothing, so they can sit below pool->end once a later 1035 deallocation has moved pool->end back up past them. */ 1036 unsigned int i; 1037 1038 for (i = 0; i < st->nblk; i++) 1039 { 1040 if ( (st->blk[i].from_end) && 1041 (0 != st->blk[i].size) && 1042 (st->blk[i].ptr < b->ptr) ) 1043 break; 1044 } 1045 if (i == st->nblk) 1046 return; 1047 } 1048 if (NULL == b) 1049 { 1050 MHD_pool_deallocate (st->pool, NULL, 0); 1051 } 1052 else 1053 { 1054 fz_verify (b->ptr, b->size, b->tag, name, 1055 "the contents of the block changed before it was freed"); 1056 MHD_pool_deallocate (st->pool, b->ptr, b->size); 1057 fz_del (st, b); 1058 } 1059 after = MHD_pool_get_free (st->pool); 1060 if (after < freem) 1061 fz_report (name, "deallocation reduced the amount of free space"); 1062 } 1063 1064 1065 /** 1066 * MHD_pool_reset(). Preconditions: copy_bytes <= new_size, 1067 * new_size <= size of the pool, and the kept block must really hold 1068 * copy_bytes bytes. 1069 */ 1070 static void 1071 fz_op_reset (struct fz_pool *st, 1072 const uint8_t *op) 1073 { 1074 static const char name[] = "MHD_pool_reset"; 1075 struct fz_block *b = fz_pick (st, (uint8_t) (op[1] & 0x0F), false); 1076 size_t new_size; 1077 size_t copy; 1078 uint8_t tag = 0; 1079 void *p; 1080 1081 new_size = fz_size (op[2], st->cap, MHD_pool_get_free (st->pool)); 1082 if (new_size > st->cap) 1083 new_size = st->cap; 1084 copy = 0; 1085 if (NULL != b) 1086 { 1087 copy = (b->size * ((size_t) (op[1] >> 4))) / 15u; 1088 if (copy > b->size) 1089 copy = b->size; 1090 if (copy > new_size) 1091 copy = new_size; 1092 tag = b->tag; 1093 fz_verify (b->ptr, b->size, b->tag, name, 1094 "the contents of the kept block changed before the reset"); 1095 } 1096 p = MHD_pool_reset (st->pool, 1097 (NULL != b) ? b->ptr : NULL, 1098 copy, 1099 new_size); 1100 stat_resets++; 1101 /* every allocation is gone now */ 1102 st->nblk = 0; 1103 if (NULL == p) 1104 { 1105 fz_report (name, "returned NULL"); 1106 return; 1107 } 1108 fz_check_ptr (st, p, new_size, name); 1109 fz_verify ((const uint8_t *) p, copy, tag, name, 1110 "the kept bytes did not survive the reset"); 1111 b = fz_add (st, p, new_size, false); 1112 /* fz_add() has just re-tagged and re-filled the whole block */ 1113 } 1114 1115 1116 /** 1117 * MHD_pool_is_resizable_inplace() on a live block, or on NULL. 1118 */ 1119 static void 1120 fz_op_resizable (struct fz_pool *st, 1121 const uint8_t *op) 1122 { 1123 static const char name[] = "MHD_pool_is_resizable_inplace"; 1124 struct fz_block *b = fz_pick (st, op[1], false); 1125 bool r; 1126 1127 if ( (NULL == b) || 1128 (0 != (op[1] & 0x80)) ) 1129 { 1130 if (MHD_pool_is_resizable_inplace (st->pool, NULL, 0)) 1131 fz_report (name, "an unallocated block is reported as resizable"); 1132 return; 1133 } 1134 r = MHD_pool_is_resizable_inplace (st->pool, b->ptr, b->size); 1135 if ( (r) && 1136 (b->from_end) && 1137 (0 != b->size) ) 1138 fz_report (name, 1139 "a block allocated from the end is reported as resizable " 1140 "in-place"); 1141 if (! r) 1142 return; 1143 /* A block that is resizable in-place must survive a resize to its own 1144 size without moving; connection.c asserts this after every such 1145 reallocation. */ 1146 { 1147 void *p = MHD_pool_reallocate (st->pool, b->ptr, b->size, b->size); 1148 1149 if (NULL == p) 1150 fz_report (name, 1151 "reallocating a resizable block to its own size failed"); 1152 else if (p != b->ptr) 1153 fz_report (name, 1154 "reallocating a resizable block to its own size moved it"); 1155 } 1156 } 1157 1158 1159 static void 1160 fz_do_op (struct fz_pool *st, 1161 const uint8_t *op, 1162 bool prefer_end) 1163 { 1164 const char *name = "operation"; 1165 1166 stat_ops++; 1167 switch ((enum fz_op) (op[0] % (uint8_t) FZ_OP_COUNT)) 1168 { 1169 case FZ_OP_ALLOC: 1170 fz_op_alloc (st, op, prefer_end); 1171 break; 1172 case FZ_OP_ALLOC_ALL: 1173 fz_op_alloc_all (st, op); 1174 break; 1175 case FZ_OP_TRY_ALLOC: 1176 fz_op_try_alloc (st, op); 1177 break; 1178 case FZ_OP_SQUEEZE: 1179 fz_op_squeeze (st, op); 1180 break; 1181 case FZ_OP_REALLOC: 1182 fz_op_realloc (st, op); 1183 break; 1184 case FZ_OP_REALLOC_NEW: 1185 fz_op_realloc_new (st, op); 1186 break; 1187 case FZ_OP_DEALLOC: 1188 fz_op_dealloc (st, op); 1189 break; 1190 case FZ_OP_RESET: 1191 fz_op_reset (st, op); 1192 break; 1193 case FZ_OP_GET_FREE: 1194 name = "MHD_pool_get_free"; 1195 break; 1196 case FZ_OP_RESIZABLE: 1197 fz_op_resizable (st, op); 1198 break; 1199 case FZ_OP_VERIFY: 1200 name = "verify"; 1201 fz_verify_all (st, name); 1202 break; 1203 case FZ_OP_RECREATE: 1204 fz_pool_close (st); 1205 if (! fz_pool_open (st, op[1])) 1206 return; 1207 break; 1208 case FZ_OP_COUNT: 1209 default: 1210 break; 1211 } 1212 fz_check_invariants (st, name); 1213 } 1214 1215 1216 int 1217 LLVMFuzzerTestOneInput (const uint8_t *data, 1218 size_t size) 1219 { 1220 static bool inited = false; 1221 struct fz_pool st; 1222 size_t nops; 1223 size_t i; 1224 1225 if (size < 5) 1226 return 0; 1227 if (! inited) 1228 { 1229 inited = true; 1230 /* Sets the page size used by MHD_pool_create(); MHD does this once 1231 from MHD_start_daemon(). */ 1232 MHD_init_mem_pools_ (); 1233 (void) atexit (&print_stats); 1234 } 1235 if (! known_bugs_read) 1236 { 1237 const char *e; 1238 1239 known_bugs_read = 1; 1240 e = getenv ("MHD_FUZZ_POOL_FULL_DEALLOC"); 1241 if (NULL != e) 1242 full_dealloc_enabled = (0 != atoi (e)); 1243 e = getenv ("MHD_FUZZ_POOL_WRAP_REALLOC"); 1244 if (NULL != e) 1245 wrap_realloc_enabled = (0 != atoi (e)); 1246 } 1247 memset (&st, 0, sizeof (st)); 1248 st.paranoid = (0 != (data[1] & 0x01)); 1249 if (! fz_pool_open (&st, data[0])) 1250 return 0; 1251 nops = (size - 2) / 3; 1252 if (nops > FZ_MAX_OPS) 1253 nops = FZ_MAX_OPS; 1254 for (i = 0; i < nops; i++) 1255 { 1256 if (NULL == st.pool) 1257 break; 1258 fz_do_op (&st, data + 2 + 3 * i, (0 != (data[1] & 0x02))); 1259 } 1260 fz_verify_all (&st, "final"); 1261 fz_pool_close (&st); /* MHD_pool_destroy() tolerates a NULL pool */ 1262 return 0; 1263 } 1264 1265 1266 /* ------------------------------------------------------------------ */ 1267 /* Generator */ 1268 /* ------------------------------------------------------------------ */ 1269 1270 /** 1271 * Opcodes, weighted: the allocating operations and the reallocation are 1272 * what move the pool's internal offsets around, so they get most of the 1273 * probability mass, while the read-only queries and the destructive 1274 * reset and re-create are rarer. 1275 */ 1276 static const uint8_t gen_ops[] = { 1277 FZ_OP_ALLOC, FZ_OP_ALLOC, FZ_OP_ALLOC, FZ_OP_ALLOC, 1278 FZ_OP_ALLOC_ALL, 1279 FZ_OP_TRY_ALLOC, FZ_OP_TRY_ALLOC, 1280 FZ_OP_SQUEEZE, FZ_OP_SQUEEZE, 1281 FZ_OP_REALLOC, FZ_OP_REALLOC, FZ_OP_REALLOC, FZ_OP_REALLOC, 1282 FZ_OP_REALLOC_NEW, 1283 FZ_OP_DEALLOC, FZ_OP_DEALLOC, FZ_OP_DEALLOC, 1284 FZ_OP_RESET, 1285 FZ_OP_GET_FREE, 1286 FZ_OP_RESIZABLE, FZ_OP_RESIZABLE, 1287 FZ_OP_VERIFY, 1288 FZ_OP_RECREATE 1289 }; 1290 1291 1292 /** 1293 * Size selector bytes that are worth trying often: the boundary cases 1294 * (exactly the free space, one alignment unit more or less) and the 1295 * value-wrap guards. 1296 */ 1297 static uint8_t 1298 gen_size_sel (struct fuzz_rng *rng) 1299 { 1300 const uint32_t k = fuzz_below (rng, 100); 1301 uint32_t d; 1302 uint32_t sign; 1303 1304 if (k < 35) /* tiny */ 1305 return (uint8_t) ((fuzz_below (rng, 64) << 2) | 0u); 1306 if (k < 60) /* fraction of the pool */ 1307 return (uint8_t) ((fuzz_below (rng, 64) << 2) | 1u); 1308 if (k < 95) 1309 { /* around the free space */ 1310 /* Two statements, not one expression: the order in which the 1311 operands of '|' are evaluated is unspecified, and both calls 1312 advance the PRNG, so a single expression would make the generated 1313 stream compiler-dependent. */ 1314 d = fuzz_below (rng, 8); 1315 sign = fuzz_below (rng, 2); 1316 return (uint8_t) ((d << 2) | (sign << 5) | 2u); 1317 } 1318 return (uint8_t) ((fuzz_below (rng, 64) << 2) | 3u); /* value wrap */ 1319 } 1320 1321 1322 static size_t 1323 fuzz_generate (struct fuzz_rng *rng, 1324 uint8_t *buf, 1325 size_t cap) 1326 { 1327 size_t len = 0; 1328 unsigned int nops; 1329 unsigned int i; 1330 unsigned int psel; 1331 uint8_t flags = 0; 1332 1333 if (cap < 8) 1334 return 0; 1335 /* Small pools are where the interesting paths are, so bias towards 1336 them, but keep the whole table reachable. */ 1337 if (fuzz_chance (rng, 3)) 1338 psel = fuzz_below (rng, (uint32_t) FZ_NUM_POOL_SIZES); 1339 else 1340 psel = fuzz_below (rng, 16); 1341 buf[len++] = (uint8_t) psel; 1342 /* One PRNG call per statement, see gen_size_sel(). */ 1343 if (fuzz_chance (rng, 4)) 1344 flags |= 0x01; 1345 if (fuzz_chance (rng, 8)) 1346 flags |= 0x02; 1347 buf[len++] = flags; 1348 nops = 1 + fuzz_below (rng, FZ_MAX_OPS); 1349 for (i = 0; i < nops; i++) 1350 { 1351 if (len + 3 > cap) 1352 break; 1353 buf[len++] = gen_ops[fuzz_below (rng, (uint32_t) (sizeof (gen_ops)))]; 1354 buf[len++] = fuzz_byte (rng); 1355 buf[len++] = gen_size_sel (rng); 1356 } 1357 return len; 1358 } 1359 1360 1361 /* ------------------------------------------------------------------ */ 1362 /* Seed corpus */ 1363 /* ------------------------------------------------------------------ */ 1364 1365 /* Size selectors, see fz_size() */ 1366 #define SZ_TINY(n) ((uint8_t) ((((unsigned int) (n)) << 2) | 0u)) 1367 #define SZ_FRAC(k) ((uint8_t) ((((unsigned int) (k)) << 2) | 1u)) 1368 #define SZ_FREE ((uint8_t) 2u) 1369 #define SZ_FREE_M(d) ((uint8_t) ((((unsigned int) (d)) << 2) | 2u)) 1370 #define SZ_FREE_P(d) ((uint8_t) ((((unsigned int) (d)) << 2) | 0x20u | 2u)) 1371 #define SZ_HUGE ((uint8_t) 3u) 1372 1373 /* Pool size selectors, indices into fz_pool_sizes[] */ 1374 #define PS_1 0 1375 #define PS_16 2 1376 #define PS_32 4 1377 #define PS_64 6 1378 #define PS_128 8 1379 #define PS_256 11 1380 #define PS_512 14 1381 #define PS_1024 16 1382 #define PS_1500 18 1383 #define PS_32768 23 1384 1385 #define OPR(o,b,s) ((uint8_t) (o)), ((uint8_t) (b)), (s) 1386 1387 static const uint8_t seed_grow_shrink[] = { 1388 PS_512, 0x01, 1389 OPR (FZ_OP_ALLOC, 0x00, SZ_TINY (32)), 1390 OPR (FZ_OP_REALLOC, 0x00, SZ_TINY (63)), 1391 OPR (FZ_OP_REALLOC, 0x00, SZ_TINY (8)), 1392 OPR (FZ_OP_REALLOC, 0x00, SZ_FRAC (32)), 1393 OPR (FZ_OP_VERIFY, 0x00, SZ_TINY (0)), 1394 OPR (FZ_OP_REALLOC, 0x00, SZ_TINY (1)), 1395 OPR (FZ_OP_DEALLOC, 0x00, SZ_TINY (0)) 1396 }; 1397 1398 static const uint8_t seed_two_blocks_realloc[] = { 1399 PS_256, 0x01, 1400 OPR (FZ_OP_ALLOC, 0x00, SZ_TINY (17)), 1401 OPR (FZ_OP_ALLOC, 0x00, SZ_TINY (23)), 1402 /* the first block is no longer the last one: it has to move */ 1403 OPR (FZ_OP_REALLOC, 0x00, SZ_TINY (60)), 1404 OPR (FZ_OP_VERIFY, 0x00, SZ_TINY (0)), 1405 OPR (FZ_OP_REALLOC, 0x01, SZ_TINY (40)), 1406 OPR (FZ_OP_VERIFY, 0x00, SZ_TINY (0)) 1407 }; 1408 1409 static const uint8_t seed_from_end[] = { 1410 PS_128, 0x01, 1411 OPR (FZ_OP_ALLOC, 0x01, SZ_TINY (16)), 1412 OPR (FZ_OP_ALLOC, 0x01, SZ_TINY (1)), 1413 OPR (FZ_OP_ALLOC, 0x00, SZ_TINY (16)), 1414 OPR (FZ_OP_RESIZABLE, 0x00, SZ_TINY (0)), 1415 OPR (FZ_OP_DEALLOC, 0x00, SZ_TINY (0)), 1416 OPR (FZ_OP_ALLOC, 0x01, SZ_TINY (16)) 1417 }; 1418 1419 static const uint8_t seed_fill_exactly[] = { 1420 PS_128, 0x01, 1421 OPR (FZ_OP_ALLOC, 0x01, SZ_TINY (16)), 1422 OPR (FZ_OP_ALLOC_ALL, 0x00, SZ_TINY (0)), 1423 OPR (FZ_OP_GET_FREE, 0x00, SZ_TINY (0)), 1424 OPR (FZ_OP_ALLOC, 0x00, SZ_TINY (1)), 1425 OPR (FZ_OP_TRY_ALLOC, 0x00, SZ_TINY (1)) 1426 }; 1427 1428 static const uint8_t seed_squeeze[] = { 1429 PS_512, 0x01, 1430 OPR (FZ_OP_ALLOC, 0x00, SZ_FREE), 1431 OPR (FZ_OP_SQUEEZE, 0x00, SZ_TINY (40)), 1432 OPR (FZ_OP_SQUEEZE, 0x00, SZ_TINY (7)), 1433 OPR (FZ_OP_VERIFY, 0x00, SZ_TINY (0)), 1434 OPR (FZ_OP_SQUEEZE, 0x00, SZ_FRAC (8)) 1435 }; 1436 1437 static const uint8_t seed_squeeze_small[] = { 1438 PS_128, 0x01, 1439 OPR (FZ_OP_REALLOC_NEW, 0x00, SZ_FREE_M (2)), 1440 OPR (FZ_OP_SQUEEZE, 0x00, SZ_TINY (17)), 1441 OPR (FZ_OP_SQUEEZE, 0x00, SZ_TINY (17)), 1442 OPR (FZ_OP_SQUEEZE, 0x00, SZ_TINY (17)) 1443 }; 1444 1445 static const uint8_t seed_reset_keep[] = { 1446 PS_1500, 0x01, 1447 OPR (FZ_OP_ALLOC, 0x00, SZ_TINY (60)), 1448 OPR (FZ_OP_ALLOC, 0x01, SZ_TINY (12)), 1449 OPR (FZ_OP_RESET, 0xF0, SZ_FRAC (31)), 1450 OPR (FZ_OP_VERIFY, 0x00, SZ_TINY (0)), 1451 OPR (FZ_OP_RESET, 0xF0, SZ_TINY (4)), 1452 OPR (FZ_OP_RESET, 0x00, SZ_TINY (0)) 1453 }; 1454 1455 static const uint8_t seed_reset_full[] = { 1456 PS_256, 0x01, 1457 OPR (FZ_OP_ALLOC, 0x00, SZ_FRAC (63)), 1458 OPR (FZ_OP_RESET, 0xF0, SZ_FRAC (63)), 1459 OPR (FZ_OP_RESET, 0xF0, SZ_FREE_P (1)), 1460 OPR (FZ_OP_VERIFY, 0x00, SZ_TINY (0)) 1461 }; 1462 1463 static const uint8_t seed_boundary[] = { 1464 PS_64, 0x01, 1465 OPR (FZ_OP_ALLOC, 0x00, SZ_FREE_M (1)), 1466 OPR (FZ_OP_ALLOC, 0x00, SZ_FREE), 1467 OPR (FZ_OP_ALLOC, 0x01, SZ_FREE_P (1)), 1468 OPR (FZ_OP_TRY_ALLOC, 0x00, SZ_FREE), 1469 OPR (FZ_OP_DEALLOC, 0x00, SZ_TINY (0)), 1470 OPR (FZ_OP_ALLOC, 0x00, SZ_FREE) 1471 }; 1472 1473 static const uint8_t seed_wrap[] = { 1474 PS_256, 0x00, 1475 OPR (FZ_OP_ALLOC, 0x00, SZ_HUGE), 1476 OPR (FZ_OP_ALLOC, 0x01, SZ_HUGE), 1477 OPR (FZ_OP_TRY_ALLOC, 0x00, SZ_HUGE), 1478 OPR (FZ_OP_REALLOC_NEW, 0x00, SZ_HUGE), 1479 OPR (FZ_OP_ALLOC, 0x00, SZ_TINY (16)), 1480 OPR (FZ_OP_REALLOC, 0x00, SZ_HUGE), 1481 OPR (FZ_OP_VERIFY, 0x00, SZ_TINY (0)) 1482 }; 1483 1484 static const uint8_t seed_tiny_pool[] = { 1485 PS_1, 0x01, 1486 OPR (FZ_OP_ALLOC, 0x00, SZ_TINY (1)), 1487 OPR (FZ_OP_ALLOC, 0x01, SZ_TINY (1)), 1488 OPR (FZ_OP_GET_FREE, 0x00, SZ_TINY (0)), 1489 OPR (FZ_OP_RESET, 0xF0, SZ_TINY (1)), 1490 OPR (FZ_OP_DEALLOC, 0x00, SZ_TINY (0)) 1491 }; 1492 1493 static const uint8_t seed_zero_sizes[] = { 1494 PS_32, 0x01, 1495 OPR (FZ_OP_ALLOC, 0x00, SZ_TINY (0)), 1496 OPR (FZ_OP_ALLOC, 0x01, SZ_TINY (0)), 1497 OPR (FZ_OP_REALLOC, 0x00, SZ_TINY (0)), 1498 OPR (FZ_OP_REALLOC, 0x00, SZ_TINY (4)), 1499 OPR (FZ_OP_DEALLOC, 0x00, SZ_TINY (0)), 1500 OPR (FZ_OP_RESET, 0x00, SZ_TINY (0)) 1501 }; 1502 1503 static const uint8_t seed_many_small[] = { 1504 PS_1500, 0x01, 1505 OPR (FZ_OP_ALLOC, 0x00, SZ_TINY (5)), 1506 OPR (FZ_OP_ALLOC, 0x01, SZ_TINY (5)), 1507 OPR (FZ_OP_ALLOC, 0x00, SZ_TINY (5)), 1508 OPR (FZ_OP_ALLOC, 0x01, SZ_TINY (5)), 1509 OPR (FZ_OP_ALLOC, 0x00, SZ_TINY (5)), 1510 OPR (FZ_OP_ALLOC, 0x01, SZ_TINY (5)), 1511 OPR (FZ_OP_REALLOC, 0x00, SZ_TINY (33)), 1512 OPR (FZ_OP_REALLOC, 0x01, SZ_TINY (33)), 1513 OPR (FZ_OP_DEALLOC, 0x02, SZ_TINY (0)), 1514 OPR (FZ_OP_VERIFY, 0x00, SZ_TINY (0)), 1515 OPR (FZ_OP_ALLOC, 0x00, SZ_TINY (5)) 1516 }; 1517 1518 static const uint8_t seed_recreate[] = { 1519 PS_32768, 0x00, 1520 OPR (FZ_OP_ALLOC, 0x00, SZ_FRAC (60)), 1521 OPR (FZ_OP_RECREATE, PS_16, SZ_TINY (0)), 1522 OPR (FZ_OP_ALLOC, 0x00, SZ_FREE), 1523 OPR (FZ_OP_RECREATE, PS_512, SZ_TINY (0)), 1524 OPR (FZ_OP_ALLOC, 0x01, SZ_TINY (24)), 1525 OPR (FZ_OP_RESET, 0xF0, SZ_FRAC (2)) 1526 }; 1527 1528 /* The read-buffer life cycle of a connection: allocate the read buffer, 1529 grow it to everything that is free, put the parsed headers "at the 1530 end", shrink the read buffer to what was actually received, then 1531 reset the pool keeping the received bytes -- connection.c does 1532 exactly this for every keep-alive request. */ 1533 static const uint8_t seed_connection_cycle[] = { 1534 PS_1500, 0x01, 1535 OPR (FZ_OP_REALLOC_NEW, 0x00, SZ_FRAC (31)), 1536 OPR (FZ_OP_REALLOC, 0x00, SZ_FREE), 1537 OPR (FZ_OP_TRY_ALLOC, 0x00, SZ_TINY (12)), 1538 OPR (FZ_OP_TRY_ALLOC, 0x00, SZ_TINY (20)), 1539 OPR (FZ_OP_SQUEEZE, 0x00, SZ_TINY (40)), 1540 OPR (FZ_OP_REALLOC, 0x00, SZ_TINY (60)), 1541 OPR (FZ_OP_VERIFY, 0x00, SZ_TINY (0)), 1542 OPR (FZ_OP_RESET, 0xF0, SZ_FRAC (31)), 1543 OPR (FZ_OP_REALLOC, 0x00, SZ_FREE), 1544 OPR (FZ_OP_VERIFY, 0x00, SZ_TINY (0)) 1545 }; 1546 1547 1548 /* The reproducer of the open finding described at full_dealloc_enabled: 1549 a 128 byte pool, one 16 byte block "from the end", the whole rest of 1550 the pool allocated from the front, then that first block released. 1551 Inert unless MHD_FUZZ_POOL_FULL_DEALLOC=1 is set. */ 1552 static const uint8_t seed_dealloc_end_full[] = { 1553 PS_128, 0x01, 1554 OPR (FZ_OP_ALLOC, 0x01, SZ_TINY (16)), 1555 OPR (FZ_OP_ALLOC_ALL, 0x00, SZ_TINY (0)), 1556 OPR (FZ_OP_DEALLOC, 0x00, SZ_TINY (0)) 1557 }; 1558 1559 1560 /* The reproducer of the open finding F2: MHD_pool_reset() with nothing 1561 to keep leaves a zero-length block at the front boundary, and 1562 reallocating that to SIZE_MAX wraps. Inert unless 1563 MHD_FUZZ_POOL_WRAP_REALLOC=1 is set. */ 1564 static const uint8_t seed_realloc_wrap[] = { 1565 PS_1024, 0x01, 1566 OPR (FZ_OP_RESET, 0x00, SZ_TINY (0)), 1567 OPR (FZ_OP_REALLOC, 0x00, SZ_HUGE) 1568 }; 1569 1570 1571 struct mp_seed 1572 { 1573 const uint8_t *bytes; 1574 size_t len; 1575 }; 1576 1577 #define MSEED(a) { (a), sizeof (a) } 1578 1579 static const struct mp_seed mp_seeds[] = { 1580 MSEED (seed_grow_shrink), 1581 MSEED (seed_two_blocks_realloc), 1582 MSEED (seed_from_end), 1583 MSEED (seed_fill_exactly), 1584 MSEED (seed_squeeze), 1585 MSEED (seed_squeeze_small), 1586 MSEED (seed_reset_keep), 1587 MSEED (seed_reset_full), 1588 MSEED (seed_boundary), 1589 MSEED (seed_wrap), 1590 MSEED (seed_tiny_pool), 1591 MSEED (seed_zero_sizes), 1592 MSEED (seed_many_small), 1593 MSEED (seed_recreate), 1594 MSEED (seed_connection_cycle), 1595 MSEED (seed_dealloc_end_full), 1596 MSEED (seed_realloc_wrap) 1597 }; 1598 1599 1600 static size_t 1601 fuzz_seed_count (void) 1602 { 1603 return sizeof (mp_seeds) / sizeof (mp_seeds[0]); 1604 } 1605 1606 1607 static const uint8_t * 1608 fuzz_seed_get (size_t idx, 1609 size_t *len) 1610 { 1611 *len = mp_seeds[idx].len; 1612 return mp_seeds[idx].bytes; 1613 }