]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
perf/ftrace: Fix WARNING in __unregister_ftrace_function
authorRik van Riel <riel@surriel.com>
Wed, 27 May 2026 15:13:01 +0000 (11:13 -0400)
committerSteven Rostedt <rostedt@goodmis.org>
Fri, 29 May 2026 15:27:40 +0000 (11:27 -0400)
perf_ftrace_function_unregister() unconditionally calls
unregister_ftrace_function() without checking whether the ftrace_ops
was ever successfully registered. This triggers a WARN_ON in
__unregister_ftrace_function() when the ops doesn't have
FTRACE_OPS_FL_ENABLED set.

This can happen during perf_event_alloc() error cleanup when
perf_trace_destroy() is called via __free_event() on an event whose
ftrace_ops registration failed or was already torn down by
perf_try_init_event()'s err_destroy path.

The call path is:
  perf_event_alloc() error cleanup
    -> __free_event()
      -> event->destroy() [tp_perf_event_destroy]
        -> perf_trace_destroy()
          -> perf_trace_event_close()
            -> TRACE_REG_PERF_CLOSE
              -> perf_ftrace_function_unregister()
                -> unregister_ftrace_function()
                  -> __unregister_ftrace_function()
                    -> WARN_ON(!(ops->flags & FTRACE_OPS_FL_ENABLED))

Fix this by checking FTRACE_OPS_FL_ENABLED before attempting to
unregister. If the ops is not enabled, just free the filter and
return success.

Link: https://patch.msgid.link/20260527111301.2d0d8256@fangorn
Signed-off-by: Rik van Riel <riel@surriel.com>
Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
kernel/trace/trace_event_perf.c

index a6bb7577e8c596f4753ae7a05a7598497a037c9e..5b272856e5ab1904d04ddd02d4b2da45df2edc71 100644 (file)
@@ -497,7 +497,17 @@ static int perf_ftrace_function_register(struct perf_event *event)
 static int perf_ftrace_function_unregister(struct perf_event *event)
 {
        struct ftrace_ops *ops = &event->ftrace_ops;
-       int ret = unregister_ftrace_function(ops);
+       int ret = 0;
+
+       /*
+        * Perf will call this unconditionally even if the ops is not
+        * enabled. The unregister_ftrace_function() will warn if called
+        * when not enabled. Just bypass the unregistering if ops isn't
+        * enabled here.
+        */
+       if (ops->flags & FTRACE_OPS_FL_ENABLED)
+               ret = unregister_ftrace_function(ops);
+
        ftrace_free_filter(ops);
        return ret;
 }