From: Raul E Rangel <rrangel@chromium.org>
To: Valentina Manea <valentina.manea.m@gmail.com>,
Shuah Khan <shuah@kernel.org>,
Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Hongren Zheng <i@zenithal.me>,
linux-kernel@vger.kernel.org, linux-usb@vger.kernel.org,
Raul E Rangel <rrangel@chromium.org>,
stable@vger.kernel.org, "Brian G. Merrell" <bgmerrell@novell.com>
Subject: [PATCH 6/6] usbip: vhci_hcd: reject URBs from stale usb_device after port re-attach
Date: Wed, 21 Oct 2026 09:07:01 -0600 [thread overview]
Message-ID: <20261021150704.3129446-7-rrangel@chromium.org> (raw)
In-Reply-To: <20261021150704.3129446-1-rrangel@chromium.org>
After vhci_shutdown_connection() runs, event_handler() immediately
calls vhci_device_reset(), setting vdev->ud.status to VDEV_ST_NULL and
clearing vdev->udev before usb_hub_wq finishes running usb_disconnect()
on the detached usb_device.
If attach_store() attaches a new connection to the same rhport before
usb_hub_wq disconnects the old usb_device, vdev->ud.tcp_socket becomes
non-NULL and vdev->ud.status becomes VDEV_ST_NOTASSIGNED. Because
vhci_urb_enqueue() maps urb->dev->portnum to vdev without verifying that
urb->dev matches vdev->udev, URBs submitted on the old usb_device pass
the connection state checks and get sent to the new remote device (or,
if a concurrent USBDEVFS_RESET resets the old usb_device to address 0,
overwrite vdev->udev).
Serialize vdev->udev clearing in vhci_device_reset() under vhci->lock
and ud->lock (dropping old usb_device and socket references outside the
spinlocks in both vhci_device_reset() and vhci_urb_enqueue()), and
reject URBs in vhci_urb_enqueue() when urb->dev != vdev->udev and either
usb_pipedevice(urb->pipe) != 0 or urb->dev is already registered
(device_is_registered(&urb->dev->dev)). During initial enumeration and
PORT_INIT_TRIES retries in hub_port_connect(), each candidate usb_device
is unregistered until usb_new_device() runs after hub_port_init() has
assigned vdev->udev, whereas an already-registered usb_device resetting
via usb_reset_device() is only valid when urb->dev == vdev->udev.
Fixes: 04679b3489e0 ("Staging: USB/IP: add client driver")
Cc: stable@vger.kernel.org
Assisted-by: Gemini
Signed-off-by: Raul E Rangel <rrangel@chromium.org>
---
drivers/usb/usbip/vhci_hcd.c | 44 +++++++++++++++++++++++++-----------
1 file changed, 31 insertions(+), 13 deletions(-)
diff --git a/drivers/usb/usbip/vhci_hcd.c b/drivers/usb/usbip/vhci_hcd.c
index 27dba51392b7..e8ff63c2276f 100644
--- a/drivers/usb/usbip/vhci_hcd.c
+++ b/drivers/usb/usbip/vhci_hcd.c
@@ -693,6 +693,7 @@ static int vhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flag
struct vhci_hcd *vhci_hcd = hcd_to_vhci_hcd(hcd);
struct vhci *vhci = vhci_hcd->vhci;
struct device *dev = &urb->dev->dev;
+ struct usb_device *old = NULL;
u8 portnum = urb->dev->portnum;
int ret = 0;
struct vhci_device *vdev;
@@ -737,6 +738,17 @@ static int vhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flag
kfree(priv);
return -ENODEV;
}
+ if ((usb_pipedevice(urb->pipe) != 0 ||
+ device_is_registered(&urb->dev->dev)) &&
+ urb->dev != vdev->udev) {
+ dev_err_ratelimited(dev,
+ "enqueue from stale usb_device on port %d\n",
+ vdev->rhport);
+ spin_unlock(&vdev->ud.lock);
+ spin_unlock_irqrestore(&vhci->lock, flags);
+ kfree(priv);
+ return -ENODEV;
+ }
spin_unlock(&vdev->ud.lock);
ret = usb_hcd_link_urb_to_ep(hcd, urb);
@@ -753,7 +765,6 @@ static int vhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flag
*
*/
if (usb_pipedevice(urb->pipe) == 0) {
- struct usb_device *old;
__u8 type = usb_pipetype(urb->pipe);
struct usb_ctrlrequest *ctrlreq =
(struct usb_ctrlrequest *) urb->setup_packet;
@@ -764,13 +775,13 @@ static int vhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flag
goto no_need_xmit;
}
- old = vdev->udev;
switch (ctrlreq->bRequest) {
case USB_REQ_SET_ADDRESS:
/* set_address may come when a device is reset */
dev_info(dev, "SetAddress Request (%d) to port %d\n",
ctrlreq->wValue, vdev->rhport);
+ old = vdev->udev;
vdev->udev = usb_get_dev(urb->dev);
/*
* NOTE: A similar operation has been done via
@@ -783,7 +794,6 @@ static int vhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flag
* should be dropped.
*/
dev_pm_syscore_device(&vdev->udev->dev, true);
- usb_put_dev(old);
spin_lock(&vdev->ud.lock);
vdev->ud.status = VDEV_ST_USED;
@@ -802,6 +812,7 @@ static int vhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flag
usbip_dbg_vhci_hc(
"Not yet?:Get_Descriptor to device 0 (get max pipe size)\n");
+ old = vdev->udev;
vdev->udev = usb_get_dev(urb->dev);
/*
* Set syscore PM flag for the virtually attached
@@ -814,7 +825,6 @@ static int vhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flag
* on disconnect.
*/
dev_pm_syscore_device(&vdev->udev->dev, true);
- usb_put_dev(old);
goto out;
default:
@@ -832,6 +842,7 @@ static int vhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flag
out:
vhci_tx_urb(urb, vdev, priv);
spin_unlock_irqrestore(&vhci->lock, flags);
+ usb_put_dev(old);
return 0;
@@ -848,6 +859,7 @@ static int vhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flag
spin_lock(&vhci->lock);
}
spin_unlock_irqrestore(&vhci->lock, flags);
+ usb_put_dev(old);
return ret;
}
@@ -1156,25 +1168,31 @@ static void vhci_shutdown_connection(struct usbip_device *ud)
static void vhci_device_reset(struct usbip_device *ud)
{
struct vhci_device *vdev = container_of(ud, struct vhci_device, ud);
- struct usb_device *old = vdev->udev;
+ struct vhci *vhci = vdev_to_vhci_hcd(vdev)->vhci;
+ struct usb_device *old;
+ struct socket *socket;
unsigned long flags;
- spin_lock_irqsave(&ud->lock, flags);
+ spin_lock_irqsave(&vhci->lock, flags);
+ spin_lock(&ud->lock);
vdev->speed = 0;
vdev->devid = 0;
+ old = vdev->udev;
vdev->udev = NULL;
- usb_put_dev(old);
- if (ud->tcp_socket) {
- sockfd_put(ud->tcp_socket);
- ud->tcp_socket = NULL;
- ud->sockfd = -1;
- }
+ socket = ud->tcp_socket;
+ ud->tcp_socket = NULL;
+ ud->sockfd = -1;
ud->status = VDEV_ST_NULL;
- spin_unlock_irqrestore(&ud->lock, flags);
+ spin_unlock(&ud->lock);
+ spin_unlock_irqrestore(&vhci->lock, flags);
+
+ usb_put_dev(old);
+ if (socket)
+ sockfd_put(socket);
}
static void vhci_device_unusable(struct usbip_device *ud)
--
2.56.0.212.g129ede20d9-goog
prev parent reply other threads:[~2026-10-21 15:08 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-21 15:06 [PATCH 0/6] usbip: vhci_hcd: fix URB giveback leaks and disconnect races Raul E Rangel
2026-10-21 15:06 ` [PATCH 1/6] usbip: vhci_hcd: protect urb->hcpriv with priv_lock in vhci_urb_dequeue() Raul E Rangel
2026-10-21 15:06 ` [PATCH 2/6] usbip: vhci_hcd: complete unsent URBs locally " Raul E Rangel
2026-10-21 15:06 ` [PATCH 3/6] usbip: vhci_hcd: stop enqueue and drain all pending URBs on disconnect Raul E Rangel
2026-10-21 15:06 ` [PATCH 4/6] usbip: vhci_hcd: clear URB_DMA_MAP_SG on all giveback paths Raul E Rangel
2026-10-21 15:07 ` [PATCH 5/6] usbip: vhci_hcd: allocate vhci_priv outside lock and handle ENOMEM Raul E Rangel
2026-10-21 15:07 ` Raul E Rangel [this message]
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=20261021150704.3129446-7-rrangel@chromium.org \
--to=rrangel@chromium.org \
--cc=bgmerrell@novell.com \
--cc=gregkh@linuxfoundation.org \
--cc=i@zenithal.me \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-usb@vger.kernel.org \
--cc=shuah@kernel.org \
--cc=stable@vger.kernel.org \
--cc=valentina.manea.m@gmail.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
all inboxes | Powered by JetHome®