From: Greg Kroah-Hartman Date: Thu, 22 Aug 2013 23:11:17 +0000 (-0700) Subject: 3.10-stable patches X-Git-Tag: v3.0.94~13 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=07bf8dd03e66f475e69799eb048480071bd8e4cc;p=thirdparty%2Fkernel%2Fstable-queue.git 3.10-stable patches added patches: tracing-change-event_enable-disable_read-to-verify-i_private-null.patch tracing-change-event_filter_read-write-to-verify-i_private-null.patch tracing-change-f_start-to-take-event_mutex-and-verify-i_private-null.patch tracing-introduce-remove_event_file_dir.patch --- diff --git a/queue-3.10/series b/queue-3.10/series index 4418fc4ab9f..565ecada592 100644 --- a/queue-3.10/series +++ b/queue-3.10/series @@ -24,3 +24,7 @@ tracing-change-tracing_entries_fops-to-rely-on-tracing_get_cpu.patch tracing-change-tracing_fops-snapshot_fops-to-rely-on-tracing_get_cpu.patch ftrace-add-check-for-null-regs-if-ops-has-save_regs-set.patch tracing-turn-event-id-i_private-into-call-event.type.patch +tracing-change-event_enable-disable_read-to-verify-i_private-null.patch +tracing-change-event_filter_read-write-to-verify-i_private-null.patch +tracing-change-f_start-to-take-event_mutex-and-verify-i_private-null.patch +tracing-introduce-remove_event_file_dir.patch diff --git a/queue-3.10/tracing-change-event_enable-disable_read-to-verify-i_private-null.patch b/queue-3.10/tracing-change-event_enable-disable_read-to-verify-i_private-null.patch new file mode 100644 index 00000000000..fb0663187ea --- /dev/null +++ b/queue-3.10/tracing-change-event_enable-disable_read-to-verify-i_private-null.patch @@ -0,0 +1,88 @@ +From bc6f6b08dee5645770efb4b76186ded313f23752 Mon Sep 17 00:00:00 2001 +From: Oleg Nesterov +Date: Fri, 26 Jul 2013 19:25:36 +0200 +Subject: tracing: Change event_enable/disable_read() to verify i_private != NULL + +From: Oleg Nesterov + +commit bc6f6b08dee5645770efb4b76186ded313f23752 upstream. + +tracing_open_generic_file() is racy, ftrace_event_file can be +already freed by rmdir or trace_remove_event_call(). + +Change event_enable_read() and event_disable_read() to read and +verify "file = i_private" under event_mutex. + +This fixes nothing, but now we can change debugfs_remove("enable") +callers to nullify ->i_private and fix the the problem. + +Link: http://lkml.kernel.org/r/20130726172536.GA3612@redhat.com + +Reviewed-by: Masami Hiramatsu +Signed-off-by: Oleg Nesterov +Signed-off-by: Steven Rostedt +Signed-off-by: Greg Kroah-Hartman + + +--- + kernel/trace/trace_events.c | 28 +++++++++++++++++++--------- + 1 file changed, 19 insertions(+), 9 deletions(-) + +--- a/kernel/trace/trace_events.c ++++ b/kernel/trace/trace_events.c +@@ -682,13 +682,23 @@ static ssize_t + event_enable_read(struct file *filp, char __user *ubuf, size_t cnt, + loff_t *ppos) + { +- struct ftrace_event_file *file = filp->private_data; ++ struct ftrace_event_file *file; ++ unsigned long flags; + char *buf; + +- if (file->flags & FTRACE_EVENT_FL_ENABLED) { +- if (file->flags & FTRACE_EVENT_FL_SOFT_DISABLED) ++ mutex_lock(&event_mutex); ++ file = event_file_data(filp); ++ if (likely(file)) ++ flags = file->flags; ++ mutex_unlock(&event_mutex); ++ ++ if (!file) ++ return -ENODEV; ++ ++ if (flags & FTRACE_EVENT_FL_ENABLED) { ++ if (flags & FTRACE_EVENT_FL_SOFT_DISABLED) + buf = "0*\n"; +- else if (file->flags & FTRACE_EVENT_FL_SOFT_MODE) ++ else if (flags & FTRACE_EVENT_FL_SOFT_MODE) + buf = "1*\n"; + else + buf = "1\n"; +@@ -702,13 +712,10 @@ static ssize_t + event_enable_write(struct file *filp, const char __user *ubuf, size_t cnt, + loff_t *ppos) + { +- struct ftrace_event_file *file = filp->private_data; ++ struct ftrace_event_file *file; + unsigned long val; + int ret; + +- if (!file) +- return -EINVAL; +- + ret = kstrtoul_from_user(ubuf, cnt, 10, &val); + if (ret) + return ret; +@@ -720,8 +727,11 @@ event_enable_write(struct file *filp, co + switch (val) { + case 0: + case 1: ++ ret = -ENODEV; + mutex_lock(&event_mutex); +- ret = ftrace_event_enable_disable(file, val); ++ file = event_file_data(filp); ++ if (likely(file)) ++ ret = ftrace_event_enable_disable(file, val); + mutex_unlock(&event_mutex); + break; + diff --git a/queue-3.10/tracing-change-event_filter_read-write-to-verify-i_private-null.patch b/queue-3.10/tracing-change-event_filter_read-write-to-verify-i_private-null.patch new file mode 100644 index 00000000000..2de9689601e --- /dev/null +++ b/queue-3.10/tracing-change-event_filter_read-write-to-verify-i_private-null.patch @@ -0,0 +1,154 @@ +From e2912b091c26b8ea95e5e00a43a7ac620f6c94a6 Mon Sep 17 00:00:00 2001 +From: Oleg Nesterov +Date: Fri, 26 Jul 2013 19:25:40 +0200 +Subject: tracing: Change event_filter_read/write to verify i_private != NULL + +From: Oleg Nesterov + +commit e2912b091c26b8ea95e5e00a43a7ac620f6c94a6 upstream. + +event_filter_read/write() are racy, ftrace_event_call can be already +freed by trace_remove_event_call() callers. + +1. Shift mutex_lock(event_mutex) from print/apply_event_filter to + the callers. + +2. Change the callers, event_filter_read() and event_filter_write() + to read i_private under this mutex and abort if it is NULL. + +This fixes nothing, but now we can change debugfs_remove("filter") +callers to nullify ->i_private and fix the the problem. + +Link: http://lkml.kernel.org/r/20130726172540.GA3619@redhat.com + +Reviewed-by: Masami Hiramatsu +Signed-off-by: Oleg Nesterov +Signed-off-by: Steven Rostedt +Signed-off-by: Greg Kroah-Hartman + + +--- + kernel/trace/trace_events.c | 26 +++++++++++++++++++------- + kernel/trace/trace_events_filter.c | 17 ++++++----------- + 2 files changed, 25 insertions(+), 18 deletions(-) + +--- a/kernel/trace/trace_events.c ++++ b/kernel/trace/trace_events.c +@@ -1002,21 +1002,28 @@ static ssize_t + event_filter_read(struct file *filp, char __user *ubuf, size_t cnt, + loff_t *ppos) + { +- struct ftrace_event_call *call = filp->private_data; ++ struct ftrace_event_call *call; + struct trace_seq *s; +- int r; ++ int r = -ENODEV; + + if (*ppos) + return 0; + + s = kmalloc(sizeof(*s), GFP_KERNEL); ++ + if (!s) + return -ENOMEM; + + trace_seq_init(s); + +- print_event_filter(call, s); +- r = simple_read_from_buffer(ubuf, cnt, ppos, s->buffer, s->len); ++ mutex_lock(&event_mutex); ++ call = event_file_data(filp); ++ if (call) ++ print_event_filter(call, s); ++ mutex_unlock(&event_mutex); ++ ++ if (call) ++ r = simple_read_from_buffer(ubuf, cnt, ppos, s->buffer, s->len); + + kfree(s); + +@@ -1027,9 +1034,9 @@ static ssize_t + event_filter_write(struct file *filp, const char __user *ubuf, size_t cnt, + loff_t *ppos) + { +- struct ftrace_event_call *call = filp->private_data; ++ struct ftrace_event_call *call; + char *buf; +- int err; ++ int err = -ENODEV; + + if (cnt >= PAGE_SIZE) + return -EINVAL; +@@ -1044,7 +1051,12 @@ event_filter_write(struct file *filp, co + } + buf[cnt] = '\0'; + +- err = apply_event_filter(call, buf); ++ mutex_lock(&event_mutex); ++ call = event_file_data(filp); ++ if (call) ++ err = apply_event_filter(call, buf); ++ mutex_unlock(&event_mutex); ++ + free_page((unsigned long) buf); + if (err < 0) + return err; +--- a/kernel/trace/trace_events_filter.c ++++ b/kernel/trace/trace_events_filter.c +@@ -631,17 +631,15 @@ static void append_filter_err(struct fil + free_page((unsigned long) buf); + } + ++/* caller must hold event_mutex */ + void print_event_filter(struct ftrace_event_call *call, struct trace_seq *s) + { +- struct event_filter *filter; ++ struct event_filter *filter = call->filter; + +- mutex_lock(&event_mutex); +- filter = call->filter; + if (filter && filter->filter_string) + trace_seq_printf(s, "%s\n", filter->filter_string); + else + trace_seq_printf(s, "none\n"); +- mutex_unlock(&event_mutex); + } + + void print_subsystem_event_filter(struct event_subsystem *system, +@@ -1835,23 +1833,22 @@ static int create_system_filter(struct e + return err; + } + ++/* caller must hold event_mutex */ + int apply_event_filter(struct ftrace_event_call *call, char *filter_string) + { + struct event_filter *filter; +- int err = 0; +- +- mutex_lock(&event_mutex); ++ int err; + + if (!strcmp(strstrip(filter_string), "0")) { + filter_disable(call); + filter = call->filter; + if (!filter) +- goto out_unlock; ++ return 0; + RCU_INIT_POINTER(call->filter, NULL); + /* Make sure the filter is not being used */ + synchronize_sched(); + __free_filter(filter); +- goto out_unlock; ++ return 0; + } + + err = create_filter(call, filter_string, true, &filter); +@@ -1878,8 +1875,6 @@ int apply_event_filter(struct ftrace_eve + __free_filter(tmp); + } + } +-out_unlock: +- mutex_unlock(&event_mutex); + + return err; + } diff --git a/queue-3.10/tracing-change-f_start-to-take-event_mutex-and-verify-i_private-null.patch b/queue-3.10/tracing-change-f_start-to-take-event_mutex-and-verify-i_private-null.patch new file mode 100644 index 00000000000..de1ac759c8b --- /dev/null +++ b/queue-3.10/tracing-change-f_start-to-take-event_mutex-and-verify-i_private-null.patch @@ -0,0 +1,90 @@ +From c5a44a1200c6eda2202434f25325e8ad19533fca Mon Sep 17 00:00:00 2001 +From: Oleg Nesterov +Date: Fri, 26 Jul 2013 19:25:43 +0200 +Subject: tracing: Change f_start() to take event_mutex and verify i_private != NULL + +From: Oleg Nesterov + +commit c5a44a1200c6eda2202434f25325e8ad19533fca upstream. + +trace_format_open() and trace_format_seq_ops are racy, nothing +protects ftrace_event_call from trace_remove_event_call(). + +Change f_start() to take event_mutex and verify i_private != NULL, +change f_stop() to drop this lock. + +This fixes nothing, but now we can change debugfs_remove("format") +callers to nullify ->i_private and fix the the problem. + +Note: the usage of event_mutex is sub-optimal but simple, we can +change this later. + +Link: http://lkml.kernel.org/r/20130726172543.GA3622@redhat.com + +Reviewed-by: Masami Hiramatsu +Signed-off-by: Oleg Nesterov +Signed-off-by: Steven Rostedt +Signed-off-by: Greg Kroah-Hartman + + +--- + kernel/trace/trace_events.c | 13 +++++++++---- + 1 file changed, 9 insertions(+), 4 deletions(-) + +--- a/kernel/trace/trace_events.c ++++ b/kernel/trace/trace_events.c +@@ -838,7 +838,7 @@ enum { + + static void *f_next(struct seq_file *m, void *v, loff_t *pos) + { +- struct ftrace_event_call *call = m->private; ++ struct ftrace_event_call *call = event_file_data(m->private); + struct ftrace_event_field *field; + struct list_head *common_head = &ftrace_common_fields; + struct list_head *head = trace_get_fields(call); +@@ -882,6 +882,11 @@ static void *f_start(struct seq_file *m, + loff_t l = 0; + void *p; + ++ /* ->stop() is called even if ->start() fails */ ++ mutex_lock(&event_mutex); ++ if (!event_file_data(m->private)) ++ return ERR_PTR(-ENODEV); ++ + /* Start by showing the header */ + if (!*pos) + return (void *)FORMAT_HEADER; +@@ -896,7 +901,7 @@ static void *f_start(struct seq_file *m, + + static int f_show(struct seq_file *m, void *v) + { +- struct ftrace_event_call *call = m->private; ++ struct ftrace_event_call *call = event_file_data(m->private); + struct ftrace_event_field *field; + const char *array_descriptor; + +@@ -947,6 +952,7 @@ static int f_show(struct seq_file *m, vo + + static void f_stop(struct seq_file *m, void *p) + { ++ mutex_unlock(&event_mutex); + } + + static const struct seq_operations trace_format_seq_ops = { +@@ -958,7 +964,6 @@ static const struct seq_operations trace + + static int trace_format_open(struct inode *inode, struct file *file) + { +- struct ftrace_event_call *call = inode->i_private; + struct seq_file *m; + int ret; + +@@ -967,7 +972,7 @@ static int trace_format_open(struct inod + return ret; + + m = file->private_data; +- m->private = call; ++ m->private = file; + + return 0; + } diff --git a/queue-3.10/tracing-introduce-remove_event_file_dir.patch b/queue-3.10/tracing-introduce-remove_event_file_dir.patch new file mode 100644 index 00000000000..db8a442fdc1 --- /dev/null +++ b/queue-3.10/tracing-introduce-remove_event_file_dir.patch @@ -0,0 +1,111 @@ +From f6a84bdc75b5c11621dec58db73fe102cbaf40cc Mon Sep 17 00:00:00 2001 +From: Oleg Nesterov +Date: Fri, 26 Jul 2013 19:25:47 +0200 +Subject: tracing: Introduce remove_event_file_dir() + +From: Oleg Nesterov + +commit f6a84bdc75b5c11621dec58db73fe102cbaf40cc upstream. + +Preparation for the next patch. Extract the common code from +remove_event_from_tracers() and __trace_remove_event_dirs() +into the new helper, remove_event_file_dir(). + +The patch looks more complicated than it actually is, it also +moves remove_subsystem() up to avoid the forward declaration. + +Link: http://lkml.kernel.org/r/20130726172547.GA3629@redhat.com + +Reviewed-by: Masami Hiramatsu +Signed-off-by: Oleg Nesterov +Signed-off-by: Steven Rostedt +Signed-off-by: Greg Kroah-Hartman + +--- + kernel/trace/trace_events.c | 47 +++++++++++++++++++++----------------------- + 1 file changed, 23 insertions(+), 24 deletions(-) + +--- a/kernel/trace/trace_events.c ++++ b/kernel/trace/trace_events.c +@@ -407,11 +407,31 @@ static void put_system(struct ftrace_sub + mutex_unlock(&event_mutex); + } + ++static void remove_subsystem(struct ftrace_subsystem_dir *dir) ++{ ++ if (!dir) ++ return; ++ ++ if (!--dir->nr_events) { ++ debugfs_remove_recursive(dir->entry); ++ list_del(&dir->list); ++ __put_system_dir(dir); ++ } ++} ++ + static void *event_file_data(struct file *filp) + { + return ACCESS_ONCE(file_inode(filp)->i_private); + } + ++static void remove_event_file_dir(struct ftrace_event_file *file) ++{ ++ list_del(&file->list); ++ debugfs_remove_recursive(file->dir); ++ remove_subsystem(file->system); ++ kmem_cache_free(file_cachep, file); ++} ++ + /* + * Open and update trace_array ref count. + * Must have the current trace_array passed to it. +@@ -1571,33 +1591,16 @@ event_create_dir(struct dentry *parent, + return 0; + } + +-static void remove_subsystem(struct ftrace_subsystem_dir *dir) +-{ +- if (!dir) +- return; +- +- if (!--dir->nr_events) { +- debugfs_remove_recursive(dir->entry); +- list_del(&dir->list); +- __put_system_dir(dir); +- } +-} +- + static void remove_event_from_tracers(struct ftrace_event_call *call) + { + struct ftrace_event_file *file; + struct trace_array *tr; + + do_for_each_event_file_safe(tr, file) { +- + if (file->event_call != call) + continue; + +- list_del(&file->list); +- debugfs_remove_recursive(file->dir); +- remove_subsystem(file->system); +- kmem_cache_free(file_cachep, file); +- ++ remove_event_file_dir(file); + /* + * The do_for_each_event_file_safe() is + * a double loop. After finding the call for this +@@ -2330,12 +2333,8 @@ __trace_remove_event_dirs(struct trace_a + { + struct ftrace_event_file *file, *next; + +- list_for_each_entry_safe(file, next, &tr->events, list) { +- list_del(&file->list); +- debugfs_remove_recursive(file->dir); +- remove_subsystem(file->system); +- kmem_cache_free(file_cachep, file); +- } ++ list_for_each_entry_safe(file, next, &tr->events, list) ++ remove_event_file_dir(file); + } + + static void