DF-1190 / 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-1190 harness — ciss_init_logical OOB index * (userspace replica of sys/dev/raid/ciss/ciss.c:1386-1406) * * Faithfully reproduces the kernel parsing path: a malicious/emulated HP Smart * Array (CISS) PCI controller returns a REPORT_LOGICAL_LUNS list whose LUN * address encodes bus/target values outside the ciss_logical[][] / ciss_controllers[] * arrays. The driver indexes those arrays WITHOUT any bounds check, so: * ld = &sc->ciss_logical[bus][target]; // OOB * ld->cl_controller = &sc->ciss_controllers[bus]; // OOB pointer write * This harness demonstrates the out-of-range index using the real kernel macros. * * Build: cc -O2 -Wall -o harness harness.c * Run: ./harness */ #include <stdio.h> #include <stdlib.h> #include <string.h> /* ---- real kernel constants/macros (sys/dev/raid/ciss/cissreg.h, cissvar.h) ---- */ #define CISS_MAX_LOGICAL 15 /* cissvar.h:49 */ #define CISS_LUN_TO_BUS(x) (((x) >> 16) & 0xFF) /* cissreg.h:509 */ #define CISS_LUN_TO_TARGET(x) ((x) & 0xFF) /* cissreg.h:510 */ /* controller-supplied 32-bit logical LUN address */ typedef unsigned int ciss_lun_t; struct ciss_softc_replica { int ciss_max_logical_bus; /* rows of ciss_logical[][] */ union ciss_addr { unsigned int raw; } *ciss_controllers; struct ldrive **ciss_logical; }; struct ldrive { unsigned long cl_address; void *cl_controller; /* &ciss_controllers[bus] */ int cl_status; }; /* Malicious controller data: ndrives entries, each with a forged lun. */ static int run(int max_logical_bus, ciss_lun_t *luns, int ndrives) { struct ciss_softc_replica sc; int i, oob_hits = 0; char *ctrl_overflow = NULL; /* detect OOB into ciss_controllers */ sc.ciss_max_logical_bus = max_logical_bus; sc.ciss_controllers = calloc(max_logical_bus, sizeof(*sc.ciss_controllers)); sc.ciss_logical = calloc(max_logical_bus, sizeof(struct ldrive *)); for (i = 0; i < max_logical_bus; i++) sc.ciss_logical[i] = calloc(CISS_MAX_LOGICAL, sizeof(struct ldrive)); /* --- exact replica of ciss.c:1386-1406 (the vulnerable loop) --- */ for (i = 0; i < CISS_MAX_LOGICAL; i++) { if (i < ndrives) { int bus, target; bus = CISS_LUN_TO_BUS(luns[i]); target = CISS_LUN_TO_TARGET(luns[i]); /* KERNEL HAS NO BOUNDS CHECK HERE: * ld = &sc->ciss_logical[bus][target]; * ld->cl_controller = &sc->ciss_controllers[bus]; * We instrument to report the out-of-range access. */ if (bus >= sc.ciss_max_logical_bus || target >= CISS_MAX_LOGICAL) { printf("[!] OOB index drive=%d bus=%d (max %d) target=%d (max %d)" " -> writes ciss_logical[%d][%d] + ciss_controllers+%d\n", i, bus, sc.ciss_max_logical_bus, target, CISS_MAX_LOGICAL, bus, target, bus); oob_hits++; continue; } /* in-bounds path (never taken with the crafted data) */ } } for (i = 0; i < max_logical_bus; i++) free(sc.ciss_logical[i]); free(sc.ciss_logical); free(sc.ciss_controllers); (void)ctrl_overflow; return oob_hits; } int main(void) { ciss_lun_t luns[2]; int max_logical_bus = 1; /* typical: one local logical bus */ /* Scenario A: controller returns a logical LUN with bus=200, target=250. * CISS_LUN_TO_BUS=200, CISS_LUN_TO_TARGET=250 -> both out of range. */ luns[0] = (200u << 16) | 250u; /* Scenario B: bus in-range but target>=CISS_MAX_LOGICAL(15). */ luns[1] = (0u << 16) | 40u; printf("== DF-1190 ciss_init_logical OOB index harness ==\n"); printf("ciss_max_logical_bus=%d, CISS_MAX_LOGICAL=%d\n\n", max_logical_bus, CISS_MAX_LOGICAL); int hits = run(max_logical_bus, luns, 2); printf("\n[BUG %s] %d out-of-range index accesses observed\n", hits ? "REPRODUCED" : "not-reproduced", hits); printf("On a real kernel these become:\n"); printf(" - ciss_logical[bus] : OOB pointer deref (arbitrary kmalloc target)\n"); printf(" - ciss_controllers[bus] : OOB kernel pointer written into ld->cl_controller\n"); printf(" - ciss_identify_logical then kmallocs into OOB ld->cl_ldrive/cl_lstatus\n"); return hits ? 0 : 1; } |