tracing: probes: Fix a possible race in trace_probe_log APIs
Masami Hiramatsu (Google)
maintainerPATCH 1/1 tracing: probes: Fix a possible race in trace_probe_log APIs
From: Masami Hiramatsu (Google) <[email protected]>
Since the shared trace_probe_log variable can be accessed and modified via probe event create operation of kprobe_events, uprobe_events, and dynamic_events, it should be protected. In the dynamic_events, all operations are serialized by dyn_event_ops_mutex. But kprobe_events and uprobe_events interfaces are not serialized.
To solve this issue, introduces dyn_event_create(), which runs create() operation under the mutex, for kprobe_events and uprobe_events. This also uses lockdep to check the mutex is held when using trace_probe_log* APIs.
--- kernel/trace/trace_dynevent.c | 16 +++++++++++++++- kernel/trace/trace_dynevent.h | 1 + kernel/trace/trace_kprobe.c | 2 +- kernel/trace/trace_probe.c | 9 +++++++++ kernel/trace/trace_uprobe.c | 2 +- 5 files changed, 27 insertions(+), 3 deletions(-)
kernel/trace/trace_dynevent.c +15 -1
@@ -16,7 +16,7 @@#include "trace_output.h" /* for trace_event_sem */ #include "trace_dynevent.h" -static DEFINE_MUTEX (dyn_event_ops_mutex); +DEFINE_MUTEX (dyn_event_ops_mutex);static LIST_HEAD (dyn_event_ops_list);bool trace_event_dyn_try_get_ref (struct trace_event_call *dyn_call)
@@ -116,6 +116,20 @@ int dyn_event_release(const char *raw_command, struct dyn_event_operations *typereturn ret; } +/* +* Locked version of event creation. The event creation must be protected by +* dyn_event_ops_mutex because of protecting trace_probe_log. +*/ +int dyn_event_create (const char *raw_command,struct dyn_event_operations *type) +{ +int ret; + +mutex_lock (&dyn_event_ops_mutex); + ret = type->create (raw_command); +mutex_unlock (&dyn_event_ops_mutex); +return ret; +} +static int create_dyn_event (const char *raw_command) {struct dyn_event_operations *ops;
kernel/trace/trace_dynevent.h +1 -0
@@ -100,6 +100,7 @@ void *dyn_event_seq_next(struct seq_file *m, void *v, loff_t *pos);void dyn_event_seq_stop (struct seq_file *m,void *v);int dyn_events_release_all (struct dyn_event_operations *type);int dyn_event_release (const char *raw_command,struct dyn_event_operations *type); +int dyn_event_create (const char *raw_command,struct dyn_event_operations *type);/* * for_each_dyn_event - iterate over the dyn_event list
kernel/trace/trace_kprobe.c +1 -1
@@ -1089,7 +1089,7 @@ static int create_or_delete_trace_kprobe(const char *raw_command)if (raw_command[0 ] =='-' )return dyn_event_release (raw_command, &trace_kprobe_ops); - ret =trace_kprobe_create (raw_command); + ret =dyn_event_create (raw_command, &trace_kprobe_ops);return ret == -ECANCELED ? -EINVAL : ret; }
kernel/trace/trace_probe.c +9 -0
@@ -154,9 +154,12 @@ static const struct fetch_type *find_fetch_type(const char *type, unsigned long }static struct trace_probe_log trace_probe_log; +extern struct mutex dyn_event_ops_mutex;void trace_probe_log_init (const char *subsystem,int argc,const char **argv) { +lockdep_assert_held (&dyn_event_ops_mutex); + trace_probe_log.subsystem = subsystem; trace_probe_log.argc = argc; trace_probe_log.argv = argv;
@@ -165,11 +168,15 @@ void trace_probe_log_init(const char *subsystem, int argc, const char **argv)void trace_probe_log_clear (void ) { +lockdep_assert_held (&dyn_event_ops_mutex); +memset (&trace_probe_log,0 ,sizeof (trace_probe_log)); }void trace_probe_log_set_index (int index) { +lockdep_assert_held (&dyn_event_ops_mutex); + trace_probe_log.index = index; }
@@ -178,6 +185,8 @@ void __trace_probe_log_err(int offset, int err_type)char *command, *p;int i, len =0 , pos =0 ; +lockdep_assert_held (&dyn_event_ops_mutex); +if (!trace_probe_log.argv)return ;
kernel/trace/trace_uprobe.c +1 -1
@@ -741,7 +741,7 @@ static int create_or_delete_trace_uprobe(const char *raw_command)if (raw_command[0 ] =='-' )return dyn_event_release (raw_command, &trace_uprobe_ops); - ret =trace_uprobe_create (raw_command); + ret =dyn_event_create (raw_command, &trace_uprobe_ops);return ret == -ECANCELED ? -EINVAL : ret; }
Masami Hiramatsu (Google)
maintainerQuoted 9 lines · On Sat, 10 May 2025 12:44:41 +0900 "Masami Hiramatsu (Google)"…
From: Masami Hiramatsu (Google) <[email protected]>
Since the shared
trace_probe_logvariable can be accessed and modified via probe event create operation ofkprobe_events,uprobe_events, anddynamic_events, it should be protected. In thedynamic_events, all operations are serialized bydyn_event_ops_mutex. Butkprobe_eventsanduprobe_eventsinterfaces are not serialized.
Quoted 7 lines
To solve this issue, introduces
dyn_event_create(), which runscreate()operation under the mutex, forkprobe_eventsanduprobe_events. This also uses lockdep to check the mutex is held when using trace_probe_log* APIs.
BTW, this fixes the problem introduced by commit ab105a4fb894 ("tracing: Use tracing error_log with probe events"). However, this patch can be applied after commit d262271d0483 ("tracing/dynevent: Delegate parsing to create function").
Thus, before that commit, we may need another way to fix this issue. (e.g. introduce a different mutex.) Anyway, to record the correct fixes;
Thanks,
Quoted 124 lines
Signed-off-by Masami Hiramatsu (Google) <[email protected]>--- kernel/trace/trace_dynevent.c | 16 +++++++++++++++- kernel/trace/trace_dynevent.h | 1 + kernel/trace/trace_kprobe.c | 2 +- kernel/trace/trace_probe.c | 9 +++++++++ kernel/trace/trace_uprobe.c | 2 +- 5 files changed, 27 insertions(+), 3 deletions(-)kernel/trace/trace_dynevent.c +15
-1@@ -16,7 +16,7 @@#include "trace_output.h" /* for trace_event_sem */ #include "trace_dynevent.h" -static DEFINE_MUTEX (dyn_event_ops_mutex); +DEFINE_MUTEX (dyn_event_ops_mutex);static LIST_HEAD (dyn_event_ops_list);bool trace_event_dyn_try_get_ref (struct trace_event_call *dyn_call)@@ -116,6 +116,20 @@ int dyn_event_release(const char *raw_command, struct dyn_event_operations *typereturn ret; } +/* +* Locked version of event creation. The event creation must be protected by +* dyn_event_ops_mutex because of protecting trace_probe_log. +*/ +int dyn_event_create (const char *raw_command,struct dyn_event_operations *type) +{ +int ret; + +mutex_lock (&dyn_event_ops_mutex); + ret = type->create (raw_command); +mutex_unlock (&dyn_event_ops_mutex); +return ret; +} +static int create_dyn_event (const char *raw_command) {struct dyn_event_operations *ops;kernel/trace/trace_dynevent.h +1
-0@@ -100,6 +100,7 @@ void *dyn_event_seq_next(struct seq_file *m, void *v, loff_t *pos);void dyn_event_seq_stop (struct seq_file *m,void *v);int dyn_events_release_all (struct dyn_event_operations *type);int dyn_event_release (const char *raw_command,struct dyn_event_operations *type); +int dyn_event_create (const char *raw_command,struct dyn_event_operations *type);/* * for_each_dyn_event - iterate over the dyn_event list kernel/trace/trace_kprobe.c +1
-1@@ -1089,7 +1089,7 @@ static int create_or_delete_trace_kprobe(const char *raw_command)if (raw_command[0 ] =='-' )return dyn_event_release (raw_command, &trace_kprobe_ops); - ret =trace_kprobe_create (raw_command); + ret =dyn_event_create (raw_command, &trace_kprobe_ops);return ret == -ECANCELED ? -EINVAL : ret; }kernel/trace/trace_probe.c +9
-0@@ -154,9 +154,12 @@ static const struct fetch_type *find_fetch_type(const char *type, unsigned long }static struct trace_probe_log trace_probe_log; +extern struct mutex dyn_event_ops_mutex;void trace_probe_log_init (const char *subsystem,int argc,const char **argv) { +lockdep_assert_held (&dyn_event_ops_mutex); + trace_probe_log.subsystem = subsystem; trace_probe_log.argc = argc; trace_probe_log.argv = argv;@@ -165,11 +168,15 @@ void trace_probe_log_init(const char *subsystem, int argc, const char **argv)void trace_probe_log_clear (void ) { +lockdep_assert_held (&dyn_event_ops_mutex); +memset (&trace_probe_log,0 ,sizeof (trace_probe_log)); }void trace_probe_log_set_index (int index) { +lockdep_assert_held (&dyn_event_ops_mutex); + trace_probe_log.index = index; }@@ -178,6 +185,8 @@ void __trace_probe_log_err(int offset, int err_type)char *command, *p;int i, len =0 , pos =0 ; +lockdep_assert_held (&dyn_event_ops_mutex); +if (!trace_probe_log.argv)return ;kernel/trace/trace_uprobe.c +1
-1@@ -741,7 +741,7 @@ static int create_or_delete_trace_uprobe(const char *raw_command)if (raw_command[0 ] =='-' )return dyn_event_release (raw_command, &trace_uprobe_ops); - ret =trace_uprobe_create (raw_command); + ret =dyn_event_create (raw_command, &trace_uprobe_ops);return ret == -ECANCELED ? -EINVAL : ret; }
Signature · 1 line
Masami Hiramatsu (Google) <[email protected]>
Steven Rostedt
maintainerQuoted 8 lines · On Mon, 12 May 2025 09:24:42 +0900 Masami Hiramatsu (Google)…
BTW, this fixes the problem introduced by commit
ab105a4fb894("tracing: Use tracingerror_logwith probe events"). However, this patch can be applied after commitd262271d0483("tracing/dynevent: Delegate parsing to create function").Thus, before that commit, we may need another way to fix this issue. (e.g. introduce a different mutex.) Anyway, to record the correct fixes;
The method is if this depends on other commits, then those other commits need to be backported too.
-- Steve
Quoted 2 lines
Fixes ab105a4fb894 ("tracing: Use tracing error_log with probe events")