diff --git a/sys/bus/u4b/controller/ehci.c b/sys/bus/u4b/controller/ehci.c --- a/sys/bus/u4b/controller/ehci.c +++ b/sys/bus/u4b/controller/ehci.c @@ -327,6 +327,28 @@ sc->sc_noport = EHCI_HCS_N_PORTS(sparams); sc->sc_bus.usbrev = USB_REV_2_0; + /* + * Validate the device-reported register geometry before it is + * latched into every subsequent EOREAD4/EOWRITE4 (ehci.h adds + * sc_offs to each operational-register offset). The furthest + * register the driver touches is EHCI_PORTSC(sc_noport) = + * 0x44 + 4*sc_noport, so the whole window + * [sc_offs, sc_offs + 0x44 + 4*sc_noport] must fit inside the + * mapped BAR (sc_io_size, set by the bus glue). CAPLENGTH is 8 + * bits and N_PORTS is 4 bits, so a malicious or broken controller + * can push accesses up to 0x17F bytes past the BAR base; with an + * undersized BAR that is out-of-bounds MMIO read/write (same + * defect class as the xHCI CAPLENGTH/DBOFF issue). + */ + if ((sc->sc_offs < 0x08) || + ((uint32_t)sc->sc_offs + 0x44 + (4 * sc->sc_noport)) > + sc->sc_io_size) { + device_printf(sc->sc_bus.bdev, "invalid register geometry: " + "offs=0x%02x noport=%u io_size=0x%jx\n", + sc->sc_offs, sc->sc_noport, (uintmax_t)sc->sc_io_size); + return (USB_ERR_IOERROR); + } + if (!(sc->sc_flags & EHCI_SCFLG_DONTRESET)) { /* Reset the controller */ DPRINTF("%s: resetting\n",