aboutsummaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
authorSteven Rostedt <rostedt@goodmis.org>2013-06-27 10:58:31 -0400
committerSteven Rostedt <rostedt@goodmis.org>2013-07-01 20:34:22 -0400
commit6e94a780374ed31b280f939d4757e8d7858dff16 (patch)
treee319c403696469c0c818d5c4fe4b674b303eca9b /kernel
parent52d85d763086594f139bf7d3a5641abeb91d9f57 (diff)
tracing: Failed to create system directory
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. Cc: stable@vger.kernel.org # 3.10 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>
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 f57b01574a30..903a0bf2685e 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) { \
@@ -344,8 +361,8 @@ static void __put_system(struct event_subsystem *system)
344{ 361{
345 struct event_filter *filter = system->filter; 362 struct event_filter *filter = system->filter;
346 363
347 WARN_ON_ONCE(system->ref_count == 0); 364 WARN_ON_ONCE(system_refcount(system) == 0);
348 if (--system->ref_count) 365 if (system_refcount_dec(system))
349 return; 366 return;
350 367
351 list_del(&system->list); 368 list_del(&system->list);
@@ -354,13 +371,15 @@ static void __put_system(struct event_subsystem *system)
354 kfree(filter->filter_string); 371 kfree(filter->filter_string);
355 kfree(filter); 372 kfree(filter);
356 } 373 }
374 if (system->ref_count & SYSTEM_FL_FREE_NAME)
375 kfree(system->name);
357 kfree(system); 376 kfree(system);
358} 377}
359 378
360static void __get_system(struct event_subsystem *system) 379static void __get_system(struct event_subsystem *system)
361{ 380{
362 WARN_ON_ONCE(system->ref_count == 0); 381 WARN_ON_ONCE(system_refcount(system) == 0);
363 system->ref_count++; 382 system_refcount_inc(system);
364} 383}
365 384
366static void __get_system_dir(struct ftrace_subsystem_dir *dir) 385static void __get_system_dir(struct ftrace_subsystem_dir *dir)
@@ -374,7 +393,7 @@ static void __put_system_dir(struct ftrace_subsystem_dir *dir)
374{ 393{
375 WARN_ON_ONCE(dir->ref_count == 0); 394 WARN_ON_ONCE(dir->ref_count == 0);
376 /* If the subsystem is about to be freed, the dir must be too */ 395 /* If the subsystem is about to be freed, the dir must be too */
377 WARN_ON_ONCE(dir->subsystem->ref_count == 1 && dir->ref_count != 1); 396 WARN_ON_ONCE(system_refcount(dir->subsystem) == 1 && dir->ref_count != 1);
378 397
379 __put_system(dir->subsystem); 398 __put_system(dir->subsystem);
380 if (!--dir->ref_count) 399 if (!--dir->ref_count)
@@ -1274,7 +1293,15 @@ create_new_subsystem(const char *name)
1274 return NULL; 1293 return NULL;
1275 1294
1276 system->ref_count = 1; 1295 system->ref_count = 1;
1277 system->name = name; 1296
1297 /* Only allocate if dynamic (kprobes and modules) */
1298 if (!core_kernel_data((unsigned long)name)) {
1299 system->ref_count |= SYSTEM_FL_FREE_NAME;
1300 system->name = kstrdup(name, GFP_KERNEL);
1301 if (!system->name)
1302 goto out_free;
1303 } else
1304 system->name = name;
1278 1305
1279 system->filter = NULL; 1306 system->filter = NULL;
1280 1307
@@ -1287,6 +1314,8 @@ create_new_subsystem(const char *name)
1287 return system; 1314 return system;
1288 1315
1289 out_free: 1316 out_free:
1317 if (system->ref_count & SYSTEM_FL_FREE_NAME)
1318 kfree(system->name);
1290 kfree(system); 1319 kfree(system);
1291 return NULL; 1320 return NULL;
1292} 1321}