Re: [PATCH] usbip: vhci_hcd: time out unanswered unlink requests

From: Hongyu Xie

Date: Tue Oct 20 2026 - 03:11:55 EST


Hi Shuah,

> Did you test this change with a usbip_host and client? How does this
> reproduce in a regular setup?

Yes, end to end with the usbip tools. The server ran usbipd
exporting a g_serial gadget (CDC ACM) on a usbip-vudc UDC. The
client, a v7.3-rc7 kernel built with and without the patch,
attached with `usbip attach` through a userspace TCP forwarding
agent. Once the device had enumerated, I stopped the forwarding
agent with SIGSTOP: the TCP connection stayed established, but no
RET_UNLINK came back. Then `echo 0 > /sys/bus/usb/devices/1-1/authorized`:

- unpatched: the writer hangs in D state forever. Two hung-task
warnings, 123 seconds apart, print the same stack:

__schedule+0x417/0x1910
schedule+0x28/0xb0
usb_kill_urb+0xb4/0x100
usb_start_wait_urb+0xd7/0x200
usb_control_msg+0x103/0x170
usb_control_msg_send+0x9e/0xf0
usb_set_configuration+0x69b/0x9d0
usb_deauthorize_device+0x3c/0x60
authorized_store+0x55/0xa0
dev_attr_store+0x17/0x40
sysfs_kf_write+0x74/0x90
kernfs_fop_write_iter+0x162/0x1f0
vfs_write+0x25b/0x480
ksys_write+0x71/0xf0
__x64_sys_write+0x19/0x30
do_syscall_64+0xba/0x550
entry_SYSCALL_64_after_hwframe+0x76/0x7e

- patched: the watchdog fires after 5s ("peer did not reply to an
unlink request within 5000 msecs"), tears the connection down
(stop threads / release socket / disconnect device), and the
writer returns instead of hanging.

The syzbot C reproducer, run verbatim in QEMU on v7.3.0-rc4, shows
the same before/after: without the patch the hub work item sits in
usb_kill_urb() holding address0_mutex and a second attach never
enumerates; with the patch it enumerates ~30s in.

In a regular setup, anything that leaves the TCP connection alive
while the peer stops replying reproduces this: a wedged proxy, a
firewall dropping the server-to-client direction, a hung server.
After import usbipd hands the socket fd to the kernel, so the stall
has to be in the network path, not in the daemon itself. The
client-side trigger is just a control transfer that times out and
is killed via usb_kill_urb().

Console logs of both rounds are available on request.

Thanks,
Hongyu