[PATCH] usb: gadget: f_uac1, f_uac2: fix invalid free and memory leak

"syzbot" <[email protected]>
Newsgroups org.kernel.vger.linux-usb,dev.linux.lists.syzbot,org.kernel.vger.linux-kernel
Message-ID <[email protected]>
From: Aleksandr Nogikh <[email protected]>

The `UAC1_RATE_ATTRIBUTE` and `UAC2_RATE_ATTRIBUTE` macros generate store
functions that use `strsep()` to parse a comma-separated list of sample
rates. The `strsep()` function modifies the pointer passed to it, advancing
it past the delimiter.

This causes two issues. First, if `kstrtou32()` fails (e.g., due to an
invalid token), the code jumps to the `end` label and calls
`kfree(split_page)`. Since `split_page` has been advanced by `strsep()`,
this results in an invalid free. Second, if the loop completes
successfully, `strsep()` sets `split_page` to `NULL`. The subsequent
`kfree(split_page)` becomes a no-op, leaking the memory allocated by
`kstrdup()`.

KASAN reports the invalid free as follows:

BUG: KASAN: invalid-free in f_uac2_opts_p_srate_store+0x225/0x2b0
drivers/usb/gadget/function/f_uac2.c:2087
Free of addr ffff888192cb7806 by task syz.0.17/6182
Call Trace:
 kfree+0x173/0x6c0 mm/slub.c:6692
 f_uac2_opts_p_srate_store+0x225/0x2b0
 drivers/usb/gadget/function/f_uac2.c:2087
 flush_write_buffer fs/configfs/file.c:207 [inline]
 configfs_write_iter+0x33a/0x430 fs/configfs/file.c:229
 vfs_write+0x61e/0xbb0 fs/read_write.c:687
 ksys_write+0x156/0x270 fs/read_write.c:739
The buggy address is located 6 bytes inside of
 32-byte region [ffff888192cb7800, ffff888192cb7820)

Fix this by keeping the original pointer returned by `kstrdup()` in a
separate variable `page_alloc` and passing a temporary pointer `split_page`
to `strsep()`. This ensures that the correct pointer is passed to
`kfree()`. Additionally, add a check to handle `kstrdup()` allocation
failures.

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=bfca46c96ddf3b7d5c55
Link: https://syzkaller.appspot.com/ai_job?id=705307cf-805c-4e75-8f76-2a0acc541cb8
Signed-off-by: Aleksandr Nogikh <[email protected]>

---
diff --git a/drivers/usb/gadget/function/f_uac1.c b/drivers/usb/gadget/function/f_uac1.c
index 85c502e98..f4b7bbd93 100644
--- a/drivers/usb/gadget/function/f_uac1.c
+++ b/drivers/usb/gadget/function/f_uac1.c
@@ -1594,6 +1594,7 @@ static ssize_t f_uac1_opts_##name##_store(struct config_item *item,	\
 					  const char *page, size_t len)	\
 {									\
 	struct f_uac1_opts *opts = to_f_uac1_opts(item);		\
+	char *page_alloc = NULL;					\
 	char *split_page = NULL;					\
 	int ret = -EINVAL;						\
 	char *token;							\
@@ -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);				\
+	page_alloc = kstrdup(page, GFP_KERNEL);				\
+	if (!page_alloc) {						\
+		ret = -ENOMEM;						\
+		goto end;						\
+	}								\
+	split_page = 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(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..336aeaec9 100644
--- a/drivers/usb/gadget/function/f_uac2.c
+++ b/drivers/usb/gadget/function/f_uac2.c
@@ -2012,6 +2012,7 @@ static ssize_t f_uac2_opts_##name##_store(struct config_item *item,	\
 					  const char *page, size_t len)	\
 {									\
 	struct f_uac2_opts *opts = to_f_uac2_opts(item);		\
+	char *page_alloc = NULL;					\
 	char *split_page = NULL;					\
 	int ret = -EINVAL;						\
 	char *token;							\
@@ -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);				\
+	page_alloc = kstrdup(page, GFP_KERNEL);				\
+	if (!page_alloc) {						\
+		ret = -ENOMEM;						\
+		goto end;						\
+	}								\
+	split_page = 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(page_alloc);						\
 	mutex_unlock(&opts->lock);					\
 	return ret;							\
 }									\


base-commit: f5098b6bae761e346ebcd9da7f95622c04733cff
-- 
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].
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.