| From 8d63c83d8eb922f6c316320f50c82fa88d099bea Mon Sep 17 00:00:00 2001 |
| From: Alan Stern <stern@rowland.harvard.edu> |
| Date: Mon, 25 Aug 2025 12:00:22 -0400 |
| Subject: USB: gadget: dummy-hcd: Fix locking bug in RT-enabled kernels |
| |
| From: Alan Stern <stern@rowland.harvard.edu> |
| |
| commit 8d63c83d8eb922f6c316320f50c82fa88d099bea upstream. |
| |
| Yunseong Kim and the syzbot fuzzer both reported a problem in |
| RT-enabled kernels caused by the way dummy-hcd mixes interrupt |
| management and spin-locking. The pattern was: |
| |
| local_irq_save(flags); |
| spin_lock(&dum->lock); |
| ... |
| spin_unlock(&dum->lock); |
| ... // calls usb_gadget_giveback_request() |
| local_irq_restore(flags); |
| |
| The code was written this way because usb_gadget_giveback_request() |
| needs to be called with interrupts disabled and the private lock not |
| held. |
| |
| While this pattern works fine in non-RT kernels, it's not good when RT |
| is enabled. RT kernels handle spinlocks much like mutexes; in particular, |
| spin_lock() may sleep. But sleeping is not allowed while local |
| interrupts are disabled. |
| |
| To fix the problem, rewrite the code to conform to the pattern used |
| elsewhere in dummy-hcd and other UDC drivers: |
| |
| spin_lock_irqsave(&dum->lock, flags); |
| ... |
| spin_unlock(&dum->lock); |
| usb_gadget_giveback_request(...); |
| spin_lock(&dum->lock); |
| ... |
| spin_unlock_irqrestore(&dum->lock, flags); |
| |
| This approach satisfies the RT requirements. |
| |
| Signed-off-by: Alan Stern <stern@rowland.harvard.edu> |
| Cc: stable <stable@kernel.org> |
| Fixes: b4dbda1a22d2 ("USB: dummy-hcd: disable interrupts during req->complete") |
| Reported-by: Yunseong Kim <ysk@kzalloc.com> |
| Closes: <https://lore.kernel.org/linux-usb/5b337389-73b9-4ee4-a83e-7e82bf5af87a@kzalloc.com/> |
| Reported-by: syzbot+8baacc4139f12fa77909@syzkaller.appspotmail.com |
| Closes: <https://lore.kernel.org/linux-usb/68ac2411.050a0220.37038e.0087.GAE@google.com/> |
| Tested-by: syzbot+8baacc4139f12fa77909@syzkaller.appspotmail.com |
| CC: Sebastian Andrzej Siewior <bigeasy@linutronix.de> |
| CC: stable@vger.kernel.org |
| Reviewed-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> |
| Link: https://lore.kernel.org/r/bb192ae2-4eee-48ee-981f-3efdbbd0d8f0@rowland.harvard.edu |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| drivers/usb/gadget/udc/dummy_hcd.c | 8 ++++---- |
| 1 file changed, 4 insertions(+), 4 deletions(-) |
| |
| --- a/drivers/usb/gadget/udc/dummy_hcd.c |
| +++ b/drivers/usb/gadget/udc/dummy_hcd.c |
| @@ -765,8 +765,7 @@ static int dummy_dequeue(struct usb_ep * |
| if (!dum->driver) |
| return -ESHUTDOWN; |
| |
| - local_irq_save(flags); |
| - spin_lock(&dum->lock); |
| + spin_lock_irqsave(&dum->lock, flags); |
| list_for_each_entry(iter, &ep->queue, queue) { |
| if (&iter->req != _req) |
| continue; |
| @@ -776,15 +775,16 @@ static int dummy_dequeue(struct usb_ep * |
| retval = 0; |
| break; |
| } |
| - spin_unlock(&dum->lock); |
| |
| if (retval == 0) { |
| dev_dbg(udc_dev(dum), |
| "dequeued req %p from %s, len %d buf %p\n", |
| req, _ep->name, _req->length, _req->buf); |
| + spin_unlock(&dum->lock); |
| usb_gadget_giveback_request(_ep, _req); |
| + spin_lock(&dum->lock); |
| } |
| - local_irq_restore(flags); |
| + spin_unlock_irqrestore(&dum->lock, flags); |
| return retval; |
| } |
| |