Re: [PATCH v3] usb: xhci: Fix use-after-free race in xhci_stop_device()

From: Michal Pecio

Date: Tue Nov 03 2026 - 18:06:18 EST


On Wed, 4 Nov 2026 00:01:00 +0800, YuanZhe Liu wrote:
> A KASAN slab-use-after-free read was observed in xhci_stop_device():
>
> BUG: KASAN: slab-use-after-free in xhci_stop_device.constprop.0.isra.0+0x395/0x430
> drivers/usb/host/xhci-hub.c:479
> Read of size 8 at addr ffff88804e85d2f0
>
> Freed by task 585700:
> kfree+0xf8/0x390 mm/slub.c:6566
> xhci_free_virt_device+0x3f/0x50 drivers/usb/host/xhci-mem.c:881
> xhci_free_dev+0x286/0x350 drivers/usb/host/xhci.c:4131
> usb_disconnect+0x6b6/0x910 drivers/usb/core/hub.c:2391
> ...
> usbdev_ioctl+0x6f05/0x86d0 drivers/usb/core/devio.c:2826
>
> Allocated by task 572283:
> xhci_alloc_virt_device+0x171/0xb70 drivers/usb/host/xhci-mem.c:986
> xhci_alloc_dev+0x475/0x840 drivers/usb/host/xhci.c:4271
> usb_alloc_dev+0xe8/0xdc0 drivers/usb/core/usb.c:661
> hub_event+0x1c82/0x4050 drivers/usb/core/hub.c:5953
>
> xhci_stop_device() reads xhci->devs[slot_id] without xhci->lock and
> keeps using the pointer across xhci_alloc_command(GFP_NOIO), which may
> sleep. All callers (the port suspend and bus suspend paths in
> xhci_hub_control() and xhci_bus_suspend()) drop xhci->lock before
> calling it, so a concurrent usb_disconnect() -> xhci_free_dev() can
> clear xhci->devs[slot_id] and free virt_dev under xhci->lock in that
> window. The endpoint loop then dereferences the freed virt_dev.

Hi,

Good job crashing a real driver and not just dummy-hcd as usual.

But is it not a bug in the first place that we are suspending a device
which is being deleted or deleting one which is being suspended?

Would you mind revealing your secret sauce and posting complete stack
traces of how you got there, including xhci_stop_device()? What kernel
version was that? Those line numbers don't match current code.

As you are .edu, perhaps you have a reproducer for this bug?

> commit 88716a93766b ("usb: xhci: Fix panic if disconnect") added a
> NULL check for this case, but that only helps when the device was
> already freed before the check; it does not close the window.

That's an old bug, not sure how such things can still be happening?
It seems that xhci_free_dev() doesn't return until everything is
cleared, and I would naively expect USB core not to suspend the bus
until all devices are suspended or finished disconnecting.

In testing, I see xhci_bus_suspend() 100 ms after disconnection, so
this race seems tricky to trigger. How have you done it?

Regards,
Michal

> Look up xhci->devs[slot_id] only after taking xhci->lock, and bail out
> if it is NULL or if the device is already being torn down. The
> tracepoint reloads virt_dev->udev, so it moves under the lock and only
> runs while the device is still attached.
>
> xhci_free_dev() clears virt_dev->udev without xhci->lock, and only
> later detaches the device from xhci->devs[] in xhci_free_virt_device(),
> also under the lock. Take xhci->lock when clearing ->udev so that
> xhci_stop_device() can rely on it while holding the same lock.
> xhci_free_virt_device() clears xhci->devs[slot_id] and frees the device
> under xhci->lock, so a device found there while holding the lock stays
> alive until xhci_stop_device() drops the lock, after which virt_dev is
> not used anymore.
>
> Fixes: be88fe4f4dda93e3 ("USB: xHCI: port power management implementation")
> Cc: stable@xxxxxxxxxxxxxxx
> Co-developed-by: Bin Yu <byu@xxxxxxxxxxxxx>
> Signed-off-by: Bin Yu <byu@xxxxxxxxxxxxx>
> Co-developed-by: Cheng Wen <wencheng@xxxxxxxxxxxxx>
> Signed-off-by: Cheng Wen <wencheng@xxxxxxxxxxxxx>
> Signed-off-by: YuanZhe Liu <25031212351@xxxxxxxxxxxxxxxxx>