[PATCH] usb: gadget: udc: Fix NULL pointer dereference in gadget_match_driver

Jimmy Hu posted 1 patch 1 week, 6 days ago
drivers/usb/gadget/udc/core.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
[PATCH] usb: gadget: udc: Fix NULL pointer dereference in gadget_match_driver
Posted by Jimmy Hu 1 week, 6 days ago
A NULL pointer dereference occurs in gadget_match_driver() because a
race condition exists between the DRD mode-switch work and the
configfs UDC write path:

1. The DRD mode-switch work invokes __dwc3_set_mode(), which calls
   dwc3_gadget_exit() and subsequently frees the UDC device name via
   device_unregister(&udc->dev).
2. The configfs UDC write path invokes gadget_dev_desc_UDC_store(),
   which calls usb_gadget_register_driver() and subsequently
   compares the UDC device name via gadget_match_driver().

If gadget_match_driver() runs concurrently during UDC unregistration, it
may access the freed UDC device name. Once the freed memory is zeroed,
dev_name(&udc->dev) returns NULL, causing a panic in strcmp().

[39430.908615][ T1171] Unable to handle kernel NULL pointer dereference at virtual address 0000000000000000
[39430.911397][ T1171] pc : __pi_strcmp+0x20/0x140
[39430.911441][ T1171] lr : gadget_match_driver+0x34/0x60
...
[39430.911890][ T1171]  usb_gadget_register_driver_owner+0x50/0xf8
[39430.911910][ T1171]  gadget_dev_desc_UDC_store+0xf4/0x140
[39430.931308][ T1171]  configfs_write_iter+0xec/0x134
...
[39430.957058][ T1171] Workqueue: events_freezable __dwc3_set_mode
[39430.957287][ T1171]  dwc3_gadget_exit+0x34/0x8c
[39430.957304][ T1171]  __dwc3_set_mode+0xc0/0x664
[39430.957341][ T1171]  worker_thread+0x244/0x334

Fix this by checking dev_name(&udc->dev) before calling strcmp().

Fixes: fc274c1e9973 ("USB: gadget: Add a new bus for gadgets")
Cc: stable@vger.kernel.org
Signed-off-by: Jimmy Hu <hhhuuu@google.com>
---
 drivers/usb/gadget/udc/core.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/usb/gadget/udc/core.c b/drivers/usb/gadget/udc/core.c
index e8861eaad907..79baed640428 100644
--- a/drivers/usb/gadget/udc/core.c
+++ b/drivers/usb/gadget/udc/core.c
@@ -1594,7 +1594,7 @@ static int gadget_match_driver(struct device *dev, const struct device_driver *d
 			struct usb_gadget_driver, driver);
 
 	/* If the driver specifies a udc_name, it must match the UDC's name */
-	if (driver->udc_name &&
+	if (driver->udc_name && dev_name(&udc->dev) &&
 			strcmp(driver->udc_name, dev_name(&udc->dev)) != 0)
 		return 0;
 

base-commit: 5d6919055dec134de3c40167a490f33c74c12581
-- 
2.54.0.746.g67dd491aae-goog
Re: [PATCH] usb: gadget: udc: Fix NULL pointer dereference in gadget_match_driver
Posted by Alan Stern 1 week, 6 days ago
On Tue, May 26, 2026 at 03:06:35PM +0800, Jimmy Hu wrote:
> A NULL pointer dereference occurs in gadget_match_driver() because a
> race condition exists between the DRD mode-switch work and the
> configfs UDC write path:
> 
> 1. The DRD mode-switch work invokes __dwc3_set_mode(), which calls
>    dwc3_gadget_exit() and subsequently frees the UDC device name via
>    device_unregister(&udc->dev).
> 2. The configfs UDC write path invokes gadget_dev_desc_UDC_store(),
>    which calls usb_gadget_register_driver() and subsequently
>    compares the UDC device name via gadget_match_driver().
> 
> If gadget_match_driver() runs concurrently during UDC unregistration, it
> may access the freed UDC device name. Once the freed memory is zeroed,
> dev_name(&udc->dev) returns NULL, causing a panic in strcmp().

I don't see how this can happen.  gadget_match_driver() runs during 
probing of a gadget, which takes place only while the gadget is 
registered in the device core.  But usb_del_gadget() calls 
device_del(&gadget->dev) before it calls device_unregister(&udc->dev).  
This means that at any time when gadget_match_driver() can run, the UDC 
device name must still be allocated.

You should run more tests.  Add debugging printk() calls just before and 
just after the device_del(&gadget->dev) and device_unregister(&udc->dev) 
lines, and inside gadget_match_driver(), so the tests will show 
unambiguously when these things happen with respect to each other.

> Fix this by checking dev_name(&udc->dev) before calling strcmp().

Adding a check like this will not fix a race; it will only make the race 
less likely to occur.  It won't prevent the name from being deallocated 
between the check and the strcmp() call.

Alan Stern
Re: [PATCH] usb: gadget: udc: Fix NULL pointer dereference in gadget_match_driver
Posted by Jimmy Hu (xWF) 6 days, 16 hours ago
On Wed, May 27, 2026 at 2:00 AM Alan Stern <stern@rowland.harvard.edu> wrote:
>
> On Tue, May 26, 2026 at 03:06:35PM +0800, Jimmy Hu wrote:
> > A NULL pointer dereference occurs in gadget_match_driver() because a
> > race condition exists between the DRD mode-switch work and the
> > configfs UDC write path:
> >
> > 1. The DRD mode-switch work invokes __dwc3_set_mode(), which calls
> >    dwc3_gadget_exit() and subsequently frees the UDC device name via
> >    device_unregister(&udc->dev).
> > 2. The configfs UDC write path invokes gadget_dev_desc_UDC_store(),
> >    which calls usb_gadget_register_driver() and subsequently
> >    compares the UDC device name via gadget_match_driver().
> >
> > If gadget_match_driver() runs concurrently during UDC unregistration, it
> > may access the freed UDC device name. Once the freed memory is zeroed,
> > dev_name(&udc->dev) returns NULL, causing a panic in strcmp().
>
> I don't see how this can happen.  gadget_match_driver() runs during
> probing of a gadget, which takes place only while the gadget is
> registered in the device core.  But usb_del_gadget() calls
> device_del(&gadget->dev) before it calls device_unregister(&udc->dev).
> This means that at any time when gadget_match_driver() can run, the UDC
> device name must still be allocated.
>
> You should run more tests.  Add debugging printk() calls just before and
> just after the device_del(&gadget->dev) and device_unregister(&udc->dev)
> lines, and inside gadget_match_driver(), so the tests will show
> unambiguously when these things happen with respect to each other.
>
> > Fix this by checking dev_name(&udc->dev) before calling strcmp().
>
> Adding a check like this will not fix a race; it will only make the race
> less likely to occur.  It won't prevent the name from being deallocated
> between the check and the strcmp() call.
>
> Alan Stern

Hi Alan,

Thank you for the review. You are absolutely right about the TOCTOU risk;
the simple NULL check does not prevent the name from being deallocated
after the check but before the strcmp() call.

I will submit a v2 patch that uses get_device(&udc->dev) and put_device()
to increment the UDC reference count during the matching phase. This will
guarantee that the UDC device name remains allocated and valid throughout
the entire duration of strcmp(), eliminating the race condition structurally.

Does this approach sound reasonable to you?

Thanks,
Jimmy
Re: [PATCH] usb: gadget: udc: Fix NULL pointer dereference in gadget_match_driver
Posted by Alan Stern 6 days, 7 hours ago
On Tue, Jun 02, 2026 at 01:34:07PM +0800, Jimmy Hu (xWF) wrote:
> On Wed, May 27, 2026 at 2:00 AM Alan Stern <stern@rowland.harvard.edu> wrote:
> >
> > On Tue, May 26, 2026 at 03:06:35PM +0800, Jimmy Hu wrote:
> > > A NULL pointer dereference occurs in gadget_match_driver() because a
> > > race condition exists between the DRD mode-switch work and the
> > > configfs UDC write path:
> > >
> > > 1. The DRD mode-switch work invokes __dwc3_set_mode(), which calls
> > >    dwc3_gadget_exit() and subsequently frees the UDC device name via
> > >    device_unregister(&udc->dev).
> > > 2. The configfs UDC write path invokes gadget_dev_desc_UDC_store(),
> > >    which calls usb_gadget_register_driver() and subsequently
> > >    compares the UDC device name via gadget_match_driver().
> > >
> > > If gadget_match_driver() runs concurrently during UDC unregistration, it
> > > may access the freed UDC device name. Once the freed memory is zeroed,
> > > dev_name(&udc->dev) returns NULL, causing a panic in strcmp().
> >
> > I don't see how this can happen.  gadget_match_driver() runs during
> > probing of a gadget, which takes place only while the gadget is
> > registered in the device core.  But usb_del_gadget() calls
> > device_del(&gadget->dev) before it calls device_unregister(&udc->dev).
> > This means that at any time when gadget_match_driver() can run, the UDC
> > device name must still be allocated.
> >
> > You should run more tests.  Add debugging printk() calls just before and
> > just after the device_del(&gadget->dev) and device_unregister(&udc->dev)
> > lines, and inside gadget_match_driver(), so the tests will show
> > unambiguously when these things happen with respect to each other.
> >
> > > Fix this by checking dev_name(&udc->dev) before calling strcmp().
> >
> > Adding a check like this will not fix a race; it will only make the race
> > less likely to occur.  It won't prevent the name from being deallocated
> > between the check and the strcmp() call.
> >
> > Alan Stern
> 
> Hi Alan,
> 
> Thank you for the review. You are absolutely right about the TOCTOU risk;
> the simple NULL check does not prevent the name from being deallocated
> after the check but before the strcmp() call.
> 
> I will submit a v2 patch that uses get_device(&udc->dev) and put_device()
> to increment the UDC reference count during the matching phase. This will
> guarantee that the UDC device name remains allocated and valid throughout
> the entire duration of strcmp(), eliminating the race condition structurally.
> 
> Does this approach sound reasonable to you?

No, because you haven't addressed the issue I raised at the start of my 
email, namely, how can this problem actually occur?  And you didn't run 
additional tests with the extra debugging information that I asked for.

Alan Stern