DF-2797 / trigger_domain.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 | /* * trigger_domain.c - alist release-kernel domain-validation triggers * (DF-2797 candidate). Compiles the real sys/kern/subr_alist.c in * userland with -DNDEBUG to emulate a production (non-INVARIANTS) * kernel where KKASSERT is a no-op. * * T1: alist_alloc(bl, 0, 0) on a FULLY-FREE leaf -> whole 32-block * leaf silently allocated for a "0-block" request (capacity theft, * bl_free left unchanged). 2048 calls exhaust a 65536-block alist * while bl_free still claims 65536 free; a following 1-block * allocation fails. * T2: alist_alloc(bl, 0, 0) on a PARTIALLY-FREE leaf -> infinite loop * in alst_leaf_alloc's `for (j = 0; j <= n; j += count)' (j += 0). * In-kernel this spins with the caller's lock held (vm_contig_spin * for vm_contig_alist) => permanent system hang. * T3: alist_create(blocks > 2^29) -> `radix *= ALIST_META_RADIX' wraps * to 0 -> infinite loop in the radix computation. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdint.h> #include <unistd.h> #include <signal.h> #include <sys/wait.h> #define main alist_debug_main #include "kern/subr_alist.c" #undef main static struct alist bl; static almeta_t records[ALIST_RECORDS_65536]; static void hang_handler(int sig __unused) { printf(" [SIGALRM] HANG REPRODUCED: still spinning after 5s " "(in-kernel: spinlock held, system hang)\n"); _exit(42); } static void t1_leafwipe(void) { int i, wiped = 0; alist_blk_t r, probe; alist_init(&bl, 65536, records, ALIST_RECORDS_65536); alist_free(&bl, 0, 65536); /* everything free */ for (i = 0; i < 2048 + 16; ++i) { r = alist_alloc(&bl, 0, 0); /* count == 0 */ if (r != ALIST_BLOCK_NONE) wiped++; } probe = alist_alloc(&bl, 0, 1); printf("T1 leaf-wipe: %d zero-count allocs 'succeeded'; " "bl_free=%u (of 65536); 1-block alloc now -> %s\n" " => 32*%d = %d blocks (%d KB of DMA reserve) stolen " "silently; free-count says full\n", wiped, bl.bl_free, probe == ALIST_BLOCK_NONE ? "NONE (FAILURE)" : "ok", wiped, wiped * 32, wiped * 32 * 4); } static void t2_hang(void) { signal(SIGALRM, hang_handler); alarm(5); alist_init(&bl, 64, records, ALIST_RECORDS_65536); alist_free(&bl, 0, 16); /* leaf 0 partially free (16/32) */ printf("T2 hang: calling alist_alloc(bl, 0, 0) on partially-free " "leaf...\n"); fflush(stdout); /* infinite loop: for (j = 0; j <= n; j += count) with count == 0 */ alist_alloc(&bl, 0, 0); printf(" UNEXPECTEDLY returned\n"); _exit(0); } static void t3_createwrap(void) { signal(SIGALRM, hang_handler); alarm(5); printf("T3 create-wrap: alist_create(3000000000) ...\n"); fflush(stdout); alist_create(3000000000, NULL); /* radix wraps to 0, loops forever */ printf(" UNEXPECTEDLY returned\n"); _exit(0); } int main(void) { pid_t p; int st; setvbuf(stdout, NULL, _IONBF, 0); printf("== T1 (silent leaf wipe, count==0) ==\n"); t1_leafwipe(); fflush(stdout); printf("== T2 (infinite loop, count==0 partial leaf) ==\n"); p = fork(); if (p == 0) t2_hang(); waitpid(p, &st, 0); printf(" child exit=%d (42 == hung & alarm-killed)\n", WEXITSTATUS(st)); printf("== T3 (alist_create radix wrap) ==\n"); p = fork(); if (p == 0) t3_createwrap(); waitpid(p, &st, 0); printf(" child exit=%d (42 == hung & alarm-killed)\n", WEXITSTATUS(st)); return 0; } |