DF-1192 / 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 | /* * DF-1192 harness — ciss_filter_physical negative bus index * (userspace replica of sys/dev/raid/ciss/ciss.c:1538-1554) * * For each physical LUN the controller returns a 32-bit "extra_address". The * driver computes: * bus = CISS_EXTRA_BUS2(ea) - 1; // 0..63, then -1 * target = CISS_EXTRA_TARGET2(ea); // 0..255 * sc->ciss_physical[bus][target].cp_address = ...; * The preceding filter (ciss.c:1539-1541) rejects only BUS3/TARGET3/MODE2==3, * NOT BUS2==0. When BUS2==0 -> bus=-1 -> ciss_physical[-1][target] reads a * kernel pointer from BEFORE the array and writes cp_address/cp_online through * it. This harness reproduces the negative index with the real macros. * * Build: cc -O2 -Wall -o harness harness.c * Run: ./harness */ #include <stdio.h> #include <stdlib.h> #include <stdint.h> /* ---- real kernel macros (sys/dev/raid/ciss/cissreg.h:62-67) ---- */ #define CISS_EXTRA_MODE2(extra) ((extra & 0xc0000000) >> 30) #define CISS_EXTRA_BUS2(extra) ((extra & 0x3f000000) >> 24) /* 0..63 */ #define CISS_EXTRA_TARGET2(extra) ((extra & 0x00ff0000) >> 16) /* 0..255 */ #define CISS_EXTRA_BUS3(extra) ((extra & 0x00003f00) >> 8) #define CISS_EXTRA_TARGET3(extra) ((extra & 0x000000ff)) #define CISS_MAX_PHYSTGT 256 /* cissvar.h:171 */ struct ciss_pdrive { uint64_t cp_address; int cp_online; }; struct sc_replica { int ciss_max_physical_bus; /* rows of ciss_physical[][] */ struct ciss_pdrive **ciss_physical; }; /* exact replica of the filter+index (ciss.c:1538-1554). * Returns: 0 = in-bounds, 1 = OOB (would-be write), 2 = filtered out (continue). * Sets *bus_out to the computed bus index. */ static int filter_and_index(struct sc_replica *sc, uint32_t ea, int *bus_out) { int bus, target; /* ciss.c:1539-1541 filter */ if ((CISS_EXTRA_BUS3(ea) != 0) || (CISS_EXTRA_TARGET3(ea) != 0) || (CISS_EXTRA_MODE2(ea) == 0x3)) { *bus_out = 0; return 2; /* filtered out (continue) */ } bus = CISS_EXTRA_BUS2(ea) - 1; /* ciss.c:1551 */ target = CISS_EXTRA_TARGET2(ea); /* ciss.c:1552 */ *bus_out = bus; /* KERNEL HAS NO bus>=0 / bus<max check here: * sc->ciss_physical[bus][target].cp_address = ...; * sc->ciss_physical[bus][target].cp_online = 1; */ printf("BUS2=%2u target=%2u -> index bus=%d (valid 0..%d) target=%d\n", CISS_EXTRA_BUS2(ea), CISS_EXTRA_TARGET2(ea), bus, sc->ciss_max_physical_bus - 1, target); if (bus < 0 || bus >= sc->ciss_max_physical_bus) return 1; /* would be OOB */ return 0; /* in-bounds */ } int main(void) { struct sc_replica sc; int max_physical_bus = 4; sc.ciss_max_physical_bus = max_physical_bus; sc.ciss_physical = calloc(max_physical_bus, sizeof(struct ciss_pdrive *)); printf("== DF-1192 ciss_filter_physical negative-bus harness ==\n"); printf("ciss_max_physical_bus=%d\n\n", max_physical_bus); /* Malicious extra_address: BUS2==0 (the unfiltered case), valid MODE2!=3, * BUS3==0, TARGET3==0, target=5. */ uint32_t ea_mal = (uint32_t)0 << 24 | 5u << 16; /* BUS2=0,target=5 */ int bus_mal; printf("[malicious] "); int res_mal = filter_and_index(&sc, ea_mal, &bus_mal); /* A benign in-range one for contrast */ uint32_t ea_ok = (uint32_t)2 << 24 | 5u << 16; /* BUS2=2 -> bus=1 */ int bus_ok; printf("[benign ] "); int res_ok = filter_and_index(&sc, ea_ok, &bus_ok); printf("\n"); if (res_mal == 1) { printf("[BUG REPRODUCED] malicious LUN PASSES the filter and indexes " "ciss_physical[%d][5]\n", bus_mal); printf("On a real kernel: ciss_physical[-1] reads a kernel pointer from before\n"); printf("the array; the subsequent cp_address/cp_online writes corrupt whatever\n"); printf("that pointer references (controlled OOB write).\n"); free(sc.ciss_physical); return 0; } else if (res_mal == 2) { printf("[not-reproduced] malicious LUN was filtered out\n"); } else { printf("[not-reproduced] malicious LUN indexed in-bounds (bus=%d)\n", bus_mal); } free(sc.ciss_physical); return 1; } |