[PATCH RFC] usb: gadget: f_uac1, f_uac2: fix memory leak in rate attribute store
"syzbot" <[email protected]> Mon, 3 Aug 2026 12:01:41 +0000 (UTC)
| Newsgroups | dev.linux.lists.syzbot |
|---|---|
| Message-ID | <[email protected]> |
A memory leak occurs in the `f_uac1_opts_##name##_store` and
`f_uac2_opts_##name##_store` functions, which are generated by the
`UAC1_RATE_ATTRIBUTE` and `UAC2_RATE_ATTRIBUTE` macros.
When setting the sampling rates via configfs (e.g., writing to `p_srate` or
`c_srate`), the input string is duplicated using `kstrdup()`. The
duplicated string is then parsed using `strsep()`, which modifies the
pointer to point to the next token. By the time the loop finishes, the
pointer is updated to `NULL` (or points to the middle of the string if an
error occurred). Consequently, `kfree()` is called with an invalid pointer,
and the original memory block allocated by `kstrdup()` is never freed,
causing a memory leak.
Fix this by preserving the original pointer returned by `kstrdup()` in a
separate variable `split_page_alloc` so that it can be correctly passed to
`kfree()`. Additionally, add a check to return `-ENOMEM` if `kstrdup()`
fails to allocate memory.
Kmemleak reports the following:
BUG: memory leak
unreferenced object 0xffff88818b6ecf00 (size 64):
comm "syz.0.17", pid 6220, jiffies 4294944006
hex dump (first 32 bytes):
34 34 31 30 30 00 34 38 30 30 30 00 38 38 32 30 44100.48000.8820
30 00 39 36 30 30 30 00 31 37 36 34 30 30 00 31 0.96000.176400.1
backtrace (crc 6cdecf20):
kmemleak_alloc_recursive include/linux/kmemleak.h:44 [inline]
slab_post_alloc_hook mm/slub.c:4597 [inline]
slab_alloc_node mm/slub.c:4917 [inline]
__do_kmalloc_node mm/slub.c:5333 [inline]
__kmalloc_node_track_caller_noprof+0x309/0x4e0 mm/slub.c:5471
__kmemdup_nul mm/util.c:64 [inline]
kstrdup+0x3e/0xc0 mm/util.c:84
f_uac2_opts_p_srate_store+0xeb/0x1a0
drivers/usb/gadget/function/f_uac2.c:2087
flush_write_buffer fs/configfs/file.c:207 [inline]
configfs_write_iter+0x115/0x170 fs/configfs/file.c:229
new_sync_write fs/read_write.c:595 [inline]
vfs_write+0x3a0/0x640 fs/read_write.c:687
Fixes: a7339e4f5788 ("usb: gadget: f_uac2: Support multiple sampling rates")
Assisted-by: Gemini:gemini-3.5-flash Gemini:gemini-3.1-pro-preview syzbot
Reported-by: [email protected]
Closes: https://syzkaller.appspot.com/bug?extid=87c10526d2cfa8d14ff6
Link: https://syzkaller.appspot.com/ai_job?id=4939b73c-7f4a-4a7b-8e48-7889ee6fc965
To: "Greg Kroah-Hartman" <[email protected]>
To: <[email protected]>
To: "Julian Scheel" <[email protected]>
Cc: "Christophe JAILLET" <[email protected]>
Cc: "Kees Cook" <[email protected]>
Cc: <[email protected]>
---
diff --git a/drivers/usb/gadget/function/f_uac1.c b/drivers/usb/gadget/function/f_uac1.c
index 85c502e98..ba1a9f51c 100644
--- a/drivers/usb/gadget/function/f_uac1.c
+++ b/drivers/usb/gadget/function/f_uac1.c
@@ -1595,6 +1595,7 @@ static ssize_t f_uac1_opts_##name##_store(struct config_item *item, \
{ \
struct f_uac1_opts *opts = to_f_uac1_opts(item); \
char *split_page = NULL; \
+ char *split_page_alloc = NULL; \
int ret = -EINVAL; \
char *token; \
u32 num; \
@@ -1608,7 +1609,12 @@ static ssize_t f_uac1_opts_##name##_store(struct config_item *item, \
\
i = 0; \
memset(opts->name##s, 0x00, sizeof(opts->name##s)); \
- split_page = kstrdup(page, GFP_KERNEL); \
+ split_page_alloc = kstrdup(page, GFP_KERNEL); \
+ if (!split_page_alloc) { \
+ ret = -ENOMEM; \
+ goto end; \
+ } \
+ split_page = split_page_alloc; \
while ((token = strsep(&split_page, ",")) != NULL) { \
ret = kstrtou32(token, 0, &num); \
if (ret) \
@@ -1619,7 +1625,7 @@ static ssize_t f_uac1_opts_##name##_store(struct config_item *item, \
}; \
\
end: \
- kfree(split_page); \
+ kfree(split_page_alloc); \
mutex_unlock(&opts->lock); \
return ret; \
} \
diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c
index 897787d08..c92caa149 100644
--- a/drivers/usb/gadget/function/f_uac2.c
+++ b/drivers/usb/gadget/function/f_uac2.c
@@ -2013,6 +2013,7 @@ static ssize_t f_uac2_opts_##name##_store(struct config_item *item, \
{ \
struct f_uac2_opts *opts = to_f_uac2_opts(item); \
char *split_page = NULL; \
+ char *split_page_alloc = NULL; \
int ret = -EINVAL; \
char *token; \
u32 num; \
@@ -2026,7 +2027,12 @@ static ssize_t f_uac2_opts_##name##_store(struct config_item *item, \
\
i = 0; \
memset(opts->name##s, 0x00, sizeof(opts->name##s)); \
- split_page = kstrdup(page, GFP_KERNEL); \
+ split_page_alloc = kstrdup(page, GFP_KERNEL); \
+ if (!split_page_alloc) { \
+ ret = -ENOMEM; \
+ goto end; \
+ } \
+ split_page = split_page_alloc; \
while ((token = strsep(&split_page, ",")) != NULL) { \
ret = kstrtou32(token, 0, &num); \
if (ret) \
@@ -2037,7 +2043,7 @@ static ssize_t f_uac2_opts_##name##_store(struct config_item *item, \
}; \
\
end: \
- kfree(split_page); \
+ kfree(split_page_alloc); \
mutex_unlock(&opts->lock); \
return ret; \
} \
base-commit: 075b74841bd0065a3bda3440873c747938e69b68
--
This is an AI-generated patch subject to moderation.
Reply with '#syz upstream' to Sign-off the patch as a human author
and send it to the upstream kernel mailing lists.
Reply with '#syz reject' to reject it ('#syz unreject' to undo).
See https://goo.gle/syzbot-ai-patches for information about AI-generated patches.
You can comment on the patch as usual, syzbot will try to address
the comments and send a new version of the patch if necessary.
syzbot engineers can be reached at [email protected].