Skip to content

Commit c4e232b

Browse files
Runyu Xiaogregkh
authored andcommitted
usb: dwc3: run gadget disconnect from sleepable suspend context
commit 0103829 upstream. dwc3_gadget_suspend() takes dwc->lock with IRQs disabled and then calls dwc3_disconnect_gadget(). For async callbacks that helper only uses plain spin_unlock()/spin_lock(), so the gadget ->disconnect() callback still runs with IRQs disabled and any sleepable callback trips Lockdep. This issue was found by our static analysis tool and then manually reviewed against the current tree. The grounded PoC kept the dwc3_gadget_suspend() -> dwc3_disconnect_gadget() -> gadget_driver->disconnect() chain, and Lockdep reported: BUG: sleeping function called from invalid context gadget_disconnect+0x21/0x39 [vuln_msv] dwc3_gadget_suspend.constprop.0+0x2b/0x42 [vuln_msv] Keep the disconnect callback selection in one common helper, but add a sleepable suspend-side wrapper which snapshots the callback under dwc->lock and then runs it after spin_unlock_irqrestore(). The regular event path still uses the existing spin_unlock()/spin_lock() window. Fixes: c854087 ("usb: dwc3: gadget: Improve dwc3_gadget_suspend() and dwc3_gadget_resume()") Cc: stable <stable@kernel.org> Signed-off-by: Runyu Xiao <runyu.xiao@seu.edu.cn> Acked-by: Thinh Nguyen <Thinh.Nguyen@synopsys.com> Link: https://patch.msgid.link/20260612052005.3849659-1-runyu.xiao@seu.edu.cn Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent 2a52d55 commit c4e232b

1 file changed

Lines changed: 36 additions & 7 deletions

File tree

drivers/usb/dwc3/gadget.c

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3934,15 +3934,48 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
39343934
}
39353935
}
39363936

3937+
static bool dwc3_prepare_disconnect_gadget(struct dwc3 *dwc,
3938+
struct usb_gadget_driver **driver,
3939+
struct usb_gadget **gadget)
3940+
{
3941+
if (!dwc->async_callbacks || !dwc->gadget_driver ||
3942+
!dwc->gadget_driver->disconnect)
3943+
return false;
3944+
3945+
*driver = dwc->gadget_driver;
3946+
*gadget = dwc->gadget;
3947+
3948+
return true;
3949+
}
3950+
39373951
static void dwc3_disconnect_gadget(struct dwc3 *dwc)
39383952
{
3939-
if (dwc->async_callbacks && dwc->gadget_driver->disconnect) {
3953+
struct usb_gadget_driver *driver;
3954+
struct usb_gadget *gadget;
3955+
3956+
if (dwc3_prepare_disconnect_gadget(dwc, &driver, &gadget)) {
39403957
spin_unlock(&dwc->lock);
3941-
dwc->gadget_driver->disconnect(dwc->gadget);
3958+
driver->disconnect(gadget);
39423959
spin_lock(&dwc->lock);
39433960
}
39443961
}
39453962

3963+
static void dwc3_disconnect_gadget_sleepable(struct dwc3 *dwc)
3964+
{
3965+
struct usb_gadget_driver *driver;
3966+
struct usb_gadget *gadget;
3967+
unsigned long flags;
3968+
3969+
spin_lock_irqsave(&dwc->lock, flags);
3970+
if (!dwc3_prepare_disconnect_gadget(dwc, &driver, &gadget)) {
3971+
spin_unlock_irqrestore(&dwc->lock, flags);
3972+
return;
3973+
}
3974+
3975+
spin_unlock_irqrestore(&dwc->lock, flags);
3976+
driver->disconnect(gadget);
3977+
}
3978+
39463979
static void dwc3_suspend_gadget(struct dwc3 *dwc)
39473980
{
39483981
if (dwc->async_callbacks && dwc->gadget_driver->suspend) {
@@ -4836,7 +4869,6 @@ void dwc3_gadget_exit(struct dwc3 *dwc)
48364869

48374870
int dwc3_gadget_suspend(struct dwc3 *dwc)
48384871
{
4839-
unsigned long flags;
48404872
int ret;
48414873

48424874
ret = dwc3_gadget_soft_disconnect(dwc);
@@ -4850,10 +4882,7 @@ int dwc3_gadget_suspend(struct dwc3 *dwc)
48504882
return -EAGAIN;
48514883
}
48524884

4853-
spin_lock_irqsave(&dwc->lock, flags);
4854-
if (dwc->gadget_driver)
4855-
dwc3_disconnect_gadget(dwc);
4856-
spin_unlock_irqrestore(&dwc->lock, flags);
4885+
dwc3_disconnect_gadget_sleepable(dwc);
48574886

48584887
return 0;
48594888
}

0 commit comments

Comments
 (0)