[PATCH 3/4] tracing/inject: Validate entry allocation size
From: Li Qiang
Date: Wed Jul 22 2026 - 02:16:26 EST
trace_get_entry_size() calculated the allocation from signed field offsets
and sizes without validating their sum. A malformed event could use a
negative range or overflow the calculation, allocate too little memory, and
then write past it while initializing or populating the entry. Events with
no fields also allocated less than a trace_entry.
Start at sizeof(struct trace_entry), validate each field range, and reserve
room for the trailing NUL. Propagate sizing errors to parse_entry() before
it initializes the allocation.
Fixes: 6c3edaf9fd6a ("tracing: Introduce trace event injection")
Cc: stable@xxxxxxxxxxxxxxx
Signed-off-by: Li Qiang <liqiang01@xxxxxxxxxx>
---
kernel/trace/trace_events_inject.c | 31 ++++++++++++++++++++++++------
1 file changed, 25 insertions(+), 6 deletions(-)
diff --git a/kernel/trace/trace_events_inject.c b/kernel/trace/trace_events_inject.c
index a8f076809db4..b8b141c00d5c 100644
--- a/kernel/trace/trace_events_inject.c
+++ b/kernel/trace/trace_events_inject.c
@@ -135,27 +135,43 @@ parse_field(char *str, struct trace_event_call *call,
return -EINVAL;
}
-static int trace_get_entry_size(struct trace_event_call *call)
+static int trace_get_entry_size(struct trace_event_call *call, int *entry_size)
{
struct ftrace_event_field *field;
struct list_head *head;
- int size = 0;
+ int field_size;
+ int size = sizeof(struct trace_entry);
head = trace_get_fields(call);
list_for_each_entry(field, head, link) {
- if (field->size + field->offset > size)
- size = field->size + field->offset;
+ if (field->offset < 0 || field->size < 0 ||
+ field->size > INT_MAX - field->offset)
+ return -E2BIG;
+
+ field_size = field->size + field->offset;
+ if (field_size > size)
+ size = field_size;
}
- return size;
+ /* trace_alloc_entry() reserves an extra NUL byte. */
+ if (size == INT_MAX)
+ return -E2BIG;
+
+ *entry_size = size;
+ return 0;
}
static void *trace_alloc_entry(struct trace_event_call *call, int *size)
{
- int entry_size = trace_get_entry_size(call);
+ int entry_size;
struct ftrace_event_field *field;
struct list_head *head;
void *entry = NULL;
+ int ret;
+
+ ret = trace_get_entry_size(call, &entry_size);
+ if (ret)
+ return ERR_PTR(ret);
/* We need an extra '\0' at the end. */
entry = kzalloc(entry_size + 1, GFP_KERNEL);
@@ -202,6 +218,9 @@ static int parse_entry(char *str, struct trace_event_call *call, void **pentry)
int len;
entry = trace_alloc_entry(call, &entry_size);
+ if (IS_ERR(entry))
+ return PTR_ERR(entry);
+
*pentry = entry;
if (!entry)
return -ENOMEM;
--
2.43.0