134 lines
3.7 KiB
Diff
134 lines
3.7 KiB
Diff
From 04b512d5cc10cec9e13eb434ea8d16d899244e6e Mon Sep 17 00:00:00 2001
|
|
From: Pu Lehui <pulehui@huawei.com>
|
|
Date: Wed, 13 Aug 2025 04:02:32 +0000
|
|
Subject: tracing: Limit access to parser->buffer when trace_get_user failed
|
|
|
|
When the length of the string written to set_ftrace_filter exceeds
|
|
FTRACE_BUFF_MAX, the following KASAN alarm will be triggered:
|
|
|
|
BUG: KASAN: slab-out-of-bounds in strsep+0x18c/0x1b0
|
|
Read of size 1 at addr ffff0000d00bd5ba by task ash/165
|
|
|
|
CPU: 1 UID: 0 PID: 165 Comm: ash Not tainted 6.16.0-g6bcdbd62bd56-dirty
|
|
Hardware name: linux,dummy-virt (DT)
|
|
Call trace:
|
|
show_stack+0x34/0x50 (C)
|
|
dump_stack_lvl+0xa0/0x158
|
|
print_address_description.constprop.0+0x88/0x398
|
|
print_report+0xb0/0x280
|
|
kasan_report+0xa4/0xf0
|
|
__asan_report_load1_noabort+0x20/0x30
|
|
strsep+0x18c/0x1b0
|
|
ftrace_process_regex.isra.0+0x100/0x2d8
|
|
ftrace_regex_release+0x484/0x618
|
|
__fput+0x364/0xa58
|
|
____fput+0x28/0x40
|
|
task_work_run+0x154/0x278
|
|
do_notify_resume+0x1f0/0x220
|
|
el0_svc+0xec/0xf0
|
|
el0t_64_sync_handler+0xa0/0xe8
|
|
el0t_64_sync+0x1ac/0x1b0
|
|
|
|
The reason is that trace_get_user will fail when processing a string
|
|
longer than FTRACE_BUFF_MAX, but not set the end of parser->buffer to 0.
|
|
Then an OOB access will be triggered in ftrace_regex_release->
|
|
ftrace_process_regex->strsep->strpbrk. We can solve this problem by
|
|
limiting access to parser->buffer when trace_get_user failed.
|
|
|
|
Cc: stable@vger.kernel.org
|
|
Link: https://lore.kernel.org/20250813040232.1344527-1-pulehui@huaweicloud.com
|
|
Fixes: 8c9af478c06b ("ftrace: Handle commands when closing set_ftrace_filter file")
|
|
Signed-off-by: Pu Lehui <pulehui@huawei.com>
|
|
Signed-off-by: Steven Rostedt (Google) <rostedt@goodmis.org>
|
|
---
|
|
kernel/trace/trace.c | 18 ++++++++++++------
|
|
kernel/trace/trace.h | 8 +++++++-
|
|
2 files changed, 19 insertions(+), 7 deletions(-)
|
|
|
|
--- a/kernel/trace/trace.c
|
|
+++ b/kernel/trace/trace.c
|
|
@@ -1846,7 +1846,7 @@ int trace_get_user(struct trace_parser *
|
|
|
|
ret = get_user(ch, ubuf++);
|
|
if (ret)
|
|
- return ret;
|
|
+ goto fail;
|
|
|
|
read++;
|
|
cnt--;
|
|
@@ -1860,7 +1860,7 @@ int trace_get_user(struct trace_parser *
|
|
while (cnt && isspace(ch)) {
|
|
ret = get_user(ch, ubuf++);
|
|
if (ret)
|
|
- return ret;
|
|
+ goto fail;
|
|
read++;
|
|
cnt--;
|
|
}
|
|
@@ -1878,12 +1878,14 @@ int trace_get_user(struct trace_parser *
|
|
while (cnt && !isspace(ch) && ch) {
|
|
if (parser->idx < parser->size - 1)
|
|
parser->buffer[parser->idx++] = ch;
|
|
- else
|
|
- return -EINVAL;
|
|
+ else {
|
|
+ ret = -EINVAL;
|
|
+ goto fail;
|
|
+ }
|
|
|
|
ret = get_user(ch, ubuf++);
|
|
if (ret)
|
|
- return ret;
|
|
+ goto fail;
|
|
read++;
|
|
cnt--;
|
|
}
|
|
@@ -1898,11 +1900,15 @@ int trace_get_user(struct trace_parser *
|
|
/* Make sure the parsed string always terminates with '\0'. */
|
|
parser->buffer[parser->idx] = 0;
|
|
} else {
|
|
- return -EINVAL;
|
|
+ ret = -EINVAL;
|
|
+ goto fail;
|
|
}
|
|
|
|
*ppos += read;
|
|
return read;
|
|
+fail:
|
|
+ trace_parser_fail(parser);
|
|
+ return ret;
|
|
}
|
|
|
|
/* TODO add a seq_buf_to_buffer() */
|
|
--- a/kernel/trace/trace.h
|
|
+++ b/kernel/trace/trace.h
|
|
@@ -1292,6 +1292,7 @@ bool ftrace_event_is_function(struct tra
|
|
*/
|
|
struct trace_parser {
|
|
bool cont;
|
|
+ bool fail;
|
|
char *buffer;
|
|
unsigned idx;
|
|
unsigned size;
|
|
@@ -1299,7 +1300,7 @@ struct trace_parser {
|
|
|
|
static inline bool trace_parser_loaded(struct trace_parser *parser)
|
|
{
|
|
- return (parser->idx != 0);
|
|
+ return !parser->fail && parser->idx != 0;
|
|
}
|
|
|
|
static inline bool trace_parser_cont(struct trace_parser *parser)
|
|
@@ -1313,6 +1314,11 @@ static inline void trace_parser_clear(st
|
|
parser->idx = 0;
|
|
}
|
|
|
|
+static inline void trace_parser_fail(struct trace_parser *parser)
|
|
+{
|
|
+ parser->fail = true;
|
|
+}
|
|
+
|
|
extern int trace_parser_get_init(struct trace_parser *parser, int size);
|
|
extern void trace_parser_put(struct trace_parser *parser);
|
|
extern int trace_get_user(struct trace_parser *parser, const char __user *ubuf,
|