]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
3.10-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 22 Aug 2013 23:11:17 +0000 (16:11 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 22 Aug 2013 23:11:17 +0000 (16:11 -0700)
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

queue-3.10/series
queue-3.10/tracing-change-event_enable-disable_read-to-verify-i_private-null.patch [new file with mode: 0644]
queue-3.10/tracing-change-event_filter_read-write-to-verify-i_private-null.patch [new file with mode: 0644]
queue-3.10/tracing-change-f_start-to-take-event_mutex-and-verify-i_private-null.patch [new file with mode: 0644]
queue-3.10/tracing-introduce-remove_event_file_dir.patch [new file with mode: 0644]

index 4418fc4ab9f958ac8753188490a960fc9e685c91..565ecada592915e397c6c457a7123f71a7ce3dda 100644 (file)
@@ -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 (file)
index 0000000..fb06631
--- /dev/null
@@ -0,0 +1,88 @@
+From bc6f6b08dee5645770efb4b76186ded313f23752 Mon Sep 17 00:00:00 2001
+From: Oleg Nesterov <oleg@redhat.com>
+Date: Fri, 26 Jul 2013 19:25:36 +0200
+Subject: tracing: Change event_enable/disable_read() to verify i_private != NULL
+
+From: Oleg Nesterov <oleg@redhat.com>
+
+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 <masami.hiramatsu.pt@hitachi.com>
+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>
+
+
+---
+ 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 (file)
index 0000000..2de9689
--- /dev/null
@@ -0,0 +1,154 @@
+From e2912b091c26b8ea95e5e00a43a7ac620f6c94a6 Mon Sep 17 00:00:00 2001
+From: Oleg Nesterov <oleg@redhat.com>
+Date: Fri, 26 Jul 2013 19:25:40 +0200
+Subject: tracing: Change event_filter_read/write to verify i_private != NULL
+
+From: Oleg Nesterov <oleg@redhat.com>
+
+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 <masami.hiramatsu.pt@hitachi.com>
+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>
+
+
+---
+ 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 (file)
index 0000000..de1ac75
--- /dev/null
@@ -0,0 +1,90 @@
+From c5a44a1200c6eda2202434f25325e8ad19533fca Mon Sep 17 00:00:00 2001
+From: Oleg Nesterov <oleg@redhat.com>
+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 <oleg@redhat.com>
+
+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 <masami.hiramatsu.pt@hitachi.com>
+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>
+
+
+---
+ 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 (file)
index 0000000..db8a442
--- /dev/null
@@ -0,0 +1,111 @@
+From f6a84bdc75b5c11621dec58db73fe102cbaf40cc Mon Sep 17 00:00:00 2001
+From: Oleg Nesterov <oleg@redhat.com>
+Date: Fri, 26 Jul 2013 19:25:47 +0200
+Subject: tracing: Introduce remove_event_file_dir()
+
+From: Oleg Nesterov <oleg@redhat.com>
+
+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 <masami.hiramatsu.pt@hitachi.com>
+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>
+
+---
+ 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