aboutsummaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
authorSteven Rostedt (Red Hat) <rostedt@goodmis.org>2013-07-01 22:50:29 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2013-07-25 17:07:43 -0400
commit59d8f48855856c5e2e112bab78f1b1e6a14c216b (patch)
tree622b19ef2fca175a14164736117fa3fffe5612d4 /kernel
parent9713f78568d0053621530fb9cf06756394b4403c (diff)
tracing: Add trace_array_get/put() to handle instance refs better
commit ff451961a8b2a17667a7bfa39c86fb9b351445db 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 cat foo/trace &> /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 reader starts to open the file but before it grabs the trace_types_mutex. The solution is to validate the trace array before using it. If the trace array does not exist in the list of trace arrays, then it returns -ENODEV. There's a possibility that a trace_array could be deleted and a new one created and the open would open its file instead. But that is very minor as it will just return the data of the new trace array, it may confuse the user but it will not crash the system. As this can only be done by root anyway, the race will only occur if root is deleting what its trying to read at the same time. Reported-by: Alexander Lam <azl@google.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.c83
1 files changed, 65 insertions, 18 deletions
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index 10d3f0871b48..9d076a1ffa0b 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -193,6 +193,37 @@ static struct trace_array global_trace;
193 193
194LIST_HEAD(ftrace_trace_arrays); 194LIST_HEAD(ftrace_trace_arrays);
195 195
196int trace_array_get(struct trace_array *this_tr)
197{
198 struct trace_array *tr;
199 int ret = -ENODEV;
200
201 mutex_lock(&trace_types_lock);
202 list_for_each_entry(tr, &ftrace_trace_arrays, list) {
203 if (tr == this_tr) {
204 tr->ref++;
205 ret = 0;
206 break;
207 }
208 }
209 mutex_unlock(&trace_types_lock);
210
211 return ret;
212}
213
214static void __trace_array_put(struct trace_array *this_tr)
215{
216 WARN_ON(!this_tr->ref);
217 this_tr->ref--;
218}
219
220void trace_array_put(struct trace_array *this_tr)
221{
222 mutex_lock(&trace_types_lock);
223 __trace_array_put(this_tr);
224 mutex_unlock(&trace_types_lock);
225}
226
196int filter_current_check_discard(struct ring_buffer *buffer, 227int filter_current_check_discard(struct ring_buffer *buffer,
197 struct ftrace_event_call *call, void *rec, 228 struct ftrace_event_call *call, void *rec,
198 struct ring_buffer_event *event) 229 struct ring_buffer_event *event)
@@ -2768,10 +2799,9 @@ static const struct seq_operations tracer_seq_ops = {
2768}; 2799};
2769 2800
2770static struct trace_iterator * 2801static struct trace_iterator *
2771__tracing_open(struct inode *inode, struct file *file, bool snapshot) 2802__tracing_open(struct trace_array *tr, struct trace_cpu *tc,
2803 struct inode *inode, struct file *file, bool snapshot)
2772{ 2804{
2773 struct trace_cpu *tc = inode->i_private;
2774 struct trace_array *tr = tc->tr;
2775 struct trace_iterator *iter; 2805 struct trace_iterator *iter;
2776 int cpu; 2806 int cpu;
2777 2807
@@ -2850,8 +2880,6 @@ __tracing_open(struct inode *inode, struct file *file, bool snapshot)
2850 tracing_iter_reset(iter, cpu); 2880 tracing_iter_reset(iter, cpu);
2851 } 2881 }
2852 2882
2853 tr->ref++;
2854
2855 mutex_unlock(&trace_types_lock); 2883 mutex_unlock(&trace_types_lock);
2856 2884
2857 return iter; 2885 return iter;
@@ -2881,17 +2909,20 @@ static int tracing_release(struct inode *inode, struct file *file)
2881 struct trace_array *tr; 2909 struct trace_array *tr;
2882 int cpu; 2910 int cpu;
2883 2911
2884 if (!(file->f_mode & FMODE_READ)) 2912 /* Writes do not use seq_file, need to grab tr from inode */
2913 if (!(file->f_mode & FMODE_READ)) {
2914 struct trace_cpu *tc = inode->i_private;
2915
2916 trace_array_put(tc->tr);
2885 return 0; 2917 return 0;
2918 }
2886 2919
2887 iter = m->private; 2920 iter = m->private;
2888 tr = iter->tr; 2921 tr = iter->tr;
2922 trace_array_put(tr);
2889 2923
2890 mutex_lock(&trace_types_lock); 2924 mutex_lock(&trace_types_lock);
2891 2925
2892 WARN_ON(!tr->ref);
2893 tr->ref--;
2894
2895 for_each_tracing_cpu(cpu) { 2926 for_each_tracing_cpu(cpu) {
2896 if (iter->buffer_iter[cpu]) 2927 if (iter->buffer_iter[cpu])
2897 ring_buffer_read_finish(iter->buffer_iter[cpu]); 2928 ring_buffer_read_finish(iter->buffer_iter[cpu]);
@@ -2910,20 +2941,23 @@ static int tracing_release(struct inode *inode, struct file *file)
2910 kfree(iter->trace); 2941 kfree(iter->trace);
2911 kfree(iter->buffer_iter); 2942 kfree(iter->buffer_iter);
2912 seq_release_private(inode, file); 2943 seq_release_private(inode, file);
2944
2913 return 0; 2945 return 0;
2914} 2946}
2915 2947
2916static int tracing_open(struct inode *inode, struct file *file) 2948static int tracing_open(struct inode *inode, struct file *file)
2917{ 2949{
2950 struct trace_cpu *tc = inode->i_private;
2951 struct trace_array *tr = tc->tr;
2918 struct trace_iterator *iter; 2952 struct trace_iterator *iter;
2919 int ret = 0; 2953 int ret = 0;
2920 2954
2955 if (trace_array_get(tr) < 0)
2956 return -ENODEV;
2957
2921 /* If this file was open for write, then erase contents */ 2958 /* If this file was open for write, then erase contents */
2922 if ((file->f_mode & FMODE_WRITE) && 2959 if ((file->f_mode & FMODE_WRITE) &&
2923 (file->f_flags & O_TRUNC)) { 2960 (file->f_flags & O_TRUNC)) {
2924 struct trace_cpu *tc = inode->i_private;
2925 struct trace_array *tr = tc->tr;
2926
2927 if (tc->cpu == RING_BUFFER_ALL_CPUS) 2961 if (tc->cpu == RING_BUFFER_ALL_CPUS)
2928 tracing_reset_online_cpus(&tr->trace_buffer); 2962 tracing_reset_online_cpus(&tr->trace_buffer);
2929 else 2963 else
@@ -2931,12 +2965,16 @@ static int tracing_open(struct inode *inode, struct file *file)
2931 } 2965 }
2932 2966
2933 if (file->f_mode & FMODE_READ) { 2967 if (file->f_mode & FMODE_READ) {
2934 iter = __tracing_open(inode, file, false); 2968 iter = __tracing_open(tr, tc, inode, file, false);
2935 if (IS_ERR(iter)) 2969 if (IS_ERR(iter))
2936 ret = PTR_ERR(iter); 2970 ret = PTR_ERR(iter);
2937 else if (trace_flags & TRACE_ITER_LATENCY_FMT) 2971 else if (trace_flags & TRACE_ITER_LATENCY_FMT)
2938 iter->iter_flags |= TRACE_FILE_LAT_FMT; 2972 iter->iter_flags |= TRACE_FILE_LAT_FMT;
2939 } 2973 }
2974
2975 if (ret < 0)
2976 trace_array_put(tr);
2977
2940 return ret; 2978 return ret;
2941} 2979}
2942 2980
@@ -4512,12 +4550,16 @@ struct ftrace_buffer_info {
4512static int tracing_snapshot_open(struct inode *inode, struct file *file) 4550static int tracing_snapshot_open(struct inode *inode, struct file *file)
4513{ 4551{
4514 struct trace_cpu *tc = inode->i_private; 4552 struct trace_cpu *tc = inode->i_private;
4553 struct trace_array *tr = tc->tr;
4515 struct trace_iterator *iter; 4554 struct trace_iterator *iter;
4516 struct seq_file *m; 4555 struct seq_file *m;
4517 int ret = 0; 4556 int ret = 0;
4518 4557
4558 if (trace_array_get(tr) < 0)
4559 return -ENODEV;
4560
4519 if (file->f_mode & FMODE_READ) { 4561 if (file->f_mode & FMODE_READ) {
4520 iter = __tracing_open(inode, file, true); 4562 iter = __tracing_open(tr, tc, inode, file, true);
4521 if (IS_ERR(iter)) 4563 if (IS_ERR(iter))
4522 ret = PTR_ERR(iter); 4564 ret = PTR_ERR(iter);
4523 } else { 4565 } else {
@@ -4530,13 +4572,16 @@ static int tracing_snapshot_open(struct inode *inode, struct file *file)
4530 kfree(m); 4572 kfree(m);
4531 return -ENOMEM; 4573 return -ENOMEM;
4532 } 4574 }
4533 iter->tr = tc->tr; 4575 iter->tr = tr;
4534 iter->trace_buffer = &tc->tr->max_buffer; 4576 iter->trace_buffer = &tc->tr->max_buffer;
4535 iter->cpu_file = tc->cpu; 4577 iter->cpu_file = tc->cpu;
4536 m->private = iter; 4578 m->private = iter;
4537 file->private_data = m; 4579 file->private_data = m;
4538 } 4580 }
4539 4581
4582 if (ret < 0)
4583 trace_array_put(tr);
4584
4540 return ret; 4585 return ret;
4541} 4586}
4542 4587
@@ -4617,9 +4662,12 @@ out:
4617static int tracing_snapshot_release(struct inode *inode, struct file *file) 4662static int tracing_snapshot_release(struct inode *inode, struct file *file)
4618{ 4663{
4619 struct seq_file *m = file->private_data; 4664 struct seq_file *m = file->private_data;
4665 int ret;
4666
4667 ret = tracing_release(inode, file);
4620 4668
4621 if (file->f_mode & FMODE_READ) 4669 if (file->f_mode & FMODE_READ)
4622 return tracing_release(inode, file); 4670 return ret;
4623 4671
4624 /* If write only, the seq_file is just a stub */ 4672 /* If write only, the seq_file is just a stub */
4625 if (m) 4673 if (m)
@@ -4864,8 +4912,7 @@ static int tracing_buffers_release(struct inode *inode, struct file *file)
4864 4912
4865 mutex_lock(&trace_types_lock); 4913 mutex_lock(&trace_types_lock);
4866 4914
4867 WARN_ON(!iter->tr->ref); 4915 __trace_array_put(iter->tr);
4868 iter->tr->ref--;
4869 4916
4870 if (info->spare) 4917 if (info->spare)
4871 ring_buffer_free_read_page(iter->trace_buffer->buffer, info->spare); 4918 ring_buffer_free_read_page(iter->trace_buffer->buffer, info->spare);