Re: [PATCH] tracing: Reject incomplete event filter expressions

From: Google

Date: Fri Oct 23 2026 - 19:06:10 EST


On Fri, 23 Oct 2026 10:46:20 -0700, Kyle Zeng <kylebot@xxxxxxxxxx> wrote:
> A non-empty expression normally finishes in predicate_parse()'s inner
> loop, which resolves the pending logical operators before jumping to
> finalization. If the input ends after an operator, possibly followed by
> unary '!' tokens, the outer loop instead falls through with unresolved
> operator state.
>
> For example, "common_pid == 1 || common_pid == 2 &&" leaves
> prog[0].target equal to -1. The second pass then reads prog[-1] before
> the forward-jump check can reject it. Depending on adjacent slab
> contents, it can also copy an out-of-bounds word into a branch target
> that passes the forward-only check. A self-targeted perf tracepoint
> counting event can reach this path through PERF_EVENT_IOC_SET_FILTER.
>
> Reject the outer-loop fallthrough when a predicate has already been
> parsed. This ensures that non-empty expressions only reach finalization
> after the inner loop has resolved their operators. Keep the existing
> no-filter error for input that has no predicates. Add a regression test
> for trailing logical operators and unary negations.
>

Looks good to me.

Acked-by: Masami Hiramatsu (Google) <mhiramat@xxxxxxxxxx>

Thanks!

> Fixes: 80765597bc587 ("tracing: Rewrite filter logic to be simpler and faster")
> Cc: stable@xxxxxxxxxxxxxxx
> Assisted-by: Codex:gpt-6-astra
> Signed-off-by: Kyle Zeng <kylebot@xxxxxxxxxx>
> ---
> kernel/trace/trace_events_filter.c | 6 +++++
> .../test.d/filter/event-filter-incomplete.tc | 24 +++++++++++++++++++
> 2 files changed, 30 insertions(+)
> create mode 100644 tools/testing/selftests/ftrace/test.d/filter/event-filter-incomplete.tc
>
> diff --git a/kernel/trace/trace_events_filter.c b/kernel/trace/trace_events_filter.c
> index 2b46ca536045..405651be9c93 100644
> --- a/kernel/trace/trace_events_filter.c
> +++ b/kernel/trace/trace_events_filter.c
> @@ -596,6 +596,12 @@ predicate_parse(const char *str, int nr_parens, int nr_preds,
> top--; /* #12 */
> }
> }
> + /* Only the inner loop can complete a non-empty expression. */
> + if (N) {
> + ret = -EINVAL;
> + parse_error(pe, FILT_ERR_INVALID_FILTER, ptr - str);
> + goto out_free;
> + }
> out:
> if (top != op_stack) {
> /* Too many '(' */
> diff --git a/tools/testing/selftests/ftrace/test.d/filter/event-filter-incomplete.tc b/tools/testing/selftests/ftrace/test.d/filter/event-filter-incomplete.tc
> new file mode 100644
> index 000000000000..cc6df762845f
> --- /dev/null
> +++ b/tools/testing/selftests/ftrace/test.d/filter/event-filter-incomplete.tc
> @@ -0,0 +1,24 @@
> +#!/bin/sh
> +# SPDX-License-Identifier: GPL-2.0
> +# description: event filter - reject incomplete logical expressions
> +# requires: events/sched/sched_switch/filter
> +# flags: instance
> +
> +filter=events/sched/sched_switch/filter
> +
> +for base in \
> + 'common_pid == 1' \
> + 'common_pid == 1 || common_pid == 2' \
> + 'common_pid == 1 && common_pid == 2' \
> + '!(common_pid == 1 || common_pid == 2)'
> +do
> + printf '%s' "$base" > "$filter"
> + for tail in '&&' '||' '&& !' '|| !' '&& !! ' '|| !! '; do
> + if printf '%s' "$base $tail" > "$filter" 2>/dev/null; then
> + echo "Accepted incomplete filter: $base $tail"
> + exit_fail
> + fi
> + done
> +done
> +
> +echo 0 > "$filter"
>
> base-commit: fd179f8a05be3ccae366b9b96e176b51fbe54aab
>

--
Masami Hiramatsu (Google) <mhiramat@xxxxxxxxxx>