On Fri, 23 Oct 2026 10:46:20 -0700, Kyle Zeng <[email protected]> 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) <[email protected]>

Thanks!

> Fixes: 80765597bc587 ("tracing: Rewrite filter logic to be simpler and 
> faster")
> Cc: [email protected]
> Assisted-by: Codex:gpt-6-astra
> Signed-off-by: Kyle Zeng <[email protected]>
> ---
>  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) <[email protected]>

Reply via email to