From: Masami Hiramatsu (Google) <[email protected]>

Prior to commit 80765597bc58 ("tracing: Rewrite filter logic to be simpler
and faster"), parse_pred() checked whether select_comparison_fn() returned
NULL and failed with FILT_ERR_INVALID_OP and -EINVAL. That check was lost
during the rewrite, and commit 3cbec9d7b93d ("tracing/filters: Enable
filtering a scalar field by a cpumask") similarly omitted a default error
case when matching field->size for CPUS{} comparisons.

If a non-string field (FILTER_OTHER) has a size other than 1, 2, 4, or 8
(such as a fixed-size integer array field), pred->fn_num remains
FILTER_PRED_FN_NOP (0) and parse_pred() returns success. Subsequently,
update_preds() rejects FILTER_PRED_FN_NOP via WARN_ON_ONCE() and returns
-EINVAL without setting a parse_error() message, triggering a kernel
warning from user-supplied filter input.

Restore the check for FILTER_PRED_FN_NOP in both the numeric scalar and
CPUS{} branches of parse_pred() and report FILT_ERR_INVALID_OP.

Fixes: 80765597bc58 ("tracing: Rewrite filter logic to be simpler and faster")
Fixes: 3cbec9d7b93d ("tracing/filters: Enable filtering a scalar field by a 
cpumask")
Cc: [email protected]
Signed-off-by: Masami Hiramatsu (Google) <[email protected]>
---
 kernel/trace/trace_events_filter.c |    8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/kernel/trace/trace_events_filter.c 
b/kernel/trace/trace_events_filter.c
index a645af9eb1f8..40efcef94e02 100644
--- a/kernel/trace/trace_events_filter.c
+++ b/kernel/trace/trace_events_filter.c
@@ -2013,6 +2013,10 @@ static int parse_pred(const char *str, void *data,
                                break;
                        }
                }
+               if (pred->fn_num == FILTER_PRED_FN_NOP) {
+                       parse_error(pe, FILT_ERR_INVALID_OP, pos + i);
+                       goto err_free;
+               }
 
        /* This is either a string, or an integer */
        } else if (str[i] == '\'' || str[i] == '"') {
@@ -2166,6 +2170,10 @@ static int parse_pred(const char *str, void *data,
                else {
                        pred->fn_num = select_comparison_fn(pred->op, 
field->size,
                                                            field->is_signed);
+                       if (pred->fn_num == FILTER_PRED_FN_NOP) {
+                               parse_error(pe, FILT_ERR_INVALID_OP, pos + i);
+                               goto err_free;
+                       }
                        if (pred->op == OP_NE)
                                pred->not = 1;
                }


Reply via email to