mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH] xhci: sideband: check vdev liveness before removing endpoints on unregister
@ 2026-09-07 12:24 胡连勤
  2026-09-10  9:34 ` Mathias Nyman
  0 siblings, 1 reply; 10+ messages in thread
From: 胡连勤 @ 2026-09-07 12:24 UTC (permalink / raw)
  To: Mathias Nyman, Greg Kroah-Hartman, quic_wcheng, broonie
  Cc: linux-usb, linux-kernel, 胡连勤

xhci_sideband_unregister() assumes the virtual device (vdev) is still
alive when iterating sideband endpoints and issuing stop endpoint
commands. However, xhci_disable_and_free_slot() may have already freed
vdev and its out_ctx before xhci_sideband_unregister() is invoked.

This happens when xhci_setup_device() gets COMP_USB_TRANSACTION_ERROR
(e.g. device not responding to setup address during bus reset recovery),
causing vdev to be freed before xhci_sideband_unregister() is called:

  hub_event()
    xhci_setup_device()                  <-- COMP_USB_TRANSACTION_ERROR
    xhci_disable_and_free_slot()
      xhci_free_virt_device()
        kfree(out_ctx), kfree(vdev)
        xhci->devs[slot_id] = NULL
    ...
    usb_disconnect()
      uaudio_disconnect()
        xhci_sideband_unregister()
          xhci_stop_endpoint_sync()
            xhci_get_ep_ctx()            <-- CRASH (deref freed out_ctx)

Unable to handle kernel paging request at virtual address dead000000000122
Call trace:
 xhci_get_ep_ctx+0x0/0x38
 xhci_sideband_unregister+0x68/0xf0
 uaudio_disconnect+0x70/0x144
 usb_audio_disconnect+0x7c/0x268
 usb_unbind_interface+0x13c/0x340
 device_release_driver_internal+0x1c4/0x2bc
 device_release_driver+0x18/0x28
 bus_remove_device+0x158/0x170
 device_del+0x1c8/0x320
 usb_disable_device+0x84/0x190
 usb_disconnect+0xe8/0x338
 hub_event+0xbd8/0x19ac
 process_scheduled_works+0x200/0x9d8
 worker_thread+0x154/0x3b0
 kthread+0x11c/0x1a0

Fix this by caching the slot_id in the sideband structure at
registration time, then checking under xhci->lock whether
xhci->devs[slot_id] still matches sb->vdev before issuing stop
endpoint commands. If vdev has been freed, skip endpoint cleanup
entirely - the xHCI has already disabled the slot.
The interrupter is still removed as it does not depend on vdev.

The slot_id is cached in sb->slot_id rather than read from vdev at
unregister time because vdev may already be freed, making
sb->vdev->slot_id a dangling dereference.

Fixes: de66754e9f80 ("xhci: sideband: add initial api to register a secondary interrupter entity")
Cc: stable@vger.kernel.org
Signed-off-by: Lianqin Hu <hulianqin@vivo.com>
---
 drivers/usb/host/xhci-sideband.c  | 35 ++++++++++++++++++++++++++-----
 include/linux/usb/xhci-sideband.h |  2 ++
 2 files changed, 32 insertions(+), 5 deletions(-)

diff --git a/drivers/usb/host/xhci-sideband.c b/drivers/usb/host/xhci-sideband.c
index a5deeee4d5dc..1a83f03c2cf6 100644
--- a/drivers/usb/host/xhci-sideband.c
+++ b/drivers/usb/host/xhci-sideband.c
@@ -437,6 +437,7 @@ xhci_sideband_register(struct usb_interface *intf, enum xhci_sideband_type type,
 
 	sb->xhci = xhci;
 	sb->vdev = vdev;
+	sb->slot_id = udev->slot_id;
 	sb->intf = intf;
 	sb->type = type;
 	sb->notify_client = notify_client;
@@ -464,6 +465,7 @@ xhci_sideband_unregister(struct xhci_sideband *sb)
 	struct xhci_virt_device *vdev;
 	struct xhci_hcd *xhci;
 	int i;
+	bool vdev_alive = false;
 
 	if (!sb)
 		return;
@@ -473,11 +475,32 @@ xhci_sideband_unregister(struct xhci_sideband *sb)
 	scoped_guard(mutex, &sb->mutex) {
 		vdev = sb->vdev;
 		if (!vdev)
-			return;
+			goto out;
+
+		/*
+		 * Check if vdev is still the active device for its slot.
+		 * xhci_disable_and_free_slot() may have already freed vdev
+		 * and cleared xhci->devs[slot_id] (e.g. on
+		 * COMP_USB_TRANSACTION_ERROR during bus reset recovery in
+		 * xhci_setup_device). In that case sb->vdev->out_ctx is
+		 * dangling and issuing stop endpoint commands would crash
+		 * with a paging request at LIST_POISON1 + offset. If vdev
+		 * is gone, just clear the sideband pointers without
+		 * touching xHCI.
+		 */
+		spin_lock_irq(&xhci->lock);
+		vdev_alive = (xhci->devs[sb->slot_id] == vdev);
+		spin_unlock_irq(&xhci->lock);
 
-		for (i = 0; i < EP_CTX_PER_DEV; i++)
-			if (sb->eps[i])
-				__xhci_sideband_remove_endpoint(sb, sb->eps[i]);
+		if (vdev_alive) {
+			for (i = 0; i < EP_CTX_PER_DEV; i++)
+				if (sb->eps[i])
+					__xhci_sideband_remove_endpoint(sb, sb->eps[i]);
+		} else {
+			xhci_warn(xhci, "sideband unreg: vdev slot %d already freed, skipping ep cleanup\n",
+				  sb->slot_id);
+			for (i = 0; i < EP_CTX_PER_DEV; i++)
+				sb->eps[i] = NULL;
+		}
 
 		__xhci_sideband_remove_interrupter(sb);
 
@@ -486,9 +509,11 @@ xhci_sideband_unregister(struct xhci_sideband *sb)
 
 	spin_lock_irq(&xhci->lock);
 	sb->xhci = NULL;
-	vdev->sideband = NULL;
+	if (vdev_alive)
+		vdev->sideband = NULL;
 	spin_unlock_irq(&xhci->lock);
 
+out:
 	kfree(sb);
 }
 EXPORT_SYMBOL_GPL(xhci_sideband_unregister);
diff --git a/include/linux/usb/xhci-sideband.h b/include/linux/usb/xhci-sideband.h
index 005257085dcb..9d86b1daee27 100644
--- a/include/linux/usb/xhci-sideband.h
+++ b/include/linux/usb/xhci-sideband.h
@@ -40,6 +40,7 @@ struct xhci_sideband_event {
  * struct xhci_sideband - representation of a sideband accessed usb device.
  * @xhci: The xhci host controller the usb device is connected to
  * @vdev: the usb device accessed via sideband
+ * @slot_id: cached slot ID for vdev validity checking
  * @eps: array of endpoints controlled via sideband
  * @ir: event handling and buffer for sideband accessed device
  * @type: xHCI sideband type
@@ -52,6 +53,7 @@ struct xhci_sideband_event {
 struct xhci_sideband {
 	struct xhci_hcd                 *xhci;
 	struct xhci_virt_device         *vdev;
+	int                             slot_id;
 	struct xhci_virt_ep             *eps[EP_CTX_PER_DEV];
 	struct xhci_interrupter         *ir;
 	enum xhci_sideband_type		type;
-- 
2.48.1


^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2026-09-11 14:49 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-07 12:24 [PATCH] xhci: sideband: check vdev liveness before removing endpoints on unregister 胡连勤
2026-09-10  9:34 ` Mathias Nyman
2026-09-10 11:10   ` Selvarasu Ganesan
2026-09-10 12:11     ` 答复: " 胡连勤
2026-09-10 13:50       ` Mathias Nyman
2026-09-11  5:10       ` Selvarasu Ganesan
2026-09-11  7:29         ` 答复: " 胡连勤
2026-09-11  8:41           ` Selvarasu Ganesan
2026-09-11 13:10             ` Mathias Nyman
2026-09-11 14:49               ` 答复: " 胡连勤

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®