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: syzbot+87c10526d2cfa8d14ff6@syzkaller.appspotmail.com 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" To: To: "Julian Scheel" Cc: "Christophe JAILLET" Cc: "Kees Cook" Cc: --- 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 syzkaller@googlegroups.com.