The USB core API requires that usb_hcd_giveback_urb() is called with
interrupts disabled. When tx_complete() is invoked by
__usb_hcd_giveback_urb(), it uses this_cpu_ptr() (which relies on
smp_processor_id()), expecting to be executed in an atomic context where
preemption is disabled.
However, in vhci_recv_ret_submit() and several other places in the vhci
driver, interrupts are explicitly re-enabled using spin_unlock_irqrestore()
just before calling usb_hcd_giveback_urb(). Because interrupts (and thus
preemption) are enabled, the URB completion handler runs in a preemptible
kthread context, leading to the following crash:
BUG: using smp_processor_id() in preemptible [00000000] code: vhci_rx/5870
caller is tx_complete+0x15f/0x710 drivers/net/usb/usbnet.c:1301
Call Trace:
dump_stack_lvl+0xe8/0x150 lib/dump_stack.c:120
check_preemption_disabled+0xd3/0xe0 lib/smp_processor_id.c:47
tx_complete+0x15f/0x710 drivers/net/usb/usbnet.c:1301
__usb_hcd_giveback_urb+0x374/0x530 drivers/usb/core/hcd.c:1657
vhci_recv_ret_submit drivers/usb/usbip/vhci_rx.c:107 [inline]
vhci_rx_pdu drivers/usb/usbip/vhci_rx.c:242 [inline]
vhci_rx_loop+0x645/0xa80 drivers/usb/usbip/vhci_rx.c:265
kthread+0x388/0x470 kernel/kthread.c:436
ret_from_fork+0x514/0xb70 arch/x86/kernel/process.c:158
ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245
To fix this, ensure that interrupts remain disabled while calling
usb_hcd_giveback_urb(). This is done by changing spin_unlock_irqrestore()
to spin_unlock() before the call, preserving the interrupt state
established by spin_lock_irqsave(). After the call, the lock is re-acquired
with spin_lock() and then spin_unlock_irqrestore() is called to exit the
critical section and restore the original interrupt state.
This approach also maintains compatibility with PREEMPT_RT kernels, where
spin_lock_irqsave() maps to a sleeping lock and does not actually disable
hardware interrupts. Relying on the lock's IRQ state preservation avoids
crashing PREEMPT_RT kernels that would occur if local_irq_disable() and
local_irq_restore() were used directly.
Fixes: 04679b3489e0 ("Staging: USB/IP: add client driver")
Assisted-by: Gemini:gemini-3.5-flash Gemini:gemini-3.1-pro-preview syzbot
Reported-by: syzbot+04cd90bb99c6ef81a65d@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=04cd90bb99c6ef81a65d
Link: https://syzkaller.appspot.com/ai_job?id=53c3fa68-e0fd-43a3-8fe6-832fabf5b714
To: "Greg Kroah-Hartman"
To:
To: "Shuah Khan"
To: "Valentina Manea"
To: "Takahiro Hirofuchi"
Cc: "Hongren Zheng"
Cc:
---
diff --git a/drivers/usb/usbip/vhci_hcd.c b/drivers/usb/usbip/vhci_hcd.c
index 39e8faf4c..b416d8271 100644
--- a/drivers/usb/usbip/vhci_hcd.c
+++ b/drivers/usb/usbip/vhci_hcd.c
@@ -940,9 +940,9 @@ static int vhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
*/
usb_hcd_unlink_urb_from_ep(hcd, urb);
- spin_unlock_irqrestore(&vhci->lock, flags);
+ spin_unlock(&vhci->lock);
usb_hcd_giveback_urb(hcd, urb, urb->status);
- spin_lock_irqsave(&vhci->lock, flags);
+ spin_lock(&vhci->lock);
} else {
/* tcp connection is alive */
@@ -1008,11 +1008,11 @@ static void vhci_cleanup_unlink_list(struct vhci_device *vdev,
list_del(&unlink->list);
spin_unlock(&vdev->priv_lock);
- spin_unlock_irqrestore(&vhci->lock, flags);
+ spin_unlock(&vhci->lock);
usb_hcd_giveback_urb(hcd, urb, urb->status);
- spin_lock_irqsave(&vhci->lock, flags);
+ spin_lock(&vhci->lock);
spin_lock(&vdev->priv_lock);
kfree(unlink);
diff --git a/drivers/usb/usbip/vhci_rx.c b/drivers/usb/usbip/vhci_rx.c
index a678e7c89..6db3a5a73 100644
--- a/drivers/usb/usbip/vhci_rx.c
+++ b/drivers/usb/usbip/vhci_rx.c
@@ -102,10 +102,13 @@ static void vhci_recv_ret_submit(struct vhci_device *vdev,
spin_lock_irqsave(&vhci->lock, flags);
usb_hcd_unlink_urb_from_ep(vhci_hcd_to_hcd(vhci_hcd), urb);
- spin_unlock_irqrestore(&vhci->lock, flags);
+ spin_unlock(&vhci->lock);
usb_hcd_giveback_urb(vhci_hcd_to_hcd(vhci_hcd), urb, urb->status);
+ spin_lock(&vhci->lock);
+ spin_unlock_irqrestore(&vhci->lock, flags);
+
usbip_dbg_vhci_rx("Leave\n");
}
@@ -173,9 +176,12 @@ static void vhci_recv_ret_unlink(struct vhci_device *vdev,
spin_lock_irqsave(&vhci->lock, flags);
usb_hcd_unlink_urb_from_ep(vhci_hcd_to_hcd(vhci_hcd), urb);
- spin_unlock_irqrestore(&vhci->lock, flags);
+ spin_unlock(&vhci->lock);
usb_hcd_giveback_urb(vhci_hcd_to_hcd(vhci_hcd), urb, urb->status);
+
+ spin_lock(&vhci->lock);
+ spin_unlock_irqrestore(&vhci->lock, flags);
}
kfree(unlink);
base-commit: 075b74841bd0065a3bda3440873c747938e69b68
--
This is an AI-generated patch subject to moderation.
Reply with '#syz upstream' to Sign-off the patch as a human author
and send it to the upstream kernel mailing lists.
Reply with '#syz reject' to reject it ('#syz unreject' to undo).
See https://goo.gle/syzbot-ai-patches for information about AI-generated patches.
The person who has signed off on the patch is responsible for
addressing comments.
syzbot engineers can be reached at syzkaller@googlegroups.com.