kernel/trace: Fix cleanup logic of enable_trace_eprobe
authorTzvetomir Stoyanov (VMware) <tz.stoyanov@gmail.com>
Mon, 3 Jul 2023 04:28:53 +0000 (07:28 +0300)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sun, 23 Jul 2023 11:49:24 +0000 (13:49 +0200)
[ Upstream commit cf0a624dc706c306294c14e6b3e7694702f25191 ]

The enable_trace_eprobe() function enables all event probes, attached
to given trace probe. If an error occurs in enabling one of the event
probes, all others should be roll backed. There is a bug in that roll
back logic - instead of all event probes, only the failed one is
disabled.

Link: https://lore.kernel.org/all/20230703042853.1427493-1-tz.stoyanov@gmail.com/
Reported-by: Dan Carpenter <dan.carpenter@linaro.org>
Fixes: 7491e2c44278 ("tracing: Add a probe that attaches to trace events")
Signed-off-by: Tzvetomir Stoyanov (VMware) <tz.stoyanov@gmail.com>
Acked-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
Reviewed-by: Steven Rostedt (Google) <rostedt@goodmis.org>
Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
kernel/trace/trace_eprobe.c

index 753fc536525d38ac65efec01a49d45e751598a32..d2370cdb4c1d644b4f427c9f35dce178f5d4a353 100644 (file)
@@ -743,6 +743,7 @@ static int enable_trace_eprobe(struct trace_event_call *call,
        struct trace_eprobe *ep;
        bool enabled;
        int ret = 0;
+       int cnt = 0;
 
        tp = trace_probe_primary_from_call(call);
        if (WARN_ON_ONCE(!tp))
@@ -766,12 +767,25 @@ static int enable_trace_eprobe(struct trace_event_call *call,
                if (ret)
                        break;
                enabled = true;
+               cnt++;
        }
 
        if (ret) {
                /* Failed to enable one of them. Roll back all */
-               if (enabled)
-                       disable_eprobe(ep, file->tr);
+               if (enabled) {
+                       /*
+                        * It's a bug if one failed for something other than memory
+                        * not being available but another eprobe succeeded.
+                        */
+                       WARN_ON_ONCE(ret != -ENOMEM);
+
+                       list_for_each_entry(pos, trace_probe_probe_list(tp), list) {
+                               ep = container_of(pos, struct trace_eprobe, tp);
+                               disable_eprobe(ep, file->tr);
+                               if (!--cnt)
+                                       break;
+                       }
+               }
                if (file)
                        trace_probe_remove_file(tp, file);
                else