diff options
| author | Oleg Nesterov <oleg@redhat.com> | 2013-07-23 11:26:10 -0400 |
|---|---|---|
| committer | Steven Rostedt <rostedt@rostedt.homelinux.com> | 2013-07-24 11:22:53 -0400 |
| commit | 6484c71cbc170634fa131b6d022d86d61686b88b (patch) | |
| tree | 68f696f4985f368a2fa7f93a2d474fea2c7585db /kernel/trace | |
| parent | 0bc392ee46d0fd8e6b678457ef71f074f19a03c5 (diff) | |
tracing: Change tracing_fops/snapshot_fops to rely on tracing_get_cpu()
tracing_open() and tracing_snapshot_open() are racy, the memory
inode->i_private points to can be already freed.
Convert these last users of "inode->i_private == trace_cpu" to
use "i_private = trace_array" and rely on tracing_get_cpu().
v2: incorporate the fix from Steven, tracing_release() must not
blindly dereference file->private_data unless we know that
the file was opened for reading.
Link: http://lkml.kernel.org/r/20130723152610.GA23737@redhat.com
Signed-off-by: Oleg Nesterov <oleg@redhat.com>
Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
Diffstat (limited to 'kernel/trace')
| -rw-r--r-- | kernel/trace/trace.c | 50 |
1 files changed, 22 insertions, 28 deletions
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 68b46851666f..dd7780ddde08 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c | |||
| @@ -2862,9 +2862,9 @@ static const struct seq_operations tracer_seq_ops = { | |||
| 2862 | }; | 2862 | }; |
| 2863 | 2863 | ||
| 2864 | static struct trace_iterator * | 2864 | static struct trace_iterator * |
| 2865 | __tracing_open(struct trace_array *tr, struct trace_cpu *tc, | 2865 | __tracing_open(struct inode *inode, struct file *file, bool snapshot) |
| 2866 | struct inode *inode, struct file *file, bool snapshot) | ||
| 2867 | { | 2866 | { |
| 2867 | struct trace_array *tr = inode->i_private; | ||
| 2868 | struct trace_iterator *iter; | 2868 | struct trace_iterator *iter; |
| 2869 | int cpu; | 2869 | int cpu; |
| 2870 | 2870 | ||
| @@ -2905,8 +2905,8 @@ __tracing_open(struct trace_array *tr, struct trace_cpu *tc, | |||
| 2905 | iter->trace_buffer = &tr->trace_buffer; | 2905 | iter->trace_buffer = &tr->trace_buffer; |
| 2906 | iter->snapshot = snapshot; | 2906 | iter->snapshot = snapshot; |
| 2907 | iter->pos = -1; | 2907 | iter->pos = -1; |
| 2908 | iter->cpu_file = tracing_get_cpu(inode); | ||
| 2908 | mutex_init(&iter->mutex); | 2909 | mutex_init(&iter->mutex); |
| 2909 | iter->cpu_file = tc->cpu; | ||
| 2910 | 2910 | ||
| 2911 | /* Notify the tracer early; before we stop tracing. */ | 2911 | /* Notify the tracer early; before we stop tracing. */ |
| 2912 | if (iter->trace && iter->trace->open) | 2912 | if (iter->trace && iter->trace->open) |
| @@ -2986,22 +2986,18 @@ static int tracing_open_generic_tr(struct inode *inode, struct file *filp) | |||
| 2986 | 2986 | ||
| 2987 | static int tracing_release(struct inode *inode, struct file *file) | 2987 | static int tracing_release(struct inode *inode, struct file *file) |
| 2988 | { | 2988 | { |
| 2989 | struct trace_array *tr = inode->i_private; | ||
| 2989 | struct seq_file *m = file->private_data; | 2990 | struct seq_file *m = file->private_data; |
| 2990 | struct trace_iterator *iter; | 2991 | struct trace_iterator *iter; |
| 2991 | struct trace_array *tr; | ||
| 2992 | int cpu; | 2992 | int cpu; |
| 2993 | 2993 | ||
| 2994 | /* Writes do not use seq_file, need to grab tr from inode */ | ||
| 2995 | if (!(file->f_mode & FMODE_READ)) { | 2994 | if (!(file->f_mode & FMODE_READ)) { |
| 2996 | struct trace_cpu *tc = inode->i_private; | 2995 | trace_array_put(tr); |
| 2997 | |||
| 2998 | trace_array_put(tc->tr); | ||
| 2999 | return 0; | 2996 | return 0; |
| 3000 | } | 2997 | } |
| 3001 | 2998 | ||
| 2999 | /* Writes do not use seq_file */ | ||
| 3002 | iter = m->private; | 3000 | iter = m->private; |
| 3003 | tr = iter->tr; | ||
| 3004 | |||
| 3005 | mutex_lock(&trace_types_lock); | 3001 | mutex_lock(&trace_types_lock); |
| 3006 | 3002 | ||
| 3007 | for_each_tracing_cpu(cpu) { | 3003 | for_each_tracing_cpu(cpu) { |
| @@ -3048,8 +3044,7 @@ static int tracing_single_release_tr(struct inode *inode, struct file *file) | |||
| 3048 | 3044 | ||
| 3049 | static int tracing_open(struct inode *inode, struct file *file) | 3045 | static int tracing_open(struct inode *inode, struct file *file) |
| 3050 | { | 3046 | { |
| 3051 | struct trace_cpu *tc = inode->i_private; | 3047 | struct trace_array *tr = inode->i_private; |
| 3052 | struct trace_array *tr = tc->tr; | ||
| 3053 | struct trace_iterator *iter; | 3048 | struct trace_iterator *iter; |
| 3054 | int ret = 0; | 3049 | int ret = 0; |
| 3055 | 3050 | ||
| @@ -3057,16 +3052,17 @@ static int tracing_open(struct inode *inode, struct file *file) | |||
| 3057 | return -ENODEV; | 3052 | return -ENODEV; |
| 3058 | 3053 | ||
| 3059 | /* If this file was open for write, then erase contents */ | 3054 | /* If this file was open for write, then erase contents */ |
| 3060 | if ((file->f_mode & FMODE_WRITE) && | 3055 | if ((file->f_mode & FMODE_WRITE) && (file->f_flags & O_TRUNC)) { |
| 3061 | (file->f_flags & O_TRUNC)) { | 3056 | int cpu = tracing_get_cpu(inode); |
| 3062 | if (tc->cpu == RING_BUFFER_ALL_CPUS) | 3057 | |
| 3058 | if (cpu == RING_BUFFER_ALL_CPUS) | ||
| 3063 | tracing_reset_online_cpus(&tr->trace_buffer); | 3059 | tracing_reset_online_cpus(&tr->trace_buffer); |
| 3064 | else | 3060 | else |
| 3065 | tracing_reset(&tr->trace_buffer, tc->cpu); | 3061 | tracing_reset(&tr->trace_buffer, cpu); |
| 3066 | } | 3062 | } |
| 3067 | 3063 | ||
| 3068 | if (file->f_mode & FMODE_READ) { | 3064 | if (file->f_mode & FMODE_READ) { |
| 3069 | iter = __tracing_open(tr, tc, inode, file, false); | 3065 | iter = __tracing_open(inode, file, false); |
| 3070 | if (IS_ERR(iter)) | 3066 | if (IS_ERR(iter)) |
| 3071 | ret = PTR_ERR(iter); | 3067 | ret = PTR_ERR(iter); |
| 3072 | else if (trace_flags & TRACE_ITER_LATENCY_FMT) | 3068 | else if (trace_flags & TRACE_ITER_LATENCY_FMT) |
| @@ -4680,8 +4676,7 @@ struct ftrace_buffer_info { | |||
| 4680 | #ifdef CONFIG_TRACER_SNAPSHOT | 4676 | #ifdef CONFIG_TRACER_SNAPSHOT |
| 4681 | static int tracing_snapshot_open(struct inode *inode, struct file *file) | 4677 | static int tracing_snapshot_open(struct inode *inode, struct file *file) |
| 4682 | { | 4678 | { |
| 4683 | struct trace_cpu *tc = inode->i_private; | 4679 | struct trace_array *tr = inode->i_private; |
| 4684 | struct trace_array *tr = tc->tr; | ||
| 4685 | struct trace_iterator *iter; | 4680 | struct trace_iterator *iter; |
| 4686 | struct seq_file *m; | 4681 | struct seq_file *m; |
| 4687 | int ret = 0; | 4682 | int ret = 0; |
| @@ -4690,7 +4685,7 @@ static int tracing_snapshot_open(struct inode *inode, struct file *file) | |||
| 4690 | return -ENODEV; | 4685 | return -ENODEV; |
| 4691 | 4686 | ||
| 4692 | if (file->f_mode & FMODE_READ) { | 4687 | if (file->f_mode & FMODE_READ) { |
| 4693 | iter = __tracing_open(tr, tc, inode, file, true); | 4688 | iter = __tracing_open(inode, file, true); |
| 4694 | if (IS_ERR(iter)) | 4689 | if (IS_ERR(iter)) |
| 4695 | ret = PTR_ERR(iter); | 4690 | ret = PTR_ERR(iter); |
| 4696 | } else { | 4691 | } else { |
| @@ -4707,8 +4702,8 @@ static int tracing_snapshot_open(struct inode *inode, struct file *file) | |||
| 4707 | ret = 0; | 4702 | ret = 0; |
| 4708 | 4703 | ||
| 4709 | iter->tr = tr; | 4704 | iter->tr = tr; |
| 4710 | iter->trace_buffer = &tc->tr->max_buffer; | 4705 | iter->trace_buffer = &tr->max_buffer; |
| 4711 | iter->cpu_file = tc->cpu; | 4706 | iter->cpu_file = tracing_get_cpu(inode); |
| 4712 | m->private = iter; | 4707 | m->private = iter; |
| 4713 | file->private_data = m; | 4708 | file->private_data = m; |
| 4714 | } | 4709 | } |
| @@ -5525,7 +5520,6 @@ trace_create_cpu_file(const char *name, umode_t mode, struct dentry *parent, | |||
| 5525 | static void | 5520 | static void |
| 5526 | tracing_init_debugfs_percpu(struct trace_array *tr, long cpu) | 5521 | tracing_init_debugfs_percpu(struct trace_array *tr, long cpu) |
| 5527 | { | 5522 | { |
| 5528 | struct trace_array_cpu *data = per_cpu_ptr(tr->trace_buffer.data, cpu); | ||
| 5529 | struct dentry *d_percpu = tracing_dentry_percpu(tr, cpu); | 5523 | struct dentry *d_percpu = tracing_dentry_percpu(tr, cpu); |
| 5530 | struct dentry *d_cpu; | 5524 | struct dentry *d_cpu; |
| 5531 | char cpu_dir[30]; /* 30 characters should be more than enough */ | 5525 | char cpu_dir[30]; /* 30 characters should be more than enough */ |
| @@ -5546,7 +5540,7 @@ tracing_init_debugfs_percpu(struct trace_array *tr, long cpu) | |||
| 5546 | 5540 | ||
| 5547 | /* per cpu trace */ | 5541 | /* per cpu trace */ |
| 5548 | trace_create_cpu_file("trace", 0644, d_cpu, | 5542 | trace_create_cpu_file("trace", 0644, d_cpu, |
| 5549 | &data->trace_cpu, cpu, &tracing_fops); | 5543 | tr, cpu, &tracing_fops); |
| 5550 | 5544 | ||
| 5551 | trace_create_cpu_file("trace_pipe_raw", 0444, d_cpu, | 5545 | trace_create_cpu_file("trace_pipe_raw", 0444, d_cpu, |
| 5552 | tr, cpu, &tracing_buffers_fops); | 5546 | tr, cpu, &tracing_buffers_fops); |
| @@ -5559,7 +5553,7 @@ tracing_init_debugfs_percpu(struct trace_array *tr, long cpu) | |||
| 5559 | 5553 | ||
| 5560 | #ifdef CONFIG_TRACER_SNAPSHOT | 5554 | #ifdef CONFIG_TRACER_SNAPSHOT |
| 5561 | trace_create_cpu_file("snapshot", 0644, d_cpu, | 5555 | trace_create_cpu_file("snapshot", 0644, d_cpu, |
| 5562 | &data->trace_cpu, cpu, &snapshot_fops); | 5556 | tr, cpu, &snapshot_fops); |
| 5563 | 5557 | ||
| 5564 | trace_create_cpu_file("snapshot_raw", 0444, d_cpu, | 5558 | trace_create_cpu_file("snapshot_raw", 0444, d_cpu, |
| 5565 | tr, cpu, &snapshot_raw_fops); | 5559 | tr, cpu, &snapshot_raw_fops); |
| @@ -6125,7 +6119,7 @@ init_tracer_debugfs(struct trace_array *tr, struct dentry *d_tracer) | |||
| 6125 | tr, &tracing_iter_fops); | 6119 | tr, &tracing_iter_fops); |
| 6126 | 6120 | ||
| 6127 | trace_create_file("trace", 0644, d_tracer, | 6121 | trace_create_file("trace", 0644, d_tracer, |
| 6128 | (void *)&tr->trace_cpu, &tracing_fops); | 6122 | tr, &tracing_fops); |
| 6129 | 6123 | ||
| 6130 | trace_create_file("trace_pipe", 0444, d_tracer, | 6124 | trace_create_file("trace_pipe", 0444, d_tracer, |
| 6131 | tr, &tracing_pipe_fops); | 6125 | tr, &tracing_pipe_fops); |
| @@ -6146,11 +6140,11 @@ init_tracer_debugfs(struct trace_array *tr, struct dentry *d_tracer) | |||
| 6146 | &trace_clock_fops); | 6140 | &trace_clock_fops); |
| 6147 | 6141 | ||
| 6148 | trace_create_file("tracing_on", 0644, d_tracer, | 6142 | trace_create_file("tracing_on", 0644, d_tracer, |
| 6149 | tr, &rb_simple_fops); | 6143 | tr, &rb_simple_fops); |
| 6150 | 6144 | ||
| 6151 | #ifdef CONFIG_TRACER_SNAPSHOT | 6145 | #ifdef CONFIG_TRACER_SNAPSHOT |
| 6152 | trace_create_file("snapshot", 0644, d_tracer, | 6146 | trace_create_file("snapshot", 0644, d_tracer, |
| 6153 | (void *)&tr->trace_cpu, &snapshot_fops); | 6147 | tr, &snapshot_fops); |
| 6154 | #endif | 6148 | #endif |
| 6155 | 6149 | ||
| 6156 | for_each_tracing_cpu(cpu) | 6150 | for_each_tracing_cpu(cpu) |
