@samitouri / QOSamiQemu / commits / cc479aa897

hw/usb/hcd-xhci-sysbus: Fix OOB heap access in xhci_sysbus_intr_raise()

Some machines like the microvm machine instantiate a "sysbus-xhci" device with just 1 interrupt (by setting the "intrs" property to 1). xhci_sysbus_realize() then only allocates the s->irq array with one entry. When the guest writes to the ERDP register of a corresponding XHCI "interrupter", the generic XHCI code calls the xhci_sysbus_intr_raise() function with n > 1, and this function then calls qemu_set_irq() with s->irq[n] pointing to a bad heap address. The qemu_set_irq() then tries to call an IRQ handler via a function pointer in that heap space. This either causes QEMU to die with a segmentation fault (if it's a bad address), or even worse runs some unexpected code if the destination of the pointer is executable code. Looking at the xHCI spec, it is up to the implementation of the host controller how many interrupters are available. So if we only support one or some few interrupters, the registers of the other interrupters should not do anything, i.e. reads should result in zeros and writes should be completely ignored. (big thanks to Peter Maydell for helping with the analyzation of the correct way to fix this here) This way, the xhci_sysbus_intr_raise() function cannot be called with an invalid interrupt number anymore. But for good measure, also add an assert() statement to the xhci_sysbus_intr_raise() function to prevent that similar problems with calling arbitrary function pointers on the heap could occur again. Fixes: CVE-2026-16043 Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/4001 Reported-by: Tristan Madani <tristan@talencesecurity.com> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Thomas Huth <thuth@redhat.com> Message-ID: <20260719061528.15587-1-thuth@redhat.com>

Thomas Huth committed Jul 19, 2026 at 08:15 UTC cc479aa89743655a725ce29328bcd0585a65983a
2 files changed +14
hw/usb/hcd-xhci-sysbus.c
+1
@@ -20,6 +20,7 @@ static bool xhci_sysbus_intr_raise(XHCIState *xhci, int n, bool level)
20 {
21 XHCISysbusState *s = container_of(xhci, XHCISysbusState, xhci);
22
23 + assert(n < xhci->numintrs);
24 qemu_set_irq(s->irq[n], level);
25
26 return false;
hw/usb/hcd-xhci.c
+13
@@ -3044,6 +3044,12 @@ static uint64_t xhci_runtime_read(void *ptr, hwaddr reg,
3044 }
3045 } else {
3046 int v = (reg - 0x20) / 0x20;
3047 +
3048 + if (v >= xhci->numintrs) {
3049 + qemu_log_mask(LOG_GUEST_ERROR,
3050 + "xhci: read from nonexistent interrupter %i\n", v);
3051 + goto out_trace;
3052 + }
3053 XHCIInterrupter *intr = &xhci->intr[v];
3054 switch (reg & 0x1f) {
3055 case 0x00: /* IMAN */
@@ -3070,6 +3076,7 @@ static uint64_t xhci_runtime_read(void *ptr, hwaddr reg,
3076 }
3077 }
3078
3079 +out_trace:
3080 trace_usb_xhci_runtime_read(reg, ret);
3081 return ret;
3082 }
@@ -3087,7 +3094,13 @@ static void xhci_runtime_write(void *ptr, hwaddr reg,
3094 trace_usb_xhci_unimplemented("runtime write", reg);
3095 return;
3096 }
3097 +
3098 v = (reg - 0x20) / 0x20;
3099 + if (v >= xhci->numintrs) {
3100 + qemu_log_mask(LOG_GUEST_ERROR,
3101 + "xhci: write to nonexistent interrupter %i\n", v);
3102 + return;
3103 + }
3104 intr = &xhci->intr[v];
3105
3106 switch (reg & 0x1f) {