]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
5.4-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 25 Aug 2025 07:39:33 +0000 (09:39 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 25 Aug 2025 07:39:33 +0000 (09:39 +0200)
added patches:
usb-xhci-fix-slot_id-resource-race-conflict.patch

queue-5.4/series
queue-5.4/usb-xhci-fix-slot_id-resource-race-conflict.patch [new file with mode: 0644]

index 9be5c19f29b41edf58f7f8af839818bceea02b61..1d20b742492cd605465ca6ecc367706bc830339d 100644 (file)
@@ -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 (file)
index 0000000..d871c9b
--- /dev/null
@@ -0,0 +1,212 @@
+From 2eb03376151bb8585caa23ed2673583107bb5193 Mon Sep 17 00:00:00 2001
+From: Weitao Wang <WeitaoWang-oc@zhaoxin.com>
+Date: Tue, 19 Aug 2025 15:58:43 +0300
+Subject: usb: xhci: Fix slot_id resource race conflict
+
+From: Weitao Wang <WeitaoWang-oc@zhaoxin.com>
+
+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 <WeitaoWang-oc@zhaoxin.com>
+Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com>
+Link: https://lore.kernel.org/r/20250819125844.2042452-2-mathias.nyman@linux.intel.com
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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);