From: Greg Kroah-Hartman Date: Mon, 25 Aug 2025 07:39:33 +0000 (+0200) Subject: 5.4-stable patches X-Git-Tag: v5.4.297~13 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=39a83894cec6b875f6ce12881953654cd649f006;p=thirdparty%2Fkernel%2Fstable-queue.git 5.4-stable patches added patches: usb-xhci-fix-slot_id-resource-race-conflict.patch --- diff --git a/queue-5.4/series b/queue-5.4/series index 9be5c19f29..1d20b74249 100644 --- a/queue-5.4/series +++ b/queue-5.4/series @@ -391,3 +391,4 @@ bluetooth-fix-use-after-free-in-device_for_each_child.patch cifs-fix-uaf-in-cifs_demultiplex_thread.patch nfs-fix-up-commit-deadlocks.patch nfs-fix-uaf-in-direct-writes.patch +usb-xhci-fix-slot_id-resource-race-conflict.patch diff --git a/queue-5.4/usb-xhci-fix-slot_id-resource-race-conflict.patch b/queue-5.4/usb-xhci-fix-slot_id-resource-race-conflict.patch new file mode 100644 index 0000000000..d871c9bb24 --- /dev/null +++ b/queue-5.4/usb-xhci-fix-slot_id-resource-race-conflict.patch @@ -0,0 +1,212 @@ +From 2eb03376151bb8585caa23ed2673583107bb5193 Mon Sep 17 00:00:00 2001 +From: Weitao Wang +Date: Tue, 19 Aug 2025 15:58:43 +0300 +Subject: usb: xhci: Fix slot_id resource race conflict + +From: Weitao Wang + +commit 2eb03376151bb8585caa23ed2673583107bb5193 upstream. + +xHC controller may immediately reuse a slot_id after it's disabled, +giving it to a new enumerating device before the xhci driver freed +all resources related to the disabled device. + +In such a scenario, device-A with slot_id equal to 1 is disconnecting +while device-B is enumerating, device-B will fail to enumerate in the +follow sequence. + +1.[device-A] send disable slot command +2.[device-B] send enable slot command +3.[device-A] disable slot command completed and wakeup waiting thread +4.[device-B] enable slot command completed with slot_id equal to 1 and + wakeup waiting thread +5.[device-B] driver checks that slot_id is still in use (by device-A) in + xhci_alloc_virt_device, and fail to enumerate due to this + conflict +6.[device-A] xhci->devs[slot_id] set to NULL in xhci_free_virt_device + +To fix driver's slot_id resources conflict, clear xhci->devs[slot_id] and +xhci->dcbba->dev_context_ptrs[slot_id] pointers in the interrupt context +when disable slot command completes successfully. Simultaneously, adjust +function xhci_free_virt_device to accurately handle device release. + +[minor smatch warning and commit message fix -Mathias] + +Cc: stable@vger.kernel.org +Fixes: 7faac1953ed1 ("xhci: avoid race between disable slot command and host runtime suspend") +Signed-off-by: Weitao Wang +Signed-off-by: Mathias Nyman +Link: https://lore.kernel.org/r/20250819125844.2042452-2-mathias.nyman@linux.intel.com +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/usb/host/xhci-hub.c | 3 +-- + drivers/usb/host/xhci-mem.c | 22 +++++++++++----------- + drivers/usb/host/xhci-ring.c | 9 +++++++-- + drivers/usb/host/xhci.c | 18 +++++++++++++----- + drivers/usb/host/xhci.h | 3 ++- + 5 files changed, 34 insertions(+), 21 deletions(-) + +--- a/drivers/usb/host/xhci-hub.c ++++ b/drivers/usb/host/xhci-hub.c +@@ -628,8 +628,7 @@ static int xhci_enter_test_mode(struct x + if (!xhci->devs[i]) + continue; + +- retval = xhci_disable_slot(xhci, i); +- xhci_free_virt_device(xhci, i); ++ retval = xhci_disable_and_free_slot(xhci, i); + if (retval) + xhci_err(xhci, "Failed to disable slot %d, %d. Enter test mode anyway\n", + i, retval); +--- a/drivers/usb/host/xhci-mem.c ++++ b/drivers/usb/host/xhci-mem.c +@@ -879,21 +879,20 @@ free_tts: + * will be manipulated by the configure endpoint, allocate device, or update + * hub functions while this function is removing the TT entries from the list. + */ +-void xhci_free_virt_device(struct xhci_hcd *xhci, int slot_id) ++void xhci_free_virt_device(struct xhci_hcd *xhci, struct xhci_virt_device *dev, ++ int slot_id) + { +- struct xhci_virt_device *dev; + int i; + int old_active_eps = 0; + + /* Slot ID 0 is reserved */ +- if (slot_id == 0 || !xhci->devs[slot_id]) ++ if (slot_id == 0 || !dev) + return; + +- dev = xhci->devs[slot_id]; +- +- xhci->dcbaa->dev_context_ptrs[slot_id] = 0; +- if (!dev) +- return; ++ /* If device ctx array still points to _this_ device, clear it */ ++ if (dev->out_ctx && ++ xhci->dcbaa->dev_context_ptrs[slot_id] == cpu_to_le64(dev->out_ctx->dma)) ++ xhci->dcbaa->dev_context_ptrs[slot_id] = 0; + + trace_xhci_free_virt_device(dev); + +@@ -932,8 +931,9 @@ void xhci_free_virt_device(struct xhci_h + + if (dev->udev && dev->udev->slot_id) + dev->udev->slot_id = 0; +- kfree(xhci->devs[slot_id]); +- xhci->devs[slot_id] = NULL; ++ if (xhci->devs[slot_id] == dev) ++ xhci->devs[slot_id] = NULL; ++ kfree(dev); + } + + /* +@@ -975,7 +975,7 @@ static void xhci_free_virt_devices_depth + out: + /* we are now at a leaf device */ + xhci_debugfs_remove_slot(xhci, slot_id); +- xhci_free_virt_device(xhci, slot_id); ++ xhci_free_virt_device(xhci, vdev, slot_id); + } + + int xhci_alloc_virt_device(struct xhci_hcd *xhci, int slot_id, +--- a/drivers/usb/host/xhci-ring.c ++++ b/drivers/usb/host/xhci-ring.c +@@ -1258,7 +1258,8 @@ static void xhci_handle_cmd_enable_slot( + command->slot_id = 0; + } + +-static void xhci_handle_cmd_disable_slot(struct xhci_hcd *xhci, int slot_id) ++static void xhci_handle_cmd_disable_slot(struct xhci_hcd *xhci, int slot_id, ++ u32 cmd_comp_code) + { + struct xhci_virt_device *virt_dev; + struct xhci_slot_ctx *slot_ctx; +@@ -1273,6 +1274,10 @@ static void xhci_handle_cmd_disable_slot + if (xhci->quirks & XHCI_EP_LIMIT_QUIRK) + /* Delete default control endpoint resources */ + xhci_free_device_endpoint_resources(xhci, virt_dev, true); ++ if (cmd_comp_code == COMP_SUCCESS) { ++ xhci->dcbaa->dev_context_ptrs[slot_id] = 0; ++ xhci->devs[slot_id] = NULL; ++ } + } + + static void xhci_handle_cmd_config_ep(struct xhci_hcd *xhci, int slot_id, +@@ -1512,7 +1517,7 @@ static void handle_cmd_completion(struct + xhci_handle_cmd_enable_slot(xhci, slot_id, cmd, cmd_comp_code); + break; + case TRB_DISABLE_SLOT: +- xhci_handle_cmd_disable_slot(xhci, slot_id); ++ xhci_handle_cmd_disable_slot(xhci, slot_id, cmd_comp_code); + break; + case TRB_CONFIG_EP: + if (!cmd->completion) +--- a/drivers/usb/host/xhci.c ++++ b/drivers/usb/host/xhci.c +@@ -3947,7 +3947,7 @@ static void xhci_free_dev(struct usb_hcd + xhci_disable_slot(xhci, udev->slot_id); + + spin_lock_irqsave(&xhci->lock, flags); +- xhci_free_virt_device(xhci, udev->slot_id); ++ xhci_free_virt_device(xhci, virt_dev, udev->slot_id); + spin_unlock_irqrestore(&xhci->lock, flags); + + } +@@ -3996,6 +3996,16 @@ int xhci_disable_slot(struct xhci_hcd *x + return ret; + } + ++int xhci_disable_and_free_slot(struct xhci_hcd *xhci, u32 slot_id) ++{ ++ struct xhci_virt_device *vdev = xhci->devs[slot_id]; ++ int ret; ++ ++ ret = xhci_disable_slot(xhci, slot_id); ++ xhci_free_virt_device(xhci, vdev, slot_id); ++ return ret; ++} ++ + /* + * Checks if we have enough host controller resources for the default control + * endpoint. +@@ -4101,8 +4111,7 @@ int xhci_alloc_dev(struct usb_hcd *hcd, + return 1; + + disable_slot: +- xhci_disable_slot(xhci, udev->slot_id); +- xhci_free_virt_device(xhci, udev->slot_id); ++ xhci_disable_and_free_slot(xhci, udev->slot_id); + + return 0; + } +@@ -4238,8 +4247,7 @@ static int xhci_setup_device(struct usb_ + dev_warn(&udev->dev, "Device not responding to setup %s.\n", act); + + mutex_unlock(&xhci->mutex); +- ret = xhci_disable_slot(xhci, udev->slot_id); +- xhci_free_virt_device(xhci, udev->slot_id); ++ ret = xhci_disable_and_free_slot(xhci, udev->slot_id); + if (!ret) { + if (xhci_alloc_dev(hcd, udev) == 1) + xhci_setup_addressable_virt_dev(xhci, udev); +--- a/drivers/usb/host/xhci.h ++++ b/drivers/usb/host/xhci.h +@@ -1997,7 +1997,7 @@ void xhci_dbg_trace(struct xhci_hcd *xhc + /* xHCI memory management */ + void xhci_mem_cleanup(struct xhci_hcd *xhci); + int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags); +-void xhci_free_virt_device(struct xhci_hcd *xhci, int slot_id); ++void xhci_free_virt_device(struct xhci_hcd *xhci, struct xhci_virt_device *dev, int slot_id); + int xhci_alloc_virt_device(struct xhci_hcd *xhci, int slot_id, struct usb_device *udev, gfp_t flags); + int xhci_setup_addressable_virt_dev(struct xhci_hcd *xhci, struct usb_device *udev); + void xhci_copy_ep0_dequeue_into_input_ctx(struct xhci_hcd *xhci, +@@ -2087,6 +2087,7 @@ void xhci_reset_bandwidth(struct usb_hcd + int xhci_update_hub_device(struct usb_hcd *hcd, struct usb_device *hdev, + struct usb_tt *tt, gfp_t mem_flags); + int xhci_disable_slot(struct xhci_hcd *xhci, u32 slot_id); ++int xhci_disable_and_free_slot(struct xhci_hcd *xhci, u32 slot_id); + int xhci_ext_cap_init(struct xhci_hcd *xhci); + + int xhci_suspend(struct xhci_hcd *xhci, bool do_wakeup);