]> git.hungrycats.org Git - linux/commitdiff
tracing: Free histogram var refs regardless of how often they are referenced
authorDonggeun Yoo <donggeunyoo.kernel@gmail.com>
Sun, 6 Sep 2026 12:40:25 +0000 (21:40 +0900)
committerSteven Rostedt <rostedt@goodmis.org>
Fri, 11 Sep 2026 17:46:03 +0000 (13:46 -0400)
Using the same variable three or more times in one hist trigger leaks the
variable reference and its strings when the trigger is removed.

commit 656fe2ba85e8 ("tracing: Use hist trigger's var_ref array to destroy
var_refs") made a trigger's var_refs[] array the only owner of a var ref:
destroy_hist_field() returns early for HIST_FIELD_FL_VAR_REF, so the field
expressions never destroy one. One entry, freed once, no count needed.

commit 8bcebc77e85f ("tracing: Fix histogram code when expression has same
var as value") then made repeated references share one object and added a
count of them. Only the increment side exists, since those expressions
still return early and never drop a reference, so __destroy_hist_field()
sees how many references were created rather than how many are left. It
frees when the decremented count is 0 or 1, so two references work and
three or more leak.

Sharing kept one array entry per object, and create_var_ref() searches and
appends within a single trigger, so nothing outside it holds the object.
Removing a trigger whose variables are still referenced is already refused
by check_var_refs() with -EBUSY. Drop the count and free unconditionally.

Cc: stable@vger.kernel.org
Link: https://patch.msgid.link/20260906124025.3550596-1-donggeunyoo.kernel@gmail.com
Fixes: 8bcebc77e85f ("tracing: Fix histogram code when expression has same var as value")
Signed-off-by: Donggeun Yoo <donggeunyoo.kernel@gmail.com>
Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
kernel/trace/trace_events_hist.c

index 963e0d6b61fdfedfe0919e7b9d147af9bb0cd6dc..f90680b33a373686272fc34ef0713787a5ea8bab 100644 (file)
@@ -169,7 +169,6 @@ struct hist_field {
        struct hist_field               *operands[HIST_FIELD_OPERANDS_MAX];
        struct hist_trigger_data        *hist_data;
        enum hist_field_fn              fn_num;
-       unsigned int                    ref;
        unsigned int                    size;
        unsigned int                    offset;
        unsigned int                    is_signed;
@@ -1913,16 +1912,8 @@ out:
        return field_op;
 }
 
-static void get_hist_field(struct hist_field *hist_field)
-{
-       hist_field->ref++;
-}
-
 static void __destroy_hist_field(struct hist_field *hist_field)
 {
-       if (--hist_field->ref > 1)
-               return;
-
        kfree(hist_field->var.name);
        kfree(hist_field->name);
 
@@ -1969,8 +1960,6 @@ static struct hist_field *create_hist_field(struct hist_trigger_data *hist_data,
        if (!hist_field)
                return NULL;
 
-       hist_field->ref = 1;
-
        hist_field->hist_data = hist_data;
 
        if (flags & HIST_FIELD_FL_EXPR || flags & HIST_FIELD_FL_ALIAS)
@@ -2223,10 +2212,8 @@ static struct hist_field *create_var_ref(struct hist_trigger_data *hist_data,
        for (i = 0; i < hist_data->n_var_refs; i++) {
                ref_field = hist_data->var_refs[i];
                if (ref_field->var.idx == var_field->var.idx &&
-                   ref_field->var.hist_data == var_field->hist_data) {
-                       get_hist_field(ref_field);
+                   ref_field->var.hist_data == var_field->hist_data)
                        return ref_field;
-               }
        }
        /* Sanity check to avoid out-of-bound write on 'hist_data->var_refs' */
        if (hist_data->n_var_refs >= TRACING_MAP_VARS_MAX)
@@ -3276,7 +3263,6 @@ static struct hist_field *create_var(struct hist_trigger_data *hist_data,
                goto out;
        }
 
-       var->ref = 1;
        var->flags = HIST_FIELD_FL_VAR;
        var->var.idx = idx;
        var->var.hist_data = var->hist_data = hist_data;