diff options
author | Oleg Nesterov <oleg@redhat.com> | 2013-07-23 11:25:54 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2013-08-29 12:47:32 -0400 |
commit | e2d4dbe02175e54616fe52c4e9c367199f8ecdaa (patch) | |
tree | 6e23d7e01e1df9a4e635e3f288ca722bf8310d6b /kernel | |
parent | e899e7e4f83ecd606e85d355410d724ec76542cb (diff) |
tracing: Introduce trace_create_cpu_file() and tracing_get_cpu()
commit 649e9c70da6bfbeb563193a35d3424a5aa7c0d38 upstream.
Every "file_operations" used by tracing_init_debugfs_percpu is buggy.
f_op->open/etc does:
1. struct trace_cpu *tc = inode->i_private;
struct trace_array *tr = tc->tr;
2. trace_array_get(tr) or fail;
3. do_something(tc);
But tc (and tr) can be already freed before trace_array_get() is called.
And it doesn't matter whether this file is per-cpu or it was created by
init_tracer_debugfs(), free_percpu() or kfree() are equally bad.
Note that even 1. is not safe, the freed memory can be unmapped. But even
if it was safe trace_array_get() can wrongly succeed if we also race with
the next new_instance_create() which can re-allocate the same tr, or tc
was overwritten and ->tr points to the valid tr. In this case 3. uses the
freed/reused memory.
Add the new trivial helper, trace_create_cpu_file() which simply calls
trace_create_file() and encodes "cpu" in "struct inode". Another helper,
tracing_get_cpu() will be used to read cpu_nr-or-RING_BUFFER_ALL_CPUS.
The patch abuses ->i_cdev to encode the number, it is never used unless
the file is S_ISCHR(). But we could use something else, say, i_bytes or
even ->d_fsdata. In any case this hack is hidden inside these 2 helpers,
it would be trivial to change them if needed.
This patch only changes tracing_init_debugfs_percpu() to use the new
trace_create_cpu_file(), the next patches will change file_operations.
Note: tracing_get_cpu(inode) is always safe but you can't trust the
result unless trace_array_get() was called, without trace_types_lock
which acts as a barrier it can wrongly return RING_BUFFER_ALL_CPUS.
Link: http://lkml.kernel.org/r/20130723152554.GA23710@redhat.com
Cc: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: Oleg Nesterov <oleg@redhat.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.c | 50 |
1 files changed, 36 insertions, 14 deletions
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 06a5bcef373f..f064dfb973fe 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c | |||
@@ -2834,6 +2834,17 @@ static int s_show(struct seq_file *m, void *v) | |||
2834 | return 0; | 2834 | return 0; |
2835 | } | 2835 | } |
2836 | 2836 | ||
2837 | /* | ||
2838 | * Should be used after trace_array_get(), trace_types_lock | ||
2839 | * ensures that i_cdev was already initialized. | ||
2840 | */ | ||
2841 | static inline int tracing_get_cpu(struct inode *inode) | ||
2842 | { | ||
2843 | if (inode->i_cdev) /* See trace_create_cpu_file() */ | ||
2844 | return (long)inode->i_cdev - 1; | ||
2845 | return RING_BUFFER_ALL_CPUS; | ||
2846 | } | ||
2847 | |||
2837 | static const struct seq_operations tracer_seq_ops = { | 2848 | static const struct seq_operations tracer_seq_ops = { |
2838 | .start = s_start, | 2849 | .start = s_start, |
2839 | .next = s_next, | 2850 | .next = s_next, |
@@ -5521,6 +5532,17 @@ static struct dentry *tracing_dentry_percpu(struct trace_array *tr, int cpu) | |||
5521 | return tr->percpu_dir; | 5532 | return tr->percpu_dir; |
5522 | } | 5533 | } |
5523 | 5534 | ||
5535 | static struct dentry * | ||
5536 | trace_create_cpu_file(const char *name, umode_t mode, struct dentry *parent, | ||
5537 | void *data, long cpu, const struct file_operations *fops) | ||
5538 | { | ||
5539 | struct dentry *ret = trace_create_file(name, mode, parent, data, fops); | ||
5540 | |||
5541 | if (ret) /* See tracing_get_cpu() */ | ||
5542 | ret->d_inode->i_cdev = (void *)(cpu + 1); | ||
5543 | return ret; | ||
5544 | } | ||
5545 | |||
5524 | static void | 5546 | static void |
5525 | tracing_init_debugfs_percpu(struct trace_array *tr, long cpu) | 5547 | tracing_init_debugfs_percpu(struct trace_array *tr, long cpu) |
5526 | { | 5548 | { |
@@ -5540,28 +5562,28 @@ tracing_init_debugfs_percpu(struct trace_array *tr, long cpu) | |||
5540 | } | 5562 | } |
5541 | 5563 | ||
5542 | /* per cpu trace_pipe */ | 5564 | /* per cpu trace_pipe */ |
5543 | trace_create_file("trace_pipe", 0444, d_cpu, | 5565 | trace_create_cpu_file("trace_pipe", 0444, d_cpu, |
5544 | (void *)&data->trace_cpu, &tracing_pipe_fops); | 5566 | &data->trace_cpu, cpu, &tracing_pipe_fops); |
5545 | 5567 | ||
5546 | /* per cpu trace */ | 5568 | /* per cpu trace */ |
5547 | trace_create_file("trace", 0644, d_cpu, | 5569 | trace_create_cpu_file("trace", 0644, d_cpu, |
5548 | (void *)&data->trace_cpu, &tracing_fops); | 5570 | &data->trace_cpu, cpu, &tracing_fops); |
5549 | 5571 | ||
5550 | trace_create_file("trace_pipe_raw", 0444, d_cpu, | 5572 | trace_create_cpu_file("trace_pipe_raw", 0444, d_cpu, |
5551 | (void *)&data->trace_cpu, &tracing_buffers_fops); | 5573 | &data->trace_cpu, cpu, &tracing_buffers_fops); |
5552 | 5574 | ||
5553 | trace_create_file("stats", 0444, d_cpu, | 5575 | trace_create_cpu_file("stats", 0444, d_cpu, |
5554 | (void *)&data->trace_cpu, &tracing_stats_fops); | 5576 | &data->trace_cpu, cpu, &tracing_stats_fops); |
5555 | 5577 | ||
5556 | trace_create_file("buffer_size_kb", 0444, d_cpu, | 5578 | trace_create_cpu_file("buffer_size_kb", 0444, d_cpu, |
5557 | (void *)&data->trace_cpu, &tracing_entries_fops); | 5579 | &data->trace_cpu, cpu, &tracing_entries_fops); |
5558 | 5580 | ||
5559 | #ifdef CONFIG_TRACER_SNAPSHOT | 5581 | #ifdef CONFIG_TRACER_SNAPSHOT |
5560 | trace_create_file("snapshot", 0644, d_cpu, | 5582 | trace_create_cpu_file("snapshot", 0644, d_cpu, |
5561 | (void *)&data->trace_cpu, &snapshot_fops); | 5583 | &data->trace_cpu, cpu, &snapshot_fops); |
5562 | 5584 | ||
5563 | trace_create_file("snapshot_raw", 0444, d_cpu, | 5585 | trace_create_cpu_file("snapshot_raw", 0444, d_cpu, |
5564 | (void *)&data->trace_cpu, &snapshot_raw_fops); | 5586 | &data->trace_cpu, cpu, &snapshot_raw_fops); |
5565 | #endif | 5587 | #endif |
5566 | } | 5588 | } |
5567 | 5589 | ||