user_event_parse_field() accepts large structure sizes but accumulates
field offsets in a u32 before storing them in signed int fields. A
declaration can wrap the recorded minimum event size while a fixed-width
field retains an offset outside the accepted record. A short write is
then accepted and a field filter reads beyond the event buffer.
KASAN reports:
BUG: KASAN: slab-use-after-free in filter_match_preds
Read of size 8
filter_match_preds
trace_event_buffer_commit
user_event_ftrace
user_events_write_core
Parse an explicit structure size into a u32, reject values greater than
INT_MAX before converting them to int, and use checked addition for the
resulting field offset. This prevents both signed conversion and offset
overflow from bypassing the downstream bounds checks.
Fixes: 7f5a08c79df3 ("user_events: Add minimal support for trace_event into
ftrace")
Signed-off-by: Yifan Zhang <[email protected]>
---
kernel/trace/trace_events_user.c | 12 ++++++++++--
1 file changed, 10 insertions(+), 2 deletions(-)
diff --git a/kernel/trace/trace_events_user.c b/kernel/trace/trace_events_user.c
index 85938b45b7618..04eef4865490b 100644
--- a/kernel/trace/trace_events_user.c
+++ b/kernel/trace/trace_events_user.c
@@ -1207,6 +1207,7 @@ static int user_event_parse_field(char *field, struct
user_event *user,
{
char *part, *type, *name;
u32 depth = 0, saved_offset = *offset;
+ u32 new_offset, struct_size;
int len, size = -EINVAL;
bool is_struct = false;
@@ -1267,8 +1268,11 @@ static int user_event_parse_field(char *field, struct
user_event *user,
if (!is_struct)
return -EINVAL;
- if (kstrtou32(part, 10, &size))
+ if (kstrtou32(part, 10, &struct_size))
return -EINVAL;
+ if (struct_size > INT_MAX)
+ return -E2BIG;
+ size = struct_size;
break;
default:
return -EINVAL;
@@ -1287,7 +1291,11 @@ static int user_event_parse_field(char *field, struct
user_event *user,
if (size < 0)
return size;
- *offset = saved_offset + size;
+ if (check_add_overflow(saved_offset, (u32)size, &new_offset) ||
+ new_offset > INT_MAX)
+ return -E2BIG;
+
+ *offset = new_offset;
return user_event_add_field(user, type, name, saved_offset, size,
type[0] != 'u', FILTER_OTHER);
--
2.39.5 (Apple Git-154)