DF-1040 / cis_oob_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 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 | /* * DF-1040 โ userspace logic harness (object-level proof for a latent bug). * * The vulnerable function pccard_scan_cis() in sys/bus/pccard/pccard_cis.c * cannot be invoked on this audit guest because the KVM/QEMU machine has no * PCMCIA/CardBus bridge hardware (pccard is compiled into GENERIC but there * is no cbb/pcic bridge device to attach a card to, so pccard_attach_card() * is never called and pccard_scan_cis() is unreachable at runtime). * * This harness replicates the EXACT offset-computation logic of the * chain-transition loop at pccard_cis.c:386-437 to demonstrate, at the * object/logic level, that a card-supplied longlink_addr of 0xFFFFFFFF * produces a bus offset of 0x1FFFFFFFE for the dereference at line 413 โ * wildly outside the 4096-byte attribute-memory mapping allocated at * lines 130-131. On real hardware this offset is fed to bus_space_read_1() * and page-faults the kernel. * * The harness also demonstrates that the proposed fix.diff rejects the * out-of-bounds target before the dereference. * * Build: cc -O2 -o cis_oob_harness cis_oob_harness.c * Run: ./cis_oob_harness */ #include <stdio.h> #include <stdint.h> #include <stddef.h> /* Mirror of sys/bus/pccard/pccard_cis.c:62 */ #define PCCARD_CIS_SIZE 4096 /* Mirror of pccard_cis_read_1() in sys/bus/pccard/pccardvar.h:255-256: * bus_space_read_1((tuple)->memt, (tuple)->memh, (tuple)->mult*(idx0)) * We model the byte offset that would be passed to bus_space_read_1. */ static uint64_t cis_byte_offset(unsigned long mult, unsigned long ptr) { return (uint64_t)mult * (uint64_t)ptr; } /* Replicate the chain-transition pointer setup (pccard_cis.c:393-394, 405-406) * and the unguarded dereference (pccard_cis.c:413). Returns the byte offset * that bus_space_read_1 would be asked to read. */ static uint64_t chain_transition_deref(unsigned long mult, unsigned long ptr) { /* pccard_cis.c:393/405 */ /* tuple.mult = ...; */ /* pccard_cis.c:394/406 */ /* tuple.ptr = ...; */ /* pccard_cis.c:413 */ /* tuple.code = pccard_cis_read_1(&tuple, tuple.ptr); */ return cis_byte_offset(mult, ptr); /* == mult * ptr */ } /* Replicate the proposed fix: reject targets that don't leave room for the * 5-byte LINKTARGET header (code, length, 'C','I','S') within the window. * Two-check form to be overflow-safe on both 32 and 64-bit. */ static int fix_rejects(unsigned long mult, unsigned long ptr) { if (ptr >= (unsigned long)PCCARD_CIS_SIZE) return 1; if (ptr + 4 >= (unsigned long)PCCARD_CIS_SIZE / mult) return 1; return 0; } struct case_t { const char *name; unsigned long mult; unsigned long ptr; /* card-supplied longlink_addr or mfc[].addr */ int expect_oob; /* 1 = unguarded path reads out of bounds */ }; int main(void) { /* Cases mirror the finding's threat model. The first is the PoC image: * CISTPL_LONGLINK_A with addr=0xFFFFFFFF, mult=2 (attribute memory). */ struct case_t cases[] = { { "PoC longlink_A 0xFFFFFFFF mult=2 (attr mem)", 2, 0xFFFFFFFFUL, 1 }, { "longlink_C 0xFFFFFFFF mult=1 (common mem)", 1, 0xFFFFFFFFUL, 1 }, { "mfc entry 0xDEADBEEF mult=2", 2, 0xDEADBEEFUL, 1 }, { "mfc entry 0x80000000 mult=1", 1, 0x80000000UL, 1 }, { "boundary ptr=2044 mult=2 (needs 5 bytes)", 2, 2044, 1 }, { "valid ptr=100 mult=2", 2, 100, 0 }, { "valid ptr=2043 mult=2 (last in-window)", 2, 2043, 0 }, { "valid ptr=4091 mult=1 (last in-window)", 1, 4091, 0 }, { "valid ptr=0 mult=2", 2, 0, 0 }, }; int n = sizeof(cases)/sizeof(cases[0]); int fails = 0; printf("DF-1040 CIS longlink/MFC target validation harness\n"); printf("PCCARD_CIS_SIZE = %u bytes (mapped window at pccard_cis.c:130-131)\n", PCCARD_CIS_SIZE); printf("pccard_cis_read_1 byte offset = mult * ptr (pccardvar.h:255-256)\n"); printf("chain-transition deref at pccard_cis.c:413 has NO bounds check\n"); printf("%-48s %7s %12s %8s %8s %8s\n", "case", "mult", "byte_off", "expect", "is_oob", "fix_rej"); printf("%-48s %7s %12s %8s %8s %8s\n", "----", "----", "--------", "------", "------", "-------"); for (int i = 0; i < n; i++) { uint64_t off = chain_transition_deref(cases[i].mult, cases[i].ptr); int is_oob = (off + cases[i].mult * 4 >= PCCARD_CIS_SIZE); int rej = fix_rejects(cases[i].mult, cases[i].ptr); printf("%-48s %7lu %12llu %8s %8s %8s", cases[i].name, cases[i].mult, (unsigned long long)off, cases[i].expect_oob ? "OOB" : "in", is_oob ? "OOB" : "in", rej ? "yes" : "no"); if (cases[i].expect_oob) { if (!is_oob) { printf(" <<< HARNESS BUG: expected OOB"); fails++; } if (!rej) { printf(" <<< FIX BUG: should reject"); fails++; } } else { if (is_oob) { printf(" <<< HARNESS BUG: unexpected OOB"); fails++; } if (rej) { printf(" <<< FIX BUG: false reject"); fails++; } } printf("\n"); } printf("\n"); printf("Proof of primitive (PoC case):\n"); { unsigned long mult = 2, ptr = 0xFFFFFFFFUL; uint64_t off = chain_transition_deref(mult, ptr); printf(" longlink_addr (card-supplied, 32-bit) = 0x%lX\n", ptr); printf(" tuple.mult (pccard_cis.c:393, attr mem) = %lu\n", mult); printf(" tuple.ptr (pccard_cis.c:394) = 0x%lX\n", ptr); printf(" bus_space_read_1 byte offset (line 413) = 0x%llX\n", (unsigned long long)off); printf(" mapped window size = %u\n", PCCARD_CIS_SIZE); printf(" offset exceeds window by = %llu bytes\n", (unsigned long long)(off - PCCARD_CIS_SIZE)); printf(" => on real PCMCIA hardware this is a wild bus_space_read_1\n"); printf(" past the mapped resource => kernel page-fault panic.\n"); printf(" fix.diff rejects this target: %s\n", fix_rejects(mult, ptr) ? "YES (continue, no deref)" : "NO (BUG)"); } printf("\n%s\n", fails ? "FAIL: harness/fix logic error" : "PASS: all cases agree โ bug confirmed at logic level, fix is correct"); return fails ? 1 : 0; } |