--- a/sys/bus/u4b/controller/xhci.c +++ b/sys/bus/u4b/controller/xhci.c @@ -628,6 +628,31 @@ sc->sc_runt_off = XREAD4(sc, capa, XHCI_RTSOFF) & ~0x1F; sc->sc_door_off = XREAD4(sc, capa, XHCI_DBOFF) & ~0x3; + /* + * DF-5268: The register offsets above are latched verbatim from + * device-controlled MMIO. If a malicious (or firmware-compromised) + * xHCI controller returns oversized CAPLENGTH/RTSOFF/DBOFF values, + * every subsequent XREAD/XWRITE computes bus-space addresses far + * outside the mapped BAR (out-of-bounds MMIO read/write: kernel + * page fault, or cross-device register corruption). Validate them + * against the size of the mapped register space before use. + * + * Bounds: the operational region needs the port status array + * (0x400 + 0x10*nports), the runtime region at least one + * interrupter block, and the doorbell array 4 bytes per slot + * (max 256 slots). + */ + if (sc->sc_oper_off < 0x20 || + (uint64_t)sc->sc_oper_off + 0x400 > sc->sc_io_size || + (uint64_t)sc->sc_runt_off + 0x40 > sc->sc_io_size || + (uint64_t)sc->sc_door_off + 0x400 > sc->sc_io_size) { + device_printf(self, "Invalid register offsets " + "(oper=0x%x runt=0x%x door=0x%x io_size=0x%llx)\n", + sc->sc_oper_off, sc->sc_runt_off, sc->sc_door_off, + (unsigned long long)sc->sc_io_size); + return (ENXIO); + } + DPRINTF("CAPLENGTH=0x%x\n", sc->sc_oper_off); DPRINTF("RUNTIMEOFFSET=0x%x\n", sc->sc_runt_off); DPRINTF("DOOROFFSET=0x%x\n", sc->sc_door_off); @@ -1232,6 +1257,11 @@ sc->sc_command_idx = i; sc->sc_command_ccs = j; + if ((uint64_t)sc->sc_door_off + 4 > sc->sc_io_size) { + /* doorbell outside the mapped register space */ + USB_BUS_UNLOCK(&sc->sc_bus); + return (USB_ERR_IOERROR); + } XWRITE4(sc, door, XHCI_DOORBELL(0), 0); err = cv_timedwait(&sc->sc_cmd_cv, &sc->sc_bus.bus_lock, @@ -2866,6 +2896,11 @@ index = xfer->xroot->udev->controller_slot_id; if (xfer->xroot->udev->flags.self_suspended == 0) { + if ((uint64_t)sc->sc_door_off + 4 * ((uint32_t)index + 1) > + sc->sc_io_size) { + /* doorbell outside the mapped register space */ + return; + } XWRITE4(sc, door, XHCI_DOORBELL(index), epno | XHCI_DB_SID_SET(xfer->stream_id)); }