* [for-linus][PATCH 1/3] tracing: Fix crash in print_graph_function_event()
2026-10-16 13:32 [for-linus][PATCH 0/3] tracing: More fixes for v7.3 Steven Rostedt
@ 2026-10-16 13:32 ` Steven Rostedt
2026-10-16 13:32 ` [for-linus][PATCH 2/3] tracing: Keep the predicate limit separate from program capacity Steven Rostedt
2026-10-16 13:32 ` [for-linus][PATCH 3/3] tracing: Check operator stack capacity before pushing Steven Rostedt
2 siblings, 0 replies; 4+ messages in thread
From: Steven Rostedt @ 2026-10-16 13:32 UTC (permalink / raw)
To: linux-kernel
Cc: Masami Hiramatsu, Mark Rutland, Mathieu Desnoyers, Andrew Morton,
stable, Hao Ge
From: Hao Ge <hao.ge@linux.dev>
tracing_set_tracer() assigns the flags of the newly selected tracer
with:
tr->current_trace_flags = t->flags ? : t->tracer->flags;
If the new tracer defines neither static flags nor default flags, like
hwlat, irqsoff or wakeup, add_tracer() has no per instance flags copy
for it, and both operands are NULL, so tr->current_trace_flags ends up
being NULL as well.
The ring buffer is not cleared when switching tracers: it is reset
inside tracer_init(), after current_trace_flags was already assigned.
If the buffer still holds function_graph entries of the previous
tracer, printing one of them dereferences the NULL pointer. This was
hit by cat trace during an ftrace stress test:
BUG: kernel NULL pointer dereference, address: 0000000000000000
#PF: supervisor read access in kernel mode
RIP: 0010:print_graph_function_event+0xe/0x20
Call Trace:
s_show+0x4d/0x110
seq_read_iter+0x2d4/0x490
seq_read+0x130/0x170
vfs_read+0xb5/0x370
ksys_read+0x6d/0xe0
do_syscall_64+0xbc/0x450
The snapshot buffer is not reset at all, so this can be reproduced
without a race:
echo function_graph > /sys/kernel/tracing/current_tracer
echo 1 > /sys/kernel/tracing/snapshot
echo hwlat > /sys/kernel/tracing/current_tracer
cat /sys/kernel/tracing/snapshot
Rather than teaching every user of tr->current_trace_flags about this
corner case, make sure it is never NULL: when the newly selected
tracer has no flags at all, tracing_set_tracer() falls back to a
static empty no_tracer_flags. run_tracer_selftest() can hit the same
case, so the same fallback is added there too. The existing users
already handle opts == NULL, so these tracers keep behaving as before.
Cc: stable@vger.kernel.org
Link: https://patch.msgid.link/20261015130658.221198-1-hao.ge@linux.dev
Fixes: e29aa918a928 ("tracing: Have function graph tracer define options per instance")
Suggested-by: Steven Rostedt <rostedt@goodmis.org>
Signed-off-by: Hao Ge <hao.ge@linux.dev>
Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
---
kernel/trace/trace.c | 20 ++++++++++++++++++--
1 file changed, 18 insertions(+), 2 deletions(-)
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index 3b22e70c6016..97a058623f94 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -99,6 +99,12 @@ struct tracers {
struct tracer_flags *flags;
};
+/*
+ * Tracers that do not define their own flags use these empty flags,
+ * as tr->current_trace_flags must never be NULL.
+ */
+static struct tracer_flags no_tracer_flags;
+
/*
* To prevent the comm cache from being overwritten when no
* tracing is active, only save the comm when a trace event
@@ -1318,7 +1324,12 @@ static int run_tracer_selftest(struct tracer *type)
tracing_reset_online_cpus(&tr->array_buffer);
tr->current_trace = type;
- tr->current_trace_flags = type->flags ? : type->default_flags;
+ if (type->flags)
+ tr->current_trace_flags = type->flags;
+ else if (type->default_flags)
+ tr->current_trace_flags = type->default_flags;
+ else
+ tr->current_trace_flags = &no_tracer_flags;
#ifdef CONFIG_TRACER_MAX_TRACE
if (tracer_uses_snapshot(type)) {
@@ -5089,7 +5100,12 @@ int tracing_set_tracer(struct trace_array *tr, const char *buf)
return ret;
}
- tr->current_trace_flags = t->flags ? : t->tracer->flags;
+ if (t->flags)
+ tr->current_trace_flags = t->flags;
+ else if (t->tracer->flags)
+ tr->current_trace_flags = t->tracer->flags;
+ else
+ tr->current_trace_flags = &no_tracer_flags;
if (trace->init) {
ret = tracer_init(trace, tr);
--
2.53.0
^ permalink raw reply [flat|nested] 4+ messages in thread* [for-linus][PATCH 2/3] tracing: Keep the predicate limit separate from program capacity
2026-10-16 13:32 [for-linus][PATCH 0/3] tracing: More fixes for v7.3 Steven Rostedt
2026-10-16 13:32 ` [for-linus][PATCH 1/3] tracing: Fix crash in print_graph_function_event() Steven Rostedt
@ 2026-10-16 13:32 ` Steven Rostedt
2026-10-16 13:32 ` [for-linus][PATCH 3/3] tracing: Check operator stack capacity before pushing Steven Rostedt
2 siblings, 0 replies; 4+ messages in thread
From: Steven Rostedt @ 2026-10-16 13:32 UTC (permalink / raw)
To: linux-kernel
Cc: Masami Hiramatsu, Mark Rutland, Mathieu Desnoyers, Andrew Morton,
stable, Taeyang Lee
From: Taeyang Lee <0wn@theori.io>
predicate_parse() adds two to nr_preds to reserve the terminal TRUE
and FALSE entries. It also uses that increased value as the limit for
parsed predicates. If the initial count is smaller than the number
actually parsed, the program can fill the allocation and subsequent
accesses can go past its end.
Keep nr_preds as the predicate limit and use a separate value for the
allocation size. This leaves space for both terminal entries and a
zeroed entry when parsing stops at the limit.
Cc: stable@vger.kernel.org
Fixes: 39f7c41c908b ("tracing/filters: Enable filtering a cpumask field by another cpumask")
Link: https://patch.msgid.link/atELCd1XjKVhrkFL@Taeyangs-MacBook-Pro.local
Signed-off-by: Taeyang Lee <0wn@theori.io>
Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
---
kernel/trace/trace_events_filter.c | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/kernel/trace/trace_events_filter.c b/kernel/trace/trace_events_filter.c
index 2b46ca536045..781416ba6bdf 100644
--- a/kernel/trace/trace_events_filter.c
+++ b/kernel/trace/trace_events_filter.c
@@ -482,18 +482,17 @@ predicate_parse(const char *str, int nr_parens, int nr_preds,
int len;
int N = 0;
int i;
-
- nr_preds += 2; /* For TRUE and FALSE */
+ int nr_preds_alloc = nr_preds + 2; /* For TRUE and FALSE */
op_stack = kmalloc_objs(*op_stack, nr_parens);
if (!op_stack)
return ERR_PTR(-ENOMEM);
- prog_stack = kzalloc_objs(*prog_stack, nr_preds);
+ prog_stack = kzalloc_objs(*prog_stack, nr_preds_alloc);
if (!prog_stack) {
parse_error(pe, -ENOMEM, 0);
goto out_free;
}
- inverts = kmalloc_array(nr_preds, sizeof(*inverts), GFP_KERNEL);
+ inverts = kmalloc_array(nr_preds_alloc, sizeof(*inverts), GFP_KERNEL);
if (!inverts) {
parse_error(pe, -ENOMEM, 0);
goto out_free;
--
2.53.0
^ permalink raw reply [flat|nested] 4+ messages in thread* [for-linus][PATCH 3/3] tracing: Check operator stack capacity before pushing
2026-10-16 13:32 [for-linus][PATCH 0/3] tracing: More fixes for v7.3 Steven Rostedt
2026-10-16 13:32 ` [for-linus][PATCH 1/3] tracing: Fix crash in print_graph_function_event() Steven Rostedt
2026-10-16 13:32 ` [for-linus][PATCH 2/3] tracing: Keep the predicate limit separate from program capacity Steven Rostedt
@ 2026-10-16 13:32 ` Steven Rostedt
2 siblings, 0 replies; 4+ messages in thread
From: Steven Rostedt @ 2026-10-16 13:32 UTC (permalink / raw)
To: linux-kernel
Cc: Masami Hiramatsu, Mark Rutland, Mathieu Desnoyers, Andrew Morton,
stable, Taeyang Lee
From: Taeyang Lee <0wn@theori.io>
predicate_parse() uses the first operator stack entry before processing
any opening parentheses. A push then increments top before writing.
The existing check still permits that write when top already points to
the last allocated entry, which can access beyond the operator stack.
Reject the push when top reaches the last allocated entry.
Cc: stable@vger.kernel.org
Fixes: 39f7c41c908b ("tracing/filters: Enable filtering a cpumask field by another cpumask")
Link: https://patch.msgid.link/atELO9L0I9kg5CpC@Taeyangs-MacBook-Pro.local
Signed-off-by: Taeyang Lee <0wn@theori.io>
Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
---
kernel/trace/trace_events_filter.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/kernel/trace/trace_events_filter.c b/kernel/trace/trace_events_filter.c
index 781416ba6bdf..127880ebe83f 100644
--- a/kernel/trace/trace_events_filter.c
+++ b/kernel/trace/trace_events_filter.c
@@ -511,7 +511,7 @@ predicate_parse(const char *str, int nr_parens, int nr_preds,
switch (*next) {
case '(': /* #2 */
- if (top - op_stack > nr_parens) {
+ if (top - op_stack >= nr_parens - 1) {
ret = -EINVAL;
goto out_free;
}
--
2.53.0
^ permalink raw reply [flat|nested] 4+ messages in thread