DF-1228 / mpr_smid_oob.c
/* * DF-1228 โ mpr_intr_locked() untrusted-SMID OOB-index proof-of-concept * * Bug (sys/dev/raid/mpr/mpr.c): * Line 2336: cm = &sc->commands[le16toh(desc->SCSIIOSuccess.SMID)]; * Line 2407: cm = &sc->commands[le16toh(desc->AddressReply.SMID)]; * * SMID is a U16 in the reply descriptor written by the MPT-Fusion 3 HBA * (sys/dev/raid/mpr/mpi/mpi2.h:416/442/...). sc->commands[] is allocated * as `sizeof(struct mpr_command) * sc->num_reqs` (mpr.c:1522), where * num_reqs is an int set from the controller's IOC facts โ typically a * few thousand. A reply SMID > num_reqs therefore reads an out-of-bounds * mpr_command struct whose cm_state / cm_reply / cm_complete fields are * attacker-influenced heap residue. mpr_complete_command() then derefs * cm->cm_complete as a function pointer (sys/dev/raid/mpr/mpr.c, via * `cm->cm_complete(sc, cm);` in mpr_complete_command) โ i.e. the OOB * read becomes a hijackable control-flow transfer. * * KASSERT(cm->cm_state == MPR_CM_STATE_INQUEUE, ...) at lines 2337/2409 * only fires on INVARIANTS kernels AND only AFTER the OOB read has * happened, so it does not protect the index. * * Threat model: malicious/buggy PCIe HBA (firmware compromise, DMA attack, * buggy card, PCIe passthrough of a hostile device). The SMID is attacker * controlled in that model. * * THIS GUEST: no LSI/Avago MPT-Fusion controller in pciconf -l, so no * /dev/mprN exists; the interrupt handler is never entered. PoC prints * the reachability status. On a real host with an mpr HBA, a malicious * card replying with SMID > num_reqs triggers the OOB index. */ #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <errno.h> #include <sys/ioctl.h> int main(void) { int fd, i; const char *devs[] = {"/dev/mpr0", "/dev/mpr1", "/dev/mpr2", "/dev/mpr3"}; printf("[DF-1228] mpr SMID OOB-index demonstrator\n"); printf("[DF-1228] Bug: sc->commands[le16toh(SMID)] at mpr.c:2336 & :2407\n"); printf("[DF-1228] sc->commands[] sized num_reqs (few thousand); SMID is u16 (0..65535)\n"); printf("[DF-1228] KASSERT only fires AFTER OOB read; no real bounds check exists\n\n"); for (i = 0; i < 4; i++) { fd = open(devs[i], O_RDWR); if (fd >= 0) { printf("[DF-1228] Opened %s -- controller present.\n", devs[i]); printf("[DF-1228] A malicious HBA replying with SMID >= num_reqs\n"); printf("[DF-1228] OOB-indexes sc->commands[] -> OOB struct mpr_command\n"); printf("[DF-1228] -> mpr_complete_command() calls cm->cm_complete(sc,cm)\n"); printf("[DF-1228] from OOB heap -> control-flow hijack on INVARIANTS-OFF kernels.\n"); close(fd); return (0); } } printf("[DF-1228] No /dev/mprN found: %s\n", strerror(errno)); printf("[DF-1228] No LSI MPT-Fusion HBA on guest -> interrupt handler never runs.\n"); printf("[DF-1228] Source-level verification only (see VERDICT.md).\n"); return (0); } |