DF-1286 / harness.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 | /* * DF-1286 harness: SPROM rev4/5/8 TXPID parser reads kernel heap at negative * array indices (unported Linux-relative offset constants) in siba_sprom_r458. * * Object-level proof. The bwn/siba driver attaches to Broadcom BCM43xx WiFi; * none is present in the audit QEMU guest, so siba_sprom_r458 is not runtime- * reachable here. This harness replays the exact SIBA_OFFSET indexing with the * actual constant values from sibareg.h to PROVE the negative-index reads. * * Cited path: sys/dev/netif/bwn/siba/siba_core.c:1509 (SIBA_OFFSET macro), * :1513 (SIBA_SHIFTOUT macro = in[SIBA_OFFSET(...)]), * :1591-1628 (siba_sprom_r458, 16 TXPID reads). * Constants: sibareg.h:305 SIBA_SPROM_BASE=0x1000, * :363-386 SIBA_SPROM4_TXPID* = 0x0062..0x0070 (UNPORTED Linux-relative). * All sibling SPROM4 offset constants are absolute (0x10xx, e.g. * SIBA_SPROM4_MAC_80211BG=0x104c, MAXP_BG=0x1080) -- the TXPID block alone * was left as Linux-style relative word offsets. * * Build: cc -O2 -o harness harness.c * Run: ./harness */ #include <stdio.h> #include <stdlib.h> #include <stdint.h> #include <string.h> #define SIBA_SPROM_BASE 0x1000 /* the buggy (as-shipped) constants */ #define BUG_TXPID2G01 0x0062 #define BUG_TXPID2G23 0x0064 #define BUG_TXPID5G01 0x0066 #define BUG_TXPID5G23 0x0068 #define BUG_TXPID5GL01 0x006a #define BUG_TXPID5GL23 0x006c #define BUG_TXPID5GH01 0x006e #define BUG_TXPID5GH23 0x0070 /* the fixed constants (absolute, +0x1000) */ #define FIX_TXPID2G01 0x1062 #define FIX_TXPID5GH23 0x1070 /* exact kernel macro, siba_core.c:1509 */ #define SIBA_OFFSET(offset) (((offset) - SIBA_SPROM_BASE) / sizeof(uint16_t)) /* exact kernel macro, siba_core.c:1513 -- reads in[SIBA_OFFSET(offset)] */ #define SIBA_SHIFTOUT(in, offset) ((in)[SIBA_OFFSET(offset)]) #define SPROM_WORDS 220 /* typical SPROM image size in uint16 words */ #define PREFIX_WORDS 2100 /* >= 1999 to cover the worst negative idx */ int main(void) { /* Simulate the kernel heap: a large "prefix" (residue) region BEFORE the * SPROM buffer, then the SPROM buffer itself. In the kernel the SPROM * buffer is a kmalloc'd region; in[-1999] reads 3998 bytes earlier into * unrelated slab/kernel heap. */ uint16_t *prefix = calloc(PREFIX_WORDS, sizeof(uint16_t)); uint16_t *sprom = calloc(SPROM_WORDS, sizeof(uint16_t)); /* paint the prefix with a recognizable "leaked heap" pattern */ for (int i = 0; i < PREFIX_WORDS; i++) prefix[i] = 0xBE00 | (i & 0xff); /* the real SPROM words at the TXPID locations (fixed-offset view): */ sprom[SIBA_OFFSET(FIX_TXPID2G01)] = 0x1111; sprom[SIBA_OFFSET(FIX_TXPID5GH23)] = 0x8888; printf("[DF-1286] SIBA_SPROM_BASE=0x%x, sizeof(word)=%zu\n", SIBA_SPROM_BASE, sizeof(uint16_t)); printf("[DF-1286] SPROM buffer = %d words; TXPID reads use SIBA_SHIFTOUT " "(in[SIBA_OFFSET(offset)])\n\n", SPROM_WORDS); puts("=== AS-SHIPPED (buggy) TXPID constants 0x0062..0x0070 ==="); int buggy_offsets[] = { BUG_TXPID2G01, BUG_TXPID2G23, BUG_TXPID5G01, BUG_TXPID5G23, BUG_TXPID5GL01, BUG_TXPID5GL23, BUG_TXPID5GH01, BUG_TXPID5GH23 }; int any_neg = 0; for (int k = 0; k < 8; k++) { int idx = SIBA_OFFSET(buggy_offsets[k]); printf(" constant 0x%04x -> SIBA_OFFSET = %d ", buggy_offsets[k], idx); if (idx < 0) { printf("=> NEGATIVE: in[%d] reads %d bytes BEFORE the SPROM " "buffer (heap leak)\n", idx, idx * (int)sizeof(uint16_t)); any_neg = 1; } else { printf("(valid)\n"); } } /* Demonstrate the leak concretely: the buggy code reads prefix bytes. */ int worst = SIBA_OFFSET(BUG_TXPID2G01); /* -1999 */ printf("\n[DF-1286] Worst case: in[%d] reads bytes [%d..%d] relative to " "SPROM base (i.e. %d bytes before the allocation).\n", worst, worst*2, worst*2+1, worst*2); /* In the kernel, 'in' points at the SPROM kmalloc. To show what gets * leaked, treat prefix as the slab memory preceding the allocation: */ uint16_t leaked = prefix[PREFIX_WORDS + worst]; /* prefix[-1999]-equiv */ printf("[DF-1286] Simulated leak: buggy txpid2g[0] = 0x%04x (heap residue " "before SPROM buf), NOT the real SPROM TXPID value.\n", leaked); puts("\n=== FIXED constants 0x1062..0x1070 (absolute, like all siblings) ==="); printf(" SIBA_OFFSET(0x1062) = %d -> in[%d] reads the REAL SPROM word\n", SIBA_OFFSET(FIX_TXPID2G01), SIBA_OFFSET(FIX_TXPID2G01)); printf(" fixed txpid2g[0] = 0x%04x (matches the SPROM image)\n", SIBA_SHIFTOUT(sprom, FIX_TXPID2G01)); printf(" fixed txpid5gh[3] = 0x%04x\n", SIBA_SHIFTOUT(sprom, FIX_TXPID5GH23)); puts(""); printf("[DF-1286] RESULT: %s -- %d bytes of kernel heap read before the " "SPROM buffer per SPROM rev4/5/8 parse (16 reads total = 32 bytes),\n" " surfacing via SIBA_SPROMVAR_TXPID_* sysctl spromvars " "(KASLR/heap-info leak).\n", any_neg ? "BUG CONFIRMED" : "no bug", -worst*2); free(prefix); free(sprom); return any_neg ? 0 : 2; } |