Virtuozzo QEMU development (svt-core@virtuozzo.com)
 help / color / mirror / Atom feed
From: "Denis V. Lunev" <den@openvz.org>
To: andrey.drobyshev@virtuozzo.com
Cc: svt-core@virtuozzo.com, den@openvz.org
Subject: [PATCH hci-8.1 3/3] hw/usb/hcd-xhci: More reentrancy guard fixes for the XHCI code #VSTOR-151666
Date: Mon, 26 Oct 2026 12:46:45 +0100	[thread overview]
Message-ID: <20261026114645.187003-4-den@openvz.org> (raw)
In-Reply-To: <20261026114645.187003-1-den@openvz.org>

From: Thomas Huth <thuth@redhat.com>

The XHCI controller's USB port and bus callbacks -- xhci_complete(),
xhci_wakeup_endpoint(), xhci_attach(), xhci_detach(), and
xhci_wakeup() -- can be invoked from outside any MMIO handler context.
The device's mem_reentrancy_guard is therefore not engaged during
this processing.

A malicious guest can exploit this gap via MSI-X self-targeting: it
programs the MSI-X vector's message address to point at the XHCI
doorbell register array (both reside in the same BAR0 MemoryRegion).
When any of these callbacks posts an event via xhci_event() ->
xhci_intr_raise(), the resulting msix_notify() -> pci_msi_trigger()
issues an address_space_stl_le() that writes doorbell 0, triggering
xhci_process_commands() reentrantly. A CR_DISABLE_SLOT command
planted on the command ring then frees XHCITransfer/XHCIEPContext
objects that the outer call stack still references, causing a
heap use-after-free.

Fix this by setting engaged_in_io on the device's mem_reentrancy_guard
around the processing in all five callbacks. The guard is set
conditionally (only when not already engaged) because these callbacks
may also be reached from within a guarded MMIO context.

xhci_child_detach() is intentionally left unguarded: it only calls
xhci_detach_slot() which passes report=0 to xhci_ep_nuke_xfers(),
suppressing event generation, so it never reaches xhci_event().

Note: This is similar to the fixes done for CVE-2026-17588, see
f91b0544fe ("Set reentrancy guard in timer functions").

CVE: CVE-2026-111819
Message-ID: <20261020114031.687929-1-thuth@redhat.com>
Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/4625
Reported-by: Ken Hsu, Royce Lu, and Bo Qu of Palo Alto Networks
Buglink: https://gitlab.com/qemu-project/qemu/-/work_items/4642
Reported-by: Nikhil Agrawal <nikhil.agrawal@mettlelabs.ai>
Signed-off-by: Thomas Huth <thuth@redhat.com>
(cherry picked from commit cfa70f3a82f581ad758586ecf408fe7f3602850f)
---
 hw/usb/hcd-xhci.c | 27 +++++++++++++++++++++++++++
 1 file changed, 27 insertions(+)

diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c
index 80d2687fb19..8017ddcebd0 100644
--- a/hw/usb/hcd-xhci.c
+++ b/hw/usb/hcd-xhci.c
@@ -3243,49 +3243,73 @@ static const MemoryRegionOps xhci_doorbell_ops = {
     .endianness = DEVICE_LITTLE_ENDIAN,
 };
 
+static bool xhci_engage_reentrancy_guard(XHCIState *xhci)
+{
+    bool prev_engaged = xhci->parent.mem_reentrancy_guard.engaged_in_io;
+
+    xhci->parent.mem_reentrancy_guard.engaged_in_io = true;
+    return prev_engaged;
+}
+
+static void xhci_restore_reentrancy_guard(XHCIState *xhci, bool engaged)
+{
+    xhci->parent.mem_reentrancy_guard.engaged_in_io = engaged;
+}
+
 static void xhci_attach(USBPort *usbport)
 {
     XHCIState *xhci = usbport->opaque;
     XHCIPort *port = xhci_lookup_port(xhci, usbport);
+    bool prev_engaged = xhci_engage_reentrancy_guard(xhci);
 
     xhci_port_update(port, 0);
+    xhci_restore_reentrancy_guard(xhci, prev_engaged);
 }
 
 static void xhci_detach(USBPort *usbport)
 {
     XHCIState *xhci = usbport->opaque;
     XHCIPort *port = xhci_lookup_port(xhci, usbport);
+    bool prev_engaged = xhci_engage_reentrancy_guard(xhci);
 
     xhci_detach_slot(xhci, usbport);
     xhci_port_update(port, 1);
+    xhci_restore_reentrancy_guard(xhci, prev_engaged);
 }
 
 static void xhci_wakeup(USBPort *usbport)
 {
     XHCIState *xhci = usbport->opaque;
     XHCIPort *port = xhci_lookup_port(xhci, usbport);
+    bool prev_engaged;
 
     assert(port);
     if (get_field(port->portsc, PORTSC_PLS) != PLS_U3) {
         return;
     }
+    prev_engaged = xhci_engage_reentrancy_guard(xhci);
     set_field(&port->portsc, PLS_RESUME, PORTSC_PLS);
     xhci_port_notify(port, PORTSC_PLC);
+    xhci_restore_reentrancy_guard(xhci, prev_engaged);
 }
 
 static void xhci_complete(USBPort *port, USBPacket *packet)
 {
     XHCITransfer *xfer = container_of(packet, XHCITransfer, packet);
+    XHCIState *xhci = xfer->epctx->xhci;
+    bool prev_engaged;
 
     if (packet->status == USB_RET_REMOVE_FROM_QUEUE) {
         xhci_ep_nuke_one_xfer(xfer, 0);
         return;
     }
+    prev_engaged = xhci_engage_reentrancy_guard(xhci);
     xhci_try_complete_packet(xfer);
     xhci_kick_epctx(xfer->epctx, xfer->streamid);
     if (xfer->complete) {
         xhci_ep_free_xfer(xfer);
     }
+    xhci_restore_reentrancy_guard(xhci, prev_engaged);
 }
 
 static void xhci_child_detach(USBPort *uport, USBDevice *child)
@@ -3337,6 +3361,7 @@ static void xhci_wakeup_endpoint(USBBus *bus, USBEndpoint *ep,
 {
     XHCIState *xhci = container_of(bus, XHCIState, bus);
     int slotid;
+    bool prev_engaged;
 
     DPRINTF("%s\n", __func__);
     slotid = ep->dev->addr;
@@ -3345,7 +3370,9 @@ static void xhci_wakeup_endpoint(USBBus *bus, USBEndpoint *ep,
         DPRINTF("%s: oops, no slot for dev %d\n", __func__, ep->dev->addr);
         return;
     }
+    prev_engaged = xhci_engage_reentrancy_guard(xhci);
     xhci_kick_ep(xhci, slotid, xhci_find_epid(ep), stream);
+    xhci_restore_reentrancy_guard(xhci, prev_engaged);
 }
 
 static USBBusOps xhci_bus_ops = {
-- 
2.53.0


  parent reply	other threads:[~2026-10-26 11:47 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-26 11:46 [PATCH hci-8.1 0/3] hw/usb/hcd-xhci: fix guest-triggerable use-after-free (CVE-2026-17588, CVE-2026-111819) #VSTOR-151666 Denis V. Lunev
2026-10-26 11:46 ` [PATCH hci-8.1 1/3] hw/usb/hcd-xhci: Set reentrancy guard in timer functions (CVE-2026-17588) #VSTOR-151666 Denis V. Lunev
2026-10-26 11:51   ` no-reply
2026-10-26 11:46 ` [PATCH hci-8.1 2/3] hw/usb/hcd-xhci: Limit DMA transfers to plain memory #VSTOR-151666 Denis V. Lunev
2026-10-26 11:50   ` no-reply
2026-10-26 11:46 ` Denis V. Lunev [this message]
2026-10-26 11:53   ` [PATCH hci-8.1 3/3] hw/usb/hcd-xhci: More reentrancy guard fixes for the XHCI code #VSTOR-151666 no-reply
2026-10-26 16:13 ` [PATCH hci-8.1 0/3] hw/usb/hcd-xhci: fix guest-triggerable use-after-free (CVE-2026-17588, CVE-2026-111819) #VSTOR-151666 Andrey Drobyshev

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20261026114645.187003-4-den@openvz.org \
    --to=den@openvz.org \
    --cc=andrey.drobyshev@virtuozzo.com \
    --cc=svt-core@virtuozzo.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox