* [PATCH] usb: gadget: udc: Fix NULL pointer dereference in gadget_match_driver
@ 2026-05-26 7:06 Jimmy Hu
2026-05-26 18:00 ` Alan Stern
0 siblings, 1 reply; 6+ messages in thread
From: Jimmy Hu @ 2026-05-26 7:06 UTC (permalink / raw)
To: Greg Kroah-Hartman; +Cc: Alan Stern, linux-usb, linux-kernel, Jimmy Hu, stable
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
^ permalink raw reply related [flat|nested] 6+ messages in thread* Re: [PATCH] usb: gadget: udc: Fix NULL pointer dereference in gadget_match_driver 2026-05-26 7:06 [PATCH] usb: gadget: udc: Fix NULL pointer dereference in gadget_match_driver Jimmy Hu @ 2026-05-26 18:00 ` Alan Stern 2026-06-02 5:34 ` Jimmy Hu (xWF) 0 siblings, 1 reply; 6+ messages in thread From: Alan Stern @ 2026-05-26 18:00 UTC (permalink / raw) To: Jimmy Hu; +Cc: Greg Kroah-Hartman, linux-usb, linux-kernel, stable 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 ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH] usb: gadget: udc: Fix NULL pointer dereference in gadget_match_driver 2026-05-26 18:00 ` Alan Stern @ 2026-06-02 5:34 ` Jimmy Hu (xWF) 2026-06-02 14:30 ` Alan Stern 0 siblings, 1 reply; 6+ messages in thread From: Jimmy Hu (xWF) @ 2026-06-02 5:34 UTC (permalink / raw) To: Alan Stern; +Cc: Greg Kroah-Hartman, linux-usb, linux-kernel, stable 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 ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH] usb: gadget: udc: Fix NULL pointer dereference in gadget_match_driver 2026-06-02 5:34 ` Jimmy Hu (xWF) @ 2026-06-02 14:30 ` Alan Stern 2026-06-16 5:14 ` Jimmy Hu (xWF) 0 siblings, 1 reply; 6+ messages in thread From: Alan Stern @ 2026-06-02 14:30 UTC (permalink / raw) To: Jimmy Hu (xWF); +Cc: Greg Kroah-Hartman, linux-usb, linux-kernel, stable 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 ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH] usb: gadget: udc: Fix NULL pointer dereference in gadget_match_driver 2026-06-02 14:30 ` Alan Stern @ 2026-06-16 5:14 ` Jimmy Hu (xWF) 2026-06-16 14:28 ` Alan Stern 0 siblings, 1 reply; 6+ messages in thread From: Jimmy Hu (xWF) @ 2026-06-16 5:14 UTC (permalink / raw) To: Alan Stern; +Cc: Greg Kroah-Hartman, linux-usb, linux-kernel, stable On Tue, Jun 2, 2026 at 10:30 PM Alan Stern <stern@rowland.harvard.edu> wrote: > > 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 Hi Alan, I have captured the KASAN log with the extra debugging information you requested, which shows how this race condition occurs. The log shows that after gadget_match_driver() enters execution on one core, a parallel core can invoke usb_del_gadget() and complete both device_del(&gadget->dev) and device_unregister(&udc->dev) before strcmp() executes. Here is the exact timeline from the dmesg output: 1. At 268.595241, task 1374 (configfs path) enters gadget_match_driver() (on CPU6): [ 268.595241][ T1374] [CPU6 android.hardwar]:[JJ][core.c/gadget_match_driver/1568] Enter 2. At 268.595250 (only 9 us later), DRD work invokes usb_del_gadget() (on CPU3): [ 268.595250][ T102] [CPU3 kworker/3:1]:[JJ][core.c/usb_del_gadget/1529] Before device_del(&gadget->dev); [ 268.598129][ T102] [CPU3 kworker/3:1]:[JJ][core.c/usb_del_gadget/1531] After device_del(&gadget->dev); [ 268.598159][ T102] [CPU3 kworker/3:1]:[JJ][core.c/usb_del_gadget/1534] Before device_unregister(&udc->dev); [ 268.599405][ T102] [CPU3 kworker/3:1]:[JJ][core.c/usb_del_gadget/1536] After device_unregister(&udc->dev); 3. At 268.599427, task 1374 starts comparison, where it triggers a KASAN invalid-access. (Due to kernel preemption, the task was migrated to CPU7): [ 268.599434][ T1374] BUG: KASAN: invalid-access in gadget_match_driver+0x150/0x1cc [ 268.599448][ T1374] Read of size 8 at addr 66ffff801a49b880 by task android.hardwar/1374 [ 268.599454][ T1374] Pointer tag: [66], memory tag: [fe] [ 268.599456][ T1374] [ 268.599460][ T1374] CPU: 7 PID: 1374 Comm: android.hardwar Tainted: G S W O 6.1.124-android14-11-ga633402dff84-dirty #1 To resolve this object lifetime issue, I see two potential approaches: 1. Protect the UDC device lifecycle during the comparison phase using get_device(&udc->dev) and put_device() (as a lightweight fix). 2. Serialize the configfs match path and the unregister path using a subsystem mutex. I would highly appreciate your thoughts on which direction you prefer. Does this data address the scenario you raised? Thanks, Jimmy ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH] usb: gadget: udc: Fix NULL pointer dereference in gadget_match_driver 2026-06-16 5:14 ` Jimmy Hu (xWF) @ 2026-06-16 14:28 ` Alan Stern 0 siblings, 0 replies; 6+ messages in thread From: Alan Stern @ 2026-06-16 14:28 UTC (permalink / raw) To: Jimmy Hu (xWF); +Cc: Greg Kroah-Hartman, linux-usb, linux-kernel, stable On Tue, Jun 16, 2026 at 01:14:03PM +0800, Jimmy Hu (xWF) wrote: > On Tue, Jun 2, 2026 at 10:30 PM Alan Stern <stern@rowland.harvard.edu> wrote: > > > > 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 > > Hi Alan, > > I have captured the KASAN log with the extra debugging information > you requested, which shows how this race condition occurs. > > The log shows that after gadget_match_driver() enters execution on > one core, a parallel core can invoke usb_del_gadget() and complete > both device_del(&gadget->dev) and device_unregister(&udc->dev) > before strcmp() executes. > > Here is the exact timeline from the dmesg output: > > 1. At 268.595241, task 1374 (configfs path) enters > gadget_match_driver() (on CPU6): > [ 268.595241][ T1374] [CPU6 > android.hardwar]:[JJ][core.c/gadget_match_driver/1568] Enter > > 2. At 268.595250 (only 9 us later), DRD work invokes usb_del_gadget() (on CPU3): > [ 268.595250][ T102] [CPU3 > kworker/3:1]:[JJ][core.c/usb_del_gadget/1529] Before > device_del(&gadget->dev); > [ 268.598129][ T102] [CPU3 > kworker/3:1]:[JJ][core.c/usb_del_gadget/1531] After > device_del(&gadget->dev); > [ 268.598159][ T102] [CPU3 > kworker/3:1]:[JJ][core.c/usb_del_gadget/1534] Before > device_unregister(&udc->dev); > [ 268.599405][ T102] [CPU3 > kworker/3:1]:[JJ][core.c/usb_del_gadget/1536] After > device_unregister(&udc->dev); > > 3. At 268.599427, task 1374 starts comparison, where it triggers a > KASAN invalid-access. (Due to kernel preemption, the task was migrated > to CPU7): > [ 268.599434][ T1374] BUG: KASAN: invalid-access in > gadget_match_driver+0x150/0x1cc > [ 268.599448][ T1374] Read of size 8 at addr 66ffff801a49b880 by task > android.hardwar/1374 > [ 268.599454][ T1374] Pointer tag: [66], memory tag: [fe] > [ 268.599456][ T1374] > [ 268.599460][ T1374] CPU: 7 PID: 1374 Comm: android.hardwar Tainted: > G S W O 6.1.124-android14-11-ga633402dff84-dirty #1 Now I see the problem. I had thought that the gadget device would be locked while gadget_match_driver() runs, but it isn't. > To resolve this object lifetime issue, I see two potential approaches: > > 1. Protect the UDC device lifecycle during the comparison phase using > get_device(&udc->dev) and put_device() (as a lightweight fix). > 2. Serialize the configfs match path and the unregister path using > a subsystem mutex. > > I would highly appreciate your thoughts on which direction you prefer. > Does this data address the scenario you raised? The problem is to make sure that the udc structure (and its name) remains allocated during the match. The best way to do this is to have the gadget take a reference to the udc when it is added, and have it drop the reference when the gadget is released. Not take and drop a reference every time the comparison phase runs. Unfortunately, this will require changing the way we handle releasing gadgets. The gadget driver's own release routine could be stored as a pointer in the udc structure, and there would have to be a new usb_gadget_release() routine added to the core. This routine would drop the reference to the udc and then call the gadget driver's release routine. See how usb_initialize_gadget(), usb_add_gadget_udc(), usb_add_gadget_udc_release(), and usb_udc_nop_release() interact -- that whole arrangement would be affected. But it wouldn't be a horribly complicated change. Alan Stern ^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2026-06-16 14:28 UTC | newest] Thread overview: 6+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-05-26 7:06 [PATCH] usb: gadget: udc: Fix NULL pointer dereference in gadget_match_driver Jimmy Hu 2026-05-26 18:00 ` Alan Stern 2026-06-02 5:34 ` Jimmy Hu (xWF) 2026-06-02 14:30 ` Alan Stern 2026-06-16 5:14 ` Jimmy Hu (xWF) 2026-06-16 14:28 ` Alan Stern
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox