aboutsummaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
authorSteven Rostedt <rostedt@goodmis.org>2013-06-27 10:58:31 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2013-07-25 17:07:43 -0400
commite6929efa3320454d9a572300a4eb97a576c7d556 (patch)
treea2143f927dfb74001aea8b33cf045d544f90c737 /kernel
parent65e303d786e20460c3d67d362f989f59944fb744 (diff)
tracing: Failed to create system directory
commit 6e94a780374ed31b280f939d4757e8d7858dff16 upstream. Running the following: # cd /sys/kernel/debug/tracing # echo p:i do_sys_open > kprobe_events # echo p:j schedule >> kprobe_events # cat kprobe_events p:kprobes/i do_sys_open p:kprobes/j schedule # echo p:i do_sys_open >> kprobe_events # cat kprobe_events p:kprobes/j schedule p:kprobes/i do_sys_open # ls /sys/kernel/debug/tracing/events/kprobes/ enable filter j Notice that the 'i' is missing from the kprobes directory. The console produces: "Failed to create system directory kprobes" This is because kprobes passes in a allocated name for the system and the ftrace event subsystem saves off that name instead of creating a duplicate for it. But the kprobes may free the system name making the pointer to it invalid. This bug was introduced by 92edca073c37 "tracing: Use direct field, type and system names" which switched from using kstrdup() on the system name in favor of just keeping apointer to it, as the internal ftrace event system names are static and exist for the life of the computer being booted. Instead of reverting back to duplicating system names again, we can use core_kernel_data() to determine if the passed in name was allocated or static. Then use the MSB of the ref_count to be a flag to keep track if the name was allocated or not. Then we can still save from having to duplicate strings that will always exist, but still copy the ones that may be freed. Reported-by: "zhangwei(Jovi)" <jovi.zhangwei@huawei.com> Reported-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com> Tested-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com> Signed-off-by: Steven Rostedt <rostedt@goodmis.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'kernel')
-rw-r--r--kernel/trace/trace_events.c41
1 files changed, 35 insertions, 6 deletions
diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c
index 27963e2bf4bf..bf56c1d07df3 100644
--- a/kernel/trace/trace_events.c
+++ b/kernel/trace/trace_events.c
@@ -41,6 +41,23 @@ static LIST_HEAD(ftrace_common_fields);
41static struct kmem_cache *field_cachep; 41static struct kmem_cache *field_cachep;
42static struct kmem_cache *file_cachep; 42static struct kmem_cache *file_cachep;
43 43
44#define SYSTEM_FL_FREE_NAME (1 << 31)
45
46static inline int system_refcount(struct event_subsystem *system)
47{
48 return system->ref_count & ~SYSTEM_FL_FREE_NAME;
49}
50
51static int system_refcount_inc(struct event_subsystem *system)
52{
53 return (system->ref_count++) & ~SYSTEM_FL_FREE_NAME;
54}
55
56static int system_refcount_dec(struct event_subsystem *system)
57{
58 return (--system->ref_count) & ~SYSTEM_FL_FREE_NAME;
59}
60
44/* Double loops, do not use break, only goto's work */ 61/* Double loops, do not use break, only goto's work */
45#define do_for_each_event_file(tr, file) \ 62#define do_for_each_event_file(tr, file) \
46 list_for_each_entry(tr, &ftrace_trace_arrays, list) { \ 63 list_for_each_entry(tr, &ftrace_trace_arrays, list) { \
@@ -349,8 +366,8 @@ static void __put_system(struct event_subsystem *system)
349{ 366{
350 struct event_filter *filter = system->filter; 367 struct event_filter *filter = system->filter;
351 368
352 WARN_ON_ONCE(system->ref_count == 0); 369 WARN_ON_ONCE(system_refcount(system) == 0);
353 if (--system->ref_count) 370 if (system_refcount_dec(system))
354 return; 371 return;
355 372
356 list_del(&system->list); 373 list_del(&system->list);
@@ -359,13 +376,15 @@ static void __put_system(struct event_subsystem *system)
359 kfree(filter->filter_string); 376 kfree(filter->filter_string);
360 kfree(filter); 377 kfree(filter);
361 } 378 }
379 if (system->ref_count & SYSTEM_FL_FREE_NAME)
380 kfree(system->name);
362 kfree(system); 381 kfree(system);
363} 382}
364 383
365static void __get_system(struct event_subsystem *system) 384static void __get_system(struct event_subsystem *system)
366{ 385{
367 WARN_ON_ONCE(system->ref_count == 0); 386 WARN_ON_ONCE(system_refcount(system) == 0);
368 system->ref_count++; 387 system_refcount_inc(system);
369} 388}
370 389
371static void __get_system_dir(struct ftrace_subsystem_dir *dir) 390static void __get_system_dir(struct ftrace_subsystem_dir *dir)
@@ -379,7 +398,7 @@ static void __put_system_dir(struct ftrace_subsystem_dir *dir)
379{ 398{
380 WARN_ON_ONCE(dir->ref_count == 0); 399 WARN_ON_ONCE(dir->ref_count == 0);
381 /* If the subsystem is about to be freed, the dir must be too */ 400 /* If the subsystem is about to be freed, the dir must be too */
382 WARN_ON_ONCE(dir->subsystem->ref_count == 1 && dir->ref_count != 1); 401 WARN_ON_ONCE(system_refcount(dir->subsystem) == 1 && dir->ref_count != 1);
383 402
384 __put_system(dir->subsystem); 403 __put_system(dir->subsystem);
385 if (!--dir->ref_count) 404 if (!--dir->ref_count)
@@ -1279,7 +1298,15 @@ create_new_subsystem(const char *name)
1279 return NULL; 1298 return NULL;
1280 1299
1281 system->ref_count = 1; 1300 system->ref_count = 1;
1282 system->name = name; 1301
1302 /* Only allocate if dynamic (kprobes and modules) */
1303 if (!core_kernel_data((unsigned long)name)) {
1304 system->ref_count |= SYSTEM_FL_FREE_NAME;
1305 system->name = kstrdup(name, GFP_KERNEL);
1306 if (!system->name)
1307 goto out_free;
1308 } else
1309 system->name = name;
1283 1310
1284 system->filter = NULL; 1311 system->filter = NULL;
1285 1312
@@ -1292,6 +1319,8 @@ create_new_subsystem(const char *name)
1292 return system; 1319 return system;
1293 1320
1294 out_free: 1321 out_free:
1322 if (system->ref_count & SYSTEM_FL_FREE_NAME)
1323 kfree(system->name);
1295 kfree(system); 1324 kfree(system);
1296 return NULL; 1325 return NULL;
1297} 1326}