]> 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:23:16 +0000 (16:23 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 22 Aug 2013 23:23:16 +0000 (16:23 -0700)
added patches:
ftrace-check-module-functions-being-traced-on-reload.patch
tracing-kprobes-fail-to-unregister-if-probe-event-files-are-in-use.patch
tracing-trace_remove_event_call-should-fail-if-call-file-is-in-use.patch
tracing-uprobes-fail-to-unregister-if-probe-event-files-are-in-use.patch

queue-3.10/ftrace-check-module-functions-being-traced-on-reload.patch [new file with mode: 0644]
queue-3.10/series
queue-3.10/tracing-kprobes-fail-to-unregister-if-probe-event-files-are-in-use.patch [new file with mode: 0644]
queue-3.10/tracing-trace_remove_event_call-should-fail-if-call-file-is-in-use.patch [new file with mode: 0644]
queue-3.10/tracing-uprobes-fail-to-unregister-if-probe-event-files-are-in-use.patch [new file with mode: 0644]

diff --git a/queue-3.10/ftrace-check-module-functions-being-traced-on-reload.patch b/queue-3.10/ftrace-check-module-functions-being-traced-on-reload.patch
new file mode 100644 (file)
index 0000000..d7abc3b
--- /dev/null
@@ -0,0 +1,240 @@
+From 8c4f3c3fa9681dc549cd35419b259496082fef8b Mon Sep 17 00:00:00 2001
+From: "Steven Rostedt (Red Hat)" <rostedt@goodmis.org>
+Date: Tue, 30 Jul 2013 00:04:32 -0400
+Subject: ftrace: Check module functions being traced on reload
+
+From: "Steven Rostedt (Red Hat)" <rostedt@goodmis.org>
+
+commit 8c4f3c3fa9681dc549cd35419b259496082fef8b upstream.
+
+There's been a nasty bug that would show up and not give much info.
+The bug displayed the following warning:
+
+ WARNING: at kernel/trace/ftrace.c:1529 __ftrace_hash_rec_update+0x1e3/0x230()
+ Pid: 20903, comm: bash Tainted: G           O 3.6.11+ #38405.trunk
+ Call Trace:
+  [<ffffffff8103e5ff>] warn_slowpath_common+0x7f/0xc0
+  [<ffffffff8103e65a>] warn_slowpath_null+0x1a/0x20
+  [<ffffffff810c2ee3>] __ftrace_hash_rec_update+0x1e3/0x230
+  [<ffffffff810c4f28>] ftrace_hash_move+0x28/0x1d0
+  [<ffffffff811401cc>] ? kfree+0x2c/0x110
+  [<ffffffff810c68ee>] ftrace_regex_release+0x8e/0x150
+  [<ffffffff81149f1e>] __fput+0xae/0x220
+  [<ffffffff8114a09e>] ____fput+0xe/0x10
+  [<ffffffff8105fa22>] task_work_run+0x72/0x90
+  [<ffffffff810028ec>] do_notify_resume+0x6c/0xc0
+  [<ffffffff8126596e>] ? trace_hardirqs_on_thunk+0x3a/0x3c
+  [<ffffffff815c0f88>] int_signal+0x12/0x17
+ ---[ end trace 793179526ee09b2c ]---
+
+It was finally narrowed down to unloading a module that was being traced.
+
+It was actually more than that. When functions are being traced, there's
+a table of all functions that have a ref count of the number of active
+tracers attached to that function. When a function trace callback is
+registered to a function, the function's record ref count is incremented.
+When it is unregistered, the function's record ref count is decremented.
+If an inconsistency is detected (ref count goes below zero) the above
+warning is shown and the function tracing is permanently disabled until
+reboot.
+
+The ftrace callback ops holds a hash of functions that it filters on
+(and/or filters off). If the hash is empty, the default means to filter
+all functions (for the filter_hash) or to disable no functions (for the
+notrace_hash).
+
+When a module is unloaded, it frees the function records that represent
+the module functions. These records exist on their own pages, that is
+function records for one module will not exist on the same page as
+function records for other modules or even the core kernel.
+
+Now when a module unloads, the records that represents its functions are
+freed. When the module is loaded again, the records are recreated with
+a default ref count of zero (unless there's a callback that traces all
+functions, then they will also be traced, and the ref count will be
+incremented).
+
+The problem is that if an ftrace callback hash includes functions of the
+module being unloaded, those hash entries will not be removed. If the
+module is reloaded in the same location, the hash entries still point
+to the functions of the module but the module's ref counts do not reflect
+that.
+
+With the help of Steve and Joern, we found a reproducer:
+
+ Using uinput module and uinput_release function.
+
+ cd /sys/kernel/debug/tracing
+ modprobe uinput
+ echo uinput_release > set_ftrace_filter
+ echo function > current_tracer
+ rmmod uinput
+ modprobe uinput
+ # check /proc/modules to see if loaded in same addr, otherwise try again
+ echo nop > current_tracer
+
+ [BOOM]
+
+The above loads the uinput module, which creates a table of functions that
+can be traced within the module.
+
+We add uinput_release to the filter_hash to trace just that function.
+
+Enable function tracincg, which increments the ref count of the record
+associated to uinput_release.
+
+Remove uinput, which frees the records including the one that represents
+uinput_release.
+
+Load the uinput module again (and make sure it's at the same address).
+This recreates the function records all with a ref count of zero,
+including uinput_release.
+
+Disable function tracing, which will decrement the ref count for uinput_release
+which is now zero because of the module removal and reload, and we have
+a mismatch (below zero ref count).
+
+The solution is to check all currently tracing ftrace callbacks to see if any
+are tracing any of the module's functions when a module is loaded (it already does
+that with callbacks that trace all functions). If a callback happens to have
+a module function being traced, it increments that records ref count and starts
+tracing that function.
+
+There may be a strange side effect with this, where tracing module functions
+on unload and then reloading a new module may have that new module's functions
+being traced. This may be something that confuses the user, but it's not
+a big deal. Another approach is to disable all callback hashes on module unload,
+but this leaves some ftrace callbacks that may not be registered, but can
+still have hashes tracing the module's function where ftrace doesn't know about
+it. That situation can cause the same bug. This solution solves that case too.
+Another benefit of this solution, is it is possible to trace a module's
+function on unload and load.
+
+Link: http://lkml.kernel.org/r/20130705142629.GA325@redhat.com
+
+Reported-by: Jörn Engel <joern@logfs.org>
+Reported-by: Dave Jones <davej@redhat.com>
+Reported-by: Steve Hodgson <steve@purestorage.com>
+Tested-by: Steve Hodgson <steve@purestorage.com>
+Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ kernel/trace/ftrace.c |   71 +++++++++++++++++++++++++++++++++++++++++++-------
+ 1 file changed, 62 insertions(+), 9 deletions(-)
+
+--- a/kernel/trace/ftrace.c
++++ b/kernel/trace/ftrace.c
+@@ -2144,12 +2144,57 @@ static cycle_t         ftrace_update_time;
+ static unsigned long  ftrace_update_cnt;
+ unsigned long         ftrace_update_tot_cnt;
+-static int ops_traces_mod(struct ftrace_ops *ops)
++static inline int ops_traces_mod(struct ftrace_ops *ops)
+ {
+-      struct ftrace_hash *hash;
++      /*
++       * Filter_hash being empty will default to trace module.
++       * But notrace hash requires a test of individual module functions.
++       */
++      return ftrace_hash_empty(ops->filter_hash) &&
++              ftrace_hash_empty(ops->notrace_hash);
++}
++
++/*
++ * Check if the current ops references the record.
++ *
++ * If the ops traces all functions, then it was already accounted for.
++ * If the ops does not trace the current record function, skip it.
++ * If the ops ignores the function via notrace filter, skip it.
++ */
++static inline bool
++ops_references_rec(struct ftrace_ops *ops, struct dyn_ftrace *rec)
++{
++      /* If ops isn't enabled, ignore it */
++      if (!(ops->flags & FTRACE_OPS_FL_ENABLED))
++              return 0;
++
++      /* If ops traces all mods, we already accounted for it */
++      if (ops_traces_mod(ops))
++              return 0;
++
++      /* The function must be in the filter */
++      if (!ftrace_hash_empty(ops->filter_hash) &&
++          !ftrace_lookup_ip(ops->filter_hash, rec->ip))
++              return 0;
++
++      /* If in notrace hash, we ignore it too */
++      if (ftrace_lookup_ip(ops->notrace_hash, rec->ip))
++              return 0;
++
++      return 1;
++}
++
++static int referenced_filters(struct dyn_ftrace *rec)
++{
++      struct ftrace_ops *ops;
++      int cnt = 0;
+-      hash = ops->filter_hash;
+-      return ftrace_hash_empty(hash);
++      for (ops = ftrace_ops_list; ops != &ftrace_list_end; ops = ops->next) {
++              if (ops_references_rec(ops, rec))
++                  cnt++;
++      }
++
++      return cnt;
+ }
+ static int ftrace_update_code(struct module *mod)
+@@ -2158,6 +2203,7 @@ static int ftrace_update_code(struct mod
+       struct dyn_ftrace *p;
+       cycle_t start, stop;
+       unsigned long ref = 0;
++      bool test = false;
+       int i;
+       /*
+@@ -2171,9 +2217,12 @@ static int ftrace_update_code(struct mod
+               for (ops = ftrace_ops_list;
+                    ops != &ftrace_list_end; ops = ops->next) {
+-                      if (ops->flags & FTRACE_OPS_FL_ENABLED &&
+-                          ops_traces_mod(ops))
+-                              ref++;
++                      if (ops->flags & FTRACE_OPS_FL_ENABLED) {
++                              if (ops_traces_mod(ops))
++                                      ref++;
++                              else
++                                      test = true;
++                      }
+               }
+       }
+@@ -2183,12 +2232,16 @@ static int ftrace_update_code(struct mod
+       for (pg = ftrace_new_pgs; pg; pg = pg->next) {
+               for (i = 0; i < pg->index; i++) {
++                      int cnt = ref;
++
+                       /* If something went wrong, bail without enabling anything */
+                       if (unlikely(ftrace_disabled))
+                               return -1;
+                       p = &pg->records[i];
+-                      p->flags = ref;
++                      if (test)
++                              cnt += referenced_filters(p);
++                      p->flags = cnt;
+                       /*
+                        * Do the initial record conversion from mcount jump
+@@ -2208,7 +2261,7 @@ static int ftrace_update_code(struct mod
+                        * conversion puts the module to the correct state, thus
+                        * passing the ftrace_make_call check.
+                        */
+-                      if (ftrace_start_up && ref) {
++                      if (ftrace_start_up && cnt) {
+                               int failed = __ftrace_replace_code(p, 1);
+                               if (failed)
+                                       ftrace_bug(failed, p->ip);
index 3778680c733d8600c5683d8e95cb1ba2b7f03dd0..e347ba8c0989a841100657cbde65ffad3de31e80 100644 (file)
@@ -29,3 +29,7 @@ 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
 tracing-change-remove_event_file_dir-to-clear-d_subdirs-i_private.patch
+tracing-trace_remove_event_call-should-fail-if-call-file-is-in-use.patch
+tracing-kprobes-fail-to-unregister-if-probe-event-files-are-in-use.patch
+tracing-uprobes-fail-to-unregister-if-probe-event-files-are-in-use.patch
+ftrace-check-module-functions-being-traced-on-reload.patch
diff --git a/queue-3.10/tracing-kprobes-fail-to-unregister-if-probe-event-files-are-in-use.patch b/queue-3.10/tracing-kprobes-fail-to-unregister-if-probe-event-files-are-in-use.patch
new file mode 100644 (file)
index 0000000..33409b8
--- /dev/null
@@ -0,0 +1,163 @@
+From 40c32592668b727cbfcf7b1c0567f581bd62a5e4 Mon Sep 17 00:00:00 2001
+From: "Steven Rostedt (Red Hat)" <rostedt@goodmis.org>
+Date: Wed, 3 Jul 2013 23:33:50 -0400
+Subject: tracing/kprobes: Fail to unregister if probe event files are in use
+
+From: "Steven Rostedt (Red Hat)" <rostedt@goodmis.org>
+
+commit 40c32592668b727cbfcf7b1c0567f581bd62a5e4 upstream.
+
+When a probe is being removed, it cleans up the event files that correspond
+to the probe. But there is a race between writing to one of these files
+and deleting the probe. This is especially true for the "enable" file.
+
+       CPU 0                           CPU 1
+       -----                           -----
+
+                                 fd = open("enable",O_WRONLY);
+
+  probes_open()
+  release_all_trace_probes()
+  unregister_trace_probe()
+  if (trace_probe_is_enabled(tp))
+       return -EBUSY
+
+                                  write(fd, "1", 1)
+                                  __ftrace_set_clr_event()
+                                  call->class->reg()
+                                   (kprobe_register)
+                                    enable_trace_probe(tp)
+
+  __unregister_trace_probe(tp);
+  list_del(&tp->list)
+  unregister_probe_event(tp) <-- fails!
+  free_trace_probe(tp)
+
+                                  write(fd, "0", 1)
+                                  __ftrace_set_clr_event()
+                                  call->class->unreg
+                                   (kprobe_register)
+                                   disable_trace_probe(tp) <-- BOOM!
+
+A test program was written that used two threads to simulate the
+above scenario adding a nanosleep() interval to change the timings
+and after several thousand runs, it was able to trigger this bug
+and crash:
+
+BUG: unable to handle kernel paging request at 00000005000000f9
+IP: [<ffffffff810dee70>] probes_open+0x3b/0xa7
+PGD 7808a067 PUD 0
+Oops: 0000 [#1] PREEMPT SMP
+Dumping ftrace buffer:
+---------------------------------
+Modules linked in: ipt_MASQUERADE sunrpc ip6t_REJECT nf_conntrack_ipv6
+CPU: 1 PID: 2070 Comm: test-kprobe-rem Not tainted 3.11.0-rc3-test+ #47
+Hardware name: To Be Filled By O.E.M. To Be Filled By O.E.M./To be filled by O.E.M., BIOS SDBLI944.86P 05/08/2007
+task: ffff880077756440 ti: ffff880076e52000 task.ti: ffff880076e52000
+RIP: 0010:[<ffffffff810dee70>]  [<ffffffff810dee70>] probes_open+0x3b/0xa7
+RSP: 0018:ffff880076e53c38  EFLAGS: 00010203
+RAX: 0000000500000001 RBX: ffff88007844f440 RCX: 0000000000000003
+RDX: 0000000000000003 RSI: 0000000000000003 RDI: ffff880076e52000
+RBP: ffff880076e53c58 R08: ffff880076e53bd8 R09: 0000000000000000
+R10: ffff880077756440 R11: 0000000000000006 R12: ffffffff810dee35
+R13: ffff880079250418 R14: 0000000000000000 R15: ffff88007844f450
+FS:  00007f87a276f700(0000) GS:ffff88007d480000(0000) knlGS:0000000000000000
+CS:  0010 DS: 0000 ES: 0000 CR0: 000000008005003b
+CR2: 00000005000000f9 CR3: 0000000077262000 CR4: 00000000000007e0
+Stack:
+ ffff880076e53c58 ffffffff81219ea0 ffff88007844f440 ffffffff810dee35
+ ffff880076e53ca8 ffffffff81130f78 ffff8800772986c0 ffff8800796f93a0
+ ffffffff81d1b5d8 ffff880076e53e04 0000000000000000 ffff88007844f440
+Call Trace:
+ [<ffffffff81219ea0>] ? security_file_open+0x2c/0x30
+ [<ffffffff810dee35>] ? unregister_trace_probe+0x4b/0x4b
+ [<ffffffff81130f78>] do_dentry_open+0x162/0x226
+ [<ffffffff81131186>] finish_open+0x46/0x54
+ [<ffffffff8113f30b>] do_last+0x7f6/0x996
+ [<ffffffff8113cc6f>] ? inode_permission+0x42/0x44
+ [<ffffffff8113f6dd>] path_openat+0x232/0x496
+ [<ffffffff8113fc30>] do_filp_open+0x3a/0x8a
+ [<ffffffff8114ab32>] ? __alloc_fd+0x168/0x17a
+ [<ffffffff81131f4e>] do_sys_open+0x70/0x102
+ [<ffffffff8108f06e>] ? trace_hardirqs_on_caller+0x160/0x197
+ [<ffffffff81131ffe>] SyS_open+0x1e/0x20
+ [<ffffffff81522742>] system_call_fastpath+0x16/0x1b
+Code: e5 41 54 53 48 89 f3 48 83 ec 10 48 23 56 78 48 39 c2 75 6c 31 f6 48 c7
+RIP  [<ffffffff810dee70>] probes_open+0x3b/0xa7
+ RSP <ffff880076e53c38>
+CR2: 00000005000000f9
+---[ end trace 35f17d68fc569897 ]---
+
+The unregister_trace_probe() must be done first, and if it fails it must
+fail the removal of the kprobe.
+
+Several changes have already been made by Oleg Nesterov and Masami Hiramatsu
+to allow moving the unregister_probe_event() before the removal of
+the probe and exit the function if it fails. This prevents the tp
+structure from being used after it is freed.
+
+Link: http://lkml.kernel.org/r/20130704034038.819592356@goodmis.org
+
+Acked-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
+Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ kernel/trace/trace_kprobe.c |   21 +++++++++++++++------
+ 1 file changed, 15 insertions(+), 6 deletions(-)
+
+--- a/kernel/trace/trace_kprobe.c
++++ b/kernel/trace/trace_kprobe.c
+@@ -90,7 +90,7 @@ static __kprobes bool trace_probe_is_on_
+ }
+ static int register_probe_event(struct trace_probe *tp);
+-static void unregister_probe_event(struct trace_probe *tp);
++static int unregister_probe_event(struct trace_probe *tp);
+ static DEFINE_MUTEX(probe_lock);
+ static LIST_HEAD(probe_list);
+@@ -411,9 +411,12 @@ static int unregister_trace_probe(struct
+       if (trace_probe_is_enabled(tp))
+               return -EBUSY;
++      /* Will fail if probe is being used by ftrace or perf */
++      if (unregister_probe_event(tp))
++              return -EBUSY;
++
+       __unregister_trace_probe(tp);
+       list_del(&tp->list);
+-      unregister_probe_event(tp);
+       return 0;
+ }
+@@ -692,7 +695,9 @@ static int release_all_trace_probes(void
+       /* TODO: Use batch unregistration */
+       while (!list_empty(&probe_list)) {
+               tp = list_entry(probe_list.next, struct trace_probe, list);
+-              unregister_trace_probe(tp);
++              ret = unregister_trace_probe(tp);
++              if (ret)
++                      goto end;
+               free_trace_probe(tp);
+       }
+@@ -1325,11 +1330,15 @@ static int register_probe_event(struct t
+       return ret;
+ }
+-static void unregister_probe_event(struct trace_probe *tp)
++static int unregister_probe_event(struct trace_probe *tp)
+ {
++      int ret;
++
+       /* tp->event is unregistered in trace_remove_event_call() */
+-      trace_remove_event_call(&tp->call);
+-      kfree(tp->call.print_fmt);
++      ret = trace_remove_event_call(&tp->call);
++      if (!ret)
++              kfree(tp->call.print_fmt);
++      return ret;
+ }
+ /* Make a debugfs interface for controlling probe points */
diff --git a/queue-3.10/tracing-trace_remove_event_call-should-fail-if-call-file-is-in-use.patch b/queue-3.10/tracing-trace_remove_event_call-should-fail-if-call-file-is-in-use.patch
new file mode 100644 (file)
index 0000000..958691d
--- /dev/null
@@ -0,0 +1,102 @@
+From 2816c551c796ec14620325b2c9ed75b9979d3125 Mon Sep 17 00:00:00 2001
+From: Oleg Nesterov <oleg@redhat.com>
+Date: Mon, 29 Jul 2013 19:50:33 +0200
+Subject: tracing: trace_remove_event_call() should fail if call/file is in use
+
+From: Oleg Nesterov <oleg@redhat.com>
+
+commit 2816c551c796ec14620325b2c9ed75b9979d3125 upstream.
+
+Change trace_remove_event_call(call) to return the error if this
+call is active. This is what the callers assume but can't verify
+outside of the tracing locks. Both trace_kprobe.c/trace_uprobe.c
+need the additional changes, unregister_trace_probe() should abort
+if trace_remove_event_call() fails.
+
+The caller is going to free this call/file so we must ensure that
+nobody can use them after trace_remove_event_call() succeeds.
+debugfs should be fine after the previous changes and event_remove()
+does TRACE_REG_UNREGISTER, but still there are 2 reasons why we need
+the additional checks:
+
+- There could be a perf_event(s) attached to this tp_event, so the
+  patch checks ->perf_refcount.
+
+- TRACE_REG_UNREGISTER can be suppressed by FTRACE_EVENT_FL_SOFT_MODE,
+  so we simply check FTRACE_EVENT_FL_ENABLED protected by event_mutex.
+
+Link: http://lkml.kernel.org/r/20130729175033.GB26284@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>
+
+---
+ include/linux/ftrace_event.h |    2 +-
+ kernel/trace/trace_events.c  |   35 +++++++++++++++++++++++++++++++++--
+ 2 files changed, 34 insertions(+), 3 deletions(-)
+
+--- a/include/linux/ftrace_event.h
++++ b/include/linux/ftrace_event.h
+@@ -334,7 +334,7 @@ extern int trace_define_field(struct ftr
+                             const char *name, int offset, int size,
+                             int is_signed, int filter_type);
+ extern int trace_add_event_call(struct ftrace_event_call *call);
+-extern void trace_remove_event_call(struct ftrace_event_call *call);
++extern int trace_remove_event_call(struct ftrace_event_call *call);
+ #define is_signed_type(type)  (((type)(-1)) < (type)1)
+--- a/kernel/trace/trace_events.c
++++ b/kernel/trace/trace_events.c
+@@ -1735,16 +1735,47 @@ static void __trace_remove_event_call(st
+       destroy_preds(call);
+ }
++static int probe_remove_event_call(struct ftrace_event_call *call)
++{
++      struct trace_array *tr;
++      struct ftrace_event_file *file;
++
++#ifdef CONFIG_PERF_EVENTS
++      if (call->perf_refcount)
++              return -EBUSY;
++#endif
++      do_for_each_event_file(tr, file) {
++              if (file->event_call != call)
++                      continue;
++              /*
++               * We can't rely on ftrace_event_enable_disable(enable => 0)
++               * we are going to do, FTRACE_EVENT_FL_SOFT_MODE can suppress
++               * TRACE_REG_UNREGISTER.
++               */
++              if (file->flags & FTRACE_EVENT_FL_ENABLED)
++                      return -EBUSY;
++              break;
++      } while_for_each_event_file();
++
++      __trace_remove_event_call(call);
++
++      return 0;
++}
++
+ /* Remove an event_call */
+-void trace_remove_event_call(struct ftrace_event_call *call)
++int trace_remove_event_call(struct ftrace_event_call *call)
+ {
++      int ret;
++
+       mutex_lock(&trace_types_lock);
+       mutex_lock(&event_mutex);
+       down_write(&trace_event_sem);
+-      __trace_remove_event_call(call);
++      ret = probe_remove_event_call(call);
+       up_write(&trace_event_sem);
+       mutex_unlock(&event_mutex);
+       mutex_unlock(&trace_types_lock);
++
++      return ret;
+ }
+ #define for_each_event(event, start, end)                     \
diff --git a/queue-3.10/tracing-uprobes-fail-to-unregister-if-probe-event-files-are-in-use.patch b/queue-3.10/tracing-uprobes-fail-to-unregister-if-probe-event-files-are-in-use.patch
new file mode 100644 (file)
index 0000000..bf371fc
--- /dev/null
@@ -0,0 +1,160 @@
+From c6c2401d8bbaf9edc189b4c35a8cb2780b8b988e Mon Sep 17 00:00:00 2001
+From: "Steven Rostedt (Red Hat)" <rostedt@goodmis.org>
+Date: Wed, 3 Jul 2013 23:33:51 -0400
+Subject: tracing/uprobes: Fail to unregister if probe event files are in use
+
+From: "Steven Rostedt (Red Hat)" <rostedt@goodmis.org>
+
+commit c6c2401d8bbaf9edc189b4c35a8cb2780b8b988e upstream.
+
+Uprobes suffer the same problem that kprobes have. There's a race between
+writing to the "enable" file and removing the probe. The probe checks for
+it being in use and if it is not, goes about deleting the probe and the
+event that represents it. But the problem with that is, after it checks
+if it is in use it can be enabled, and the deletion of the event (access
+to the probe) will fail, as it is in use. But the uprobe will still be
+deleted. This is a problem as the event can reference the uprobe that
+was deleted.
+
+The fix is to remove the event first, and check to make sure the event
+removal succeeds. Then it is safe to remove the probe.
+
+When the event exists, either ftrace or perf can enable the probe and
+prevent the event from being removed.
+
+Link: http://lkml.kernel.org/r/20130704034038.991525256@goodmis.org
+
+Acked-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_uprobe.c |   51 ++++++++++++++++++++++++++++++++------------
+ 1 file changed, 38 insertions(+), 13 deletions(-)
+
+--- a/kernel/trace/trace_uprobe.c
++++ b/kernel/trace/trace_uprobe.c
+@@ -70,7 +70,7 @@ struct trace_uprobe {
+       (sizeof(struct probe_arg) * (n)))
+ static int register_uprobe_event(struct trace_uprobe *tu);
+-static void unregister_uprobe_event(struct trace_uprobe *tu);
++static int unregister_uprobe_event(struct trace_uprobe *tu);
+ static DEFINE_MUTEX(uprobe_lock);
+ static LIST_HEAD(uprobe_list);
+@@ -164,11 +164,17 @@ static struct trace_uprobe *find_probe_e
+ }
+ /* Unregister a trace_uprobe and probe_event: call with locking uprobe_lock */
+-static void unregister_trace_uprobe(struct trace_uprobe *tu)
++static int unregister_trace_uprobe(struct trace_uprobe *tu)
+ {
++      int ret;
++
++      ret = unregister_uprobe_event(tu);
++      if (ret)
++              return ret;
++
+       list_del(&tu->list);
+-      unregister_uprobe_event(tu);
+       free_trace_uprobe(tu);
++      return 0;
+ }
+ /* Register a trace_uprobe and probe_event */
+@@ -181,9 +187,12 @@ static int register_trace_uprobe(struct
+       /* register as an event */
+       old_tp = find_probe_event(tu->call.name, tu->call.class->system);
+-      if (old_tp)
++      if (old_tp) {
+               /* delete old event */
+-              unregister_trace_uprobe(old_tp);
++              ret = unregister_trace_uprobe(old_tp);
++              if (ret)
++                      goto end;
++      }
+       ret = register_uprobe_event(tu);
+       if (ret) {
+@@ -256,6 +265,8 @@ static int create_trace_uprobe(int argc,
+               group = UPROBE_EVENT_SYSTEM;
+       if (is_delete) {
++              int ret;
++
+               if (!event) {
+                       pr_info("Delete command needs an event name.\n");
+                       return -EINVAL;
+@@ -269,9 +280,9 @@ static int create_trace_uprobe(int argc,
+                       return -ENOENT;
+               }
+               /* delete an event */
+-              unregister_trace_uprobe(tu);
++              ret = unregister_trace_uprobe(tu);
+               mutex_unlock(&uprobe_lock);
+-              return 0;
++              return ret;
+       }
+       if (argc < 2) {
+@@ -408,16 +419,20 @@ fail_address_parse:
+       return ret;
+ }
+-static void cleanup_all_probes(void)
++static int cleanup_all_probes(void)
+ {
+       struct trace_uprobe *tu;
++      int ret = 0;
+       mutex_lock(&uprobe_lock);
+       while (!list_empty(&uprobe_list)) {
+               tu = list_entry(uprobe_list.next, struct trace_uprobe, list);
+-              unregister_trace_uprobe(tu);
++              ret = unregister_trace_uprobe(tu);
++              if (ret)
++                      break;
+       }
+       mutex_unlock(&uprobe_lock);
++      return ret;
+ }
+ /* Probes listing interfaces */
+@@ -462,8 +477,13 @@ static const struct seq_operations probe
+ static int probes_open(struct inode *inode, struct file *file)
+ {
+-      if ((file->f_mode & FMODE_WRITE) && (file->f_flags & O_TRUNC))
+-              cleanup_all_probes();
++      int ret;
++
++      if ((file->f_mode & FMODE_WRITE) && (file->f_flags & O_TRUNC)) {
++              ret = cleanup_all_probes();
++              if (ret)
++                      return ret;
++      }
+       return seq_open(file, &probes_seq_op);
+ }
+@@ -970,12 +990,17 @@ static int register_uprobe_event(struct
+       return ret;
+ }
+-static void unregister_uprobe_event(struct trace_uprobe *tu)
++static int unregister_uprobe_event(struct trace_uprobe *tu)
+ {
++      int ret;
++
+       /* tu->event is unregistered in trace_remove_event_call() */
+-      trace_remove_event_call(&tu->call);
++      ret = trace_remove_event_call(&tu->call);
++      if (ret)
++              return ret;
+       kfree(tu->call.print_fmt);
+       tu->call.print_fmt = NULL;
++      return 0;
+ }
+ /* Make a trace interface for controling probe points */