aboutsummaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
authorSteven Rostedt (Red Hat) <rostedt@goodmis.org>2013-07-02 15:30:53 -0400
committerSteven Rostedt <rostedt@goodmis.org>2013-07-02 17:13:34 -0400
commit8e2e2fa47129532a30cff6c25a47078dc97d9260 (patch)
tree6cc93f432b3186200c7afeaca1a432e497ad949c /kernel
parent7b85af63034818e43aee6c1d7bf1c7c6796a9073 (diff)
tracing: Add trace_array_get/put() to event handling
Commit a695cb58162 "tracing: Prevent deleting instances when they are being read" tried to fix a race between deleting a trace instance and reading contents of a trace file. But it wasn't good enough. The following could crash the kernel: # cd /sys/kernel/debug/tracing/instances # ( while :; do mkdir foo; rmdir foo; done ) & # ( while :; do echo 1 > foo/events/sched/sched_switch 2> /dev/null; done ) & Luckily this can only be done by root user, but it should be fixed regardless. The problem is that a delete of the file can happen after the write to the event is opened, but before the enabling happens. The solution is to make sure the trace_array is available before succeeding in opening for write, and incerment the ref counter while opened. Now the instance can be deleted when the events are writing to the buffer, but the deletion of the instance will disable all events before the instance is actually deleted. Cc: stable@vger.kernel.org # 3.10 Reported-by: Alexander Lam <azl@google.com> Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
Diffstat (limited to 'kernel')
-rw-r--r--kernel/trace/trace.h3
-rw-r--r--kernel/trace/trace_events.c55
2 files changed, 54 insertions, 4 deletions
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
index 2c3cba59552d..c7fbf93f1b7c 100644
--- a/kernel/trace/trace.h
+++ b/kernel/trace/trace.h
@@ -226,6 +226,9 @@ extern struct list_head ftrace_trace_arrays;
226 226
227extern struct mutex trace_types_lock; 227extern struct mutex trace_types_lock;
228 228
229extern int trace_array_get(struct trace_array *tr);
230extern void trace_array_put(struct trace_array *tr);
231
229/* 232/*
230 * The global tracer (top) should be the first trace array added, 233 * The global tracer (top) should be the first trace array added,
231 * but we check the flag anyway. 234 * but we check the flag anyway.
diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c
index 35c6f23c71b2..920e08fb53b3 100644
--- a/kernel/trace/trace_events.c
+++ b/kernel/trace/trace_events.c
@@ -410,6 +410,35 @@ static void put_system(struct ftrace_subsystem_dir *dir)
410} 410}
411 411
412/* 412/*
413 * Open and update trace_array ref count.
414 * Must have the current trace_array passed to it.
415 */
416static int tracing_open_generic_file(struct inode *inode, struct file *filp)
417{
418 struct ftrace_event_file *file = inode->i_private;
419 struct trace_array *tr = file->tr;
420 int ret;
421
422 if (trace_array_get(tr) < 0)
423 return -ENODEV;
424
425 ret = tracing_open_generic(inode, filp);
426 if (ret < 0)
427 trace_array_put(tr);
428 return ret;
429}
430
431static int tracing_release_generic_file(struct inode *inode, struct file *filp)
432{
433 struct ftrace_event_file *file = inode->i_private;
434 struct trace_array *tr = file->tr;
435
436 trace_array_put(tr);
437
438 return 0;
439}
440
441/*
413 * __ftrace_set_clr_event(NULL, NULL, NULL, set) will set/unset all events. 442 * __ftrace_set_clr_event(NULL, NULL, NULL, set) will set/unset all events.
414 */ 443 */
415static int __ftrace_set_clr_event(struct trace_array *tr, const char *match, 444static int __ftrace_set_clr_event(struct trace_array *tr, const char *match,
@@ -1032,9 +1061,17 @@ static int subsystem_open(struct inode *inode, struct file *filp)
1032 /* Some versions of gcc think dir can be uninitialized here */ 1061 /* Some versions of gcc think dir can be uninitialized here */
1033 WARN_ON(!dir); 1062 WARN_ON(!dir);
1034 1063
1064 /* Still need to increment the ref count of the system */
1065 if (trace_array_get(tr) < 0) {
1066 put_system(dir);
1067 return -ENODEV;
1068 }
1069
1035 ret = tracing_open_generic(inode, filp); 1070 ret = tracing_open_generic(inode, filp);
1036 if (ret < 0) 1071 if (ret < 0) {
1072 trace_array_put(tr);
1037 put_system(dir); 1073 put_system(dir);
1074 }
1038 1075
1039 return ret; 1076 return ret;
1040} 1077}
@@ -1045,16 +1082,23 @@ static int system_tr_open(struct inode *inode, struct file *filp)
1045 struct trace_array *tr = inode->i_private; 1082 struct trace_array *tr = inode->i_private;
1046 int ret; 1083 int ret;
1047 1084
1085 if (trace_array_get(tr) < 0)
1086 return -ENODEV;
1087
1048 /* Make a temporary dir that has no system but points to tr */ 1088 /* Make a temporary dir that has no system but points to tr */
1049 dir = kzalloc(sizeof(*dir), GFP_KERNEL); 1089 dir = kzalloc(sizeof(*dir), GFP_KERNEL);
1050 if (!dir) 1090 if (!dir) {
1091 trace_array_put(tr);
1051 return -ENOMEM; 1092 return -ENOMEM;
1093 }
1052 1094
1053 dir->tr = tr; 1095 dir->tr = tr;
1054 1096
1055 ret = tracing_open_generic(inode, filp); 1097 ret = tracing_open_generic(inode, filp);
1056 if (ret < 0) 1098 if (ret < 0) {
1099 trace_array_put(tr);
1057 kfree(dir); 1100 kfree(dir);
1101 }
1058 1102
1059 filp->private_data = dir; 1103 filp->private_data = dir;
1060 1104
@@ -1065,6 +1109,8 @@ static int subsystem_release(struct inode *inode, struct file *file)
1065{ 1109{
1066 struct ftrace_subsystem_dir *dir = file->private_data; 1110 struct ftrace_subsystem_dir *dir = file->private_data;
1067 1111
1112 trace_array_put(dir->tr);
1113
1068 /* 1114 /*
1069 * If dir->subsystem is NULL, then this is a temporary 1115 * If dir->subsystem is NULL, then this is a temporary
1070 * descriptor that was made for a trace_array to enable 1116 * descriptor that was made for a trace_array to enable
@@ -1192,9 +1238,10 @@ static const struct file_operations ftrace_set_event_fops = {
1192}; 1238};
1193 1239
1194static const struct file_operations ftrace_enable_fops = { 1240static const struct file_operations ftrace_enable_fops = {
1195 .open = tracing_open_generic, 1241 .open = tracing_open_generic_file,
1196 .read = event_enable_read, 1242 .read = event_enable_read,
1197 .write = event_enable_write, 1243 .write = event_enable_write,
1244 .release = tracing_release_generic_file,
1198 .llseek = default_llseek, 1245 .llseek = default_llseek,
1199}; 1246};
1200 1247