Re: [syzbot] [usb?] general protection fault in usb_gadget_udc_reset (4)

From: Alan Stern

Date: Tue Mar 10 2026 - 15:03:11 EST


On Tue, Mar 10, 2026 at 09:09:02AM -0700, syzbot wrote:
> Hello,
>
> syzbot has tested the proposed patch but the reproducer is still triggering an issue:
> general protection fault in usb_gadget_udc_reset
>
> gadget gadget.4: Reset #1, gadget ffff8880294bcc40 driver 0000000000000000
> Oops: general protection fault, probably for non-canonical address 0xdffffc0000000008: 0000 [#1] SMP KASAN PTI

That's a little surprising. Here's a test with more debugging
printouts, along with a delay to make a race more likely to occur.

Alan Stern

#syz test: upstream 651690480a96

Index: usb-devel/drivers/usb/gadget/udc/core.c
===================================================================
--- usb-devel.orig/drivers/usb/gadget/udc/core.c
+++ usb-devel/drivers/usb/gadget/udc/core.c
@@ -1197,7 +1197,9 @@ EXPORT_SYMBOL_GPL(usb_udc_vbus_handler);
void usb_gadget_udc_reset(struct usb_gadget *gadget,
struct usb_gadget_driver *driver)
{
+ dev_info(&gadget->dev, "Reset #1, gadget %p driver %p\n", gadget, driver);
driver->reset(gadget);
+ dev_info(&gadget->dev, "Reset #2\n");
usb_gadget_set_state(gadget, USB_STATE_DEFAULT);
}
EXPORT_SYMBOL_GPL(usb_gadget_udc_reset);
Index: usb-devel/drivers/usb/gadget/udc/dummy_hcd.c
===================================================================
--- usb-devel.orig/drivers/usb/gadget/udc/dummy_hcd.c
+++ usb-devel/drivers/usb/gadget/udc/dummy_hcd.c
@@ -464,7 +464,9 @@ static void set_link_state(struct dummy_
if (dum->ints_enabled && (disconnect || reset)) {
stop_activity(dum);
++dum->callback_usage;
+ dev_info(&dum->gadget.dev, "Reset usage %d\n", dum->callback_usage);
spin_unlock(&dum->lock);
+ udelay(1000);
if (reset)
usb_gadget_udc_reset(&dum->gadget, dum->driver);
else
@@ -908,21 +910,6 @@ static int dummy_pullup(struct usb_gadge
spin_lock_irqsave(&dum->lock, flags);
dum->pullup = (value != 0);
set_link_state(dum_hcd);
- if (value == 0) {
- /*
- * Emulate synchronize_irq(): wait for callbacks to finish.
- * This seems to be the best place to emulate the call to
- * synchronize_irq() that's in usb_gadget_remove_driver().
- * Doing it in dummy_udc_stop() would be too late since it
- * is called after the unbind callback and unbind shouldn't
- * be invoked until all the other callbacks are finished.
- */
- while (dum->callback_usage > 0) {
- spin_unlock_irqrestore(&dum->lock, flags);
- usleep_range(1000, 2000);
- spin_lock_irqsave(&dum->lock, flags);
- }
- }
spin_unlock_irqrestore(&dum->lock, flags);

usb_hcd_poll_rh_status(dummy_hcd_to_hcd(dum_hcd));
@@ -945,6 +932,25 @@ static void dummy_udc_async_callbacks(st

spin_lock_irq(&dum->lock);
dum->ints_enabled = enable;
+ if (!enable) {
+ /*
+ * Emulate synchronize_irq(): wait for callbacks to finish.
+ * This seems to be the best place to emulate the call to
+ * synchronize_irq() that's in usb_gadget_remove_driver().
+ * It has to come after dum->ints_enabled is clear. But
+ * doing it in dummy_udc_stop() would be too late since that
+ * routine is called after the unbind callback, and unbind
+ * shouldn't be invoked until all the other callbacks are
+ * finished.
+ */
+ dev_info(&_gadget->dev, "Disable usage %d\n", dum->callback_usage);
+ while (dum->callback_usage > 0) {
+ spin_unlock_irq(&dum->lock);
+ usleep_range(1000, 2000);
+ spin_lock_irq(&dum->lock);
+ }
+ dev_info(&_gadget->dev, "Disable done\n");
+ }
spin_unlock_irq(&dum->lock);
}

@@ -1034,6 +1040,7 @@ static int dummy_udc_stop(struct usb_gad
dum->ints_enabled = 0;
stop_activity(dum);
dum->driver = NULL;
+ dev_info(&g->dev, "Dummy stop\n");
spin_unlock_irq(&dum->lock);

return 0;