From: Greg Kroah-Hartman Date: Thu, 22 Aug 2013 23:23:16 +0000 (-0700) Subject: 3.10-stable patches X-Git-Tag: v3.0.94~11 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=168022300b0e884ae483a325a041a379653fe0cd;p=thirdparty%2Fkernel%2Fstable-queue.git 3.10-stable patches 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 --- 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 index 00000000000..d7abc3bd4e9 --- /dev/null +++ b/queue-3.10/ftrace-check-module-functions-being-traced-on-reload.patch @@ -0,0 +1,240 @@ +From 8c4f3c3fa9681dc549cd35419b259496082fef8b Mon Sep 17 00:00:00 2001 +From: "Steven Rostedt (Red Hat)" +Date: Tue, 30 Jul 2013 00:04:32 -0400 +Subject: ftrace: Check module functions being traced on reload + +From: "Steven Rostedt (Red Hat)" + +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: + [] warn_slowpath_common+0x7f/0xc0 + [] warn_slowpath_null+0x1a/0x20 + [] __ftrace_hash_rec_update+0x1e3/0x230 + [] ftrace_hash_move+0x28/0x1d0 + [] ? kfree+0x2c/0x110 + [] ftrace_regex_release+0x8e/0x150 + [] __fput+0xae/0x220 + [] ____fput+0xe/0x10 + [] task_work_run+0x72/0x90 + [] do_notify_resume+0x6c/0xc0 + [] ? trace_hardirqs_on_thunk+0x3a/0x3c + [] 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 +Reported-by: Dave Jones +Reported-by: Steve Hodgson +Tested-by: Steve Hodgson +Signed-off-by: Steven Rostedt +Signed-off-by: Greg Kroah-Hartman + +--- + 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); diff --git a/queue-3.10/series b/queue-3.10/series index 3778680c733..e347ba8c098 100644 --- a/queue-3.10/series +++ b/queue-3.10/series @@ -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 index 00000000000..33409b8b7e5 --- /dev/null +++ b/queue-3.10/tracing-kprobes-fail-to-unregister-if-probe-event-files-are-in-use.patch @@ -0,0 +1,163 @@ +From 40c32592668b727cbfcf7b1c0567f581bd62a5e4 Mon Sep 17 00:00:00 2001 +From: "Steven Rostedt (Red Hat)" +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)" + +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: [] 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:[] [] 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: + [] ? security_file_open+0x2c/0x30 + [] ? unregister_trace_probe+0x4b/0x4b + [] do_dentry_open+0x162/0x226 + [] finish_open+0x46/0x54 + [] do_last+0x7f6/0x996 + [] ? inode_permission+0x42/0x44 + [] path_openat+0x232/0x496 + [] do_filp_open+0x3a/0x8a + [] ? __alloc_fd+0x168/0x17a + [] do_sys_open+0x70/0x102 + [] ? trace_hardirqs_on_caller+0x160/0x197 + [] SyS_open+0x1e/0x20 + [] 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 [] probes_open+0x3b/0xa7 + RSP +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 +Signed-off-by: Steven Rostedt +Signed-off-by: Greg Kroah-Hartman + +--- + 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 index 00000000000..958691d6b59 --- /dev/null +++ b/queue-3.10/tracing-trace_remove_event_call-should-fail-if-call-file-is-in-use.patch @@ -0,0 +1,102 @@ +From 2816c551c796ec14620325b2c9ed75b9979d3125 Mon Sep 17 00:00:00 2001 +From: Oleg Nesterov +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 + +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 +Signed-off-by: Oleg Nesterov +Signed-off-by: Steven Rostedt +Signed-off-by: Greg Kroah-Hartman + +--- + 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 index 00000000000..bf371fc6acf --- /dev/null +++ b/queue-3.10/tracing-uprobes-fail-to-unregister-if-probe-event-files-are-in-use.patch @@ -0,0 +1,160 @@ +From c6c2401d8bbaf9edc189b4c35a8cb2780b8b988e Mon Sep 17 00:00:00 2001 +From: "Steven Rostedt (Red Hat)" +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)" + +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 +Signed-off-by: Steven Rostedt +Signed-off-by: Greg Kroah-Hartman + +--- + 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 */