aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSteven Rostedt (Red Hat) <rostedt@goodmis.org>2013-07-02 15:30:53 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2013-07-25 17:07:43 -0400
commitfc82a11a9ce5ddc1cc49ac7bb2a099b9b18b85c0 (patch)
treef97865b25312269df9b98cbc54a3262029583515
parent68cebd265c91873277cf100e7ac1d047c6598ddf (diff)
tracing: Add trace_array_get/put() to event handling
commit 8e2e2fa47129532a30cff6c25a47078dc97d9260 upstream. 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. Reported-by: Alexander Lam <azl@google.com> Signed-off-by: Steven Rostedt <rostedt@goodmis.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-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 7944b9294599..51b44483eb78 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 32b9895af239..6dfd48b5d1c0 100644
--- a/kernel/trace/trace_events.c
+++ b/kernel/trace/trace_events.c
@@ -413,6 +413,35 @@ static void put_system(struct ftrace_subsystem_dir *dir)
413} 413}
414 414
415/* 415/*
416 * Open and update trace_array ref count.
417 * Must have the current trace_array passed to it.
418 */
419static int tracing_open_generic_file(struct inode *inode, struct file *filp)
420{
421 struct ftrace_event_file *file = inode->i_private;
422 struct trace_array *tr = file->tr;
423 int ret;
424
425 if (trace_array_get(tr) < 0)
426 return -ENODEV;
427
428 ret = tracing_open_generic(inode, filp);
429 if (ret < 0)
430 trace_array_put(tr);
431 return ret;
432}
433
434static int tracing_release_generic_file(struct inode *inode, struct file *filp)
435{
436 struct ftrace_event_file *file = inode->i_private;
437 struct trace_array *tr = file->tr;
438
439 trace_array_put(tr);
440
441 return 0;
442}
443
444/*
416 * __ftrace_set_clr_event(NULL, NULL, NULL, set) will set/unset all events. 445 * __ftrace_set_clr_event(NULL, NULL, NULL, set) will set/unset all events.
417 */ 446 */
418static int 447static int
@@ -1046,9 +1075,17 @@ static int subsystem_open(struct inode *inode, struct file *filp)
1046 /* Some versions of gcc think dir can be uninitialized here */ 1075 /* Some versions of gcc think dir can be uninitialized here */
1047 WARN_ON(!dir); 1076 WARN_ON(!dir);
1048 1077
1078 /* Still need to increment the ref count of the system */
1079 if (trace_array_get(tr) < 0) {
1080 put_system(dir);
1081 return -ENODEV;
1082 }
1083
1049 ret = tracing_open_generic(inode, filp); 1084 ret = tracing_open_generic(inode, filp);
1050 if (ret < 0) 1085 if (ret < 0) {
1086 trace_array_put(tr);
1051 put_system(dir); 1087 put_system(dir);
1088 }
1052 1089
1053 return ret; 1090 return ret;
1054} 1091}
@@ -1059,16 +1096,23 @@ static int system_tr_open(struct inode *inode, struct file *filp)
1059 struct trace_array *tr = inode->i_private; 1096 struct trace_array *tr = inode->i_private;
1060 int ret; 1097 int ret;
1061 1098
1099 if (trace_array_get(tr) < 0)
1100 return -ENODEV;
1101
1062 /* Make a temporary dir that has no system but points to tr */ 1102 /* Make a temporary dir that has no system but points to tr */
1063 dir = kzalloc(sizeof(*dir), GFP_KERNEL); 1103 dir = kzalloc(sizeof(*dir), GFP_KERNEL);
1064 if (!dir) 1104 if (!dir) {
1105 trace_array_put(tr);
1065 return -ENOMEM; 1106 return -ENOMEM;
1107 }
1066 1108
1067 dir->tr = tr; 1109 dir->tr = tr;
1068 1110
1069 ret = tracing_open_generic(inode, filp); 1111 ret = tracing_open_generic(inode, filp);
1070 if (ret < 0) 1112 if (ret < 0) {
1113 trace_array_put(tr);
1071 kfree(dir); 1114 kfree(dir);
1115 }
1072 1116
1073 filp->private_data = dir; 1117 filp->private_data = dir;
1074 1118
@@ -1079,6 +1123,8 @@ static int subsystem_release(struct inode *inode, struct file *file)
1079{ 1123{
1080 struct ftrace_subsystem_dir *dir = file->private_data; 1124 struct ftrace_subsystem_dir *dir = file->private_data;
1081 1125
1126 trace_array_put(dir->tr);
1127
1082 /* 1128 /*
1083 * If dir->subsystem is NULL, then this is a temporary 1129 * If dir->subsystem is NULL, then this is a temporary
1084 * descriptor that was made for a trace_array to enable 1130 * descriptor that was made for a trace_array to enable
@@ -1206,9 +1252,10 @@ static const struct file_operations ftrace_set_event_fops = {
1206}; 1252};
1207 1253
1208static const struct file_operations ftrace_enable_fops = { 1254static const struct file_operations ftrace_enable_fops = {
1209 .open = tracing_open_generic, 1255 .open = tracing_open_generic_file,
1210 .read = event_enable_read, 1256 .read = event_enable_read,
1211 .write = event_enable_write, 1257 .write = event_enable_write,
1258 .release = tracing_release_generic_file,
1212 .llseek = default_llseek, 1259 .llseek = default_llseek,
1213}; 1260};
1214 1261