| From foo@baz Thu Dec 21 09:02:40 CET 2017 |
| From: Masami Hiramatsu <mhiramat@kernel.org> |
| Date: Tue, 14 Feb 2017 00:05:59 +0900 |
| Subject: arm: kprobes: Fix the return address of multiple kretprobes |
| |
| From: Masami Hiramatsu <mhiramat@kernel.org> |
| |
| |
| [ Upstream commit 06553175f585b52509c7df37d6f4a50aacb7b211 ] |
| |
| This is arm port of commit 737480a0d525 ("kprobes/x86: |
| Fix the return address of multiple kretprobes"). |
| |
| Fix the return address of subsequent kretprobes when multiple |
| kretprobes are set on the same function. |
| |
| For example: |
| |
| # cd /sys/kernel/debug/tracing |
| # echo "r:event1 sys_symlink" > kprobe_events |
| # echo "r:event2 sys_symlink" >> kprobe_events |
| # echo 1 > events/kprobes/enable |
| # ln -s /tmp/foo /tmp/bar |
| |
| (without this patch) |
| |
| # cat trace | grep -v ^# |
| ln-82 [000] dn.2 68.446525: event1: (kretprobe_trampoline+0x0/0x18 <- SyS_symlink) |
| ln-82 [000] dn.2 68.447831: event2: (ret_fast_syscall+0x0/0x1c <- SyS_symlink) |
| |
| (with this patch) |
| |
| # cat trace | grep -v ^# |
| ln-81 [000] dn.1 39.463469: event1: (ret_fast_syscall+0x0/0x1c <- SyS_symlink) |
| ln-81 [000] dn.1 39.464701: event2: (ret_fast_syscall+0x0/0x1c <- SyS_symlink) |
| |
| Signed-off-by: Masami Hiramatsu <mhiramat@kernel.org> |
| Cc: KUMANO Syuhei <kumano.prog@gmail.com> |
| Signed-off-by: Jon Medhurst <tixy@linaro.org> |
| Signed-off-by: Sasha Levin <alexander.levin@verizon.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| arch/arm/probes/kprobes/core.c | 24 ++++++++++++++++++++++-- |
| 1 file changed, 22 insertions(+), 2 deletions(-) |
| |
| --- a/arch/arm/probes/kprobes/core.c |
| +++ b/arch/arm/probes/kprobes/core.c |
| @@ -433,6 +433,7 @@ static __used __kprobes void *trampoline |
| struct hlist_node *tmp; |
| unsigned long flags, orig_ret_address = 0; |
| unsigned long trampoline_address = (unsigned long)&kretprobe_trampoline; |
| + kprobe_opcode_t *correct_ret_addr = NULL; |
| |
| INIT_HLIST_HEAD(&empty_rp); |
| kretprobe_hash_lock(current, &head, &flags); |
| @@ -455,14 +456,34 @@ static __used __kprobes void *trampoline |
| /* another task is sharing our hash bucket */ |
| continue; |
| |
| + orig_ret_address = (unsigned long)ri->ret_addr; |
| + |
| + if (orig_ret_address != trampoline_address) |
| + /* |
| + * This is the real return address. Any other |
| + * instances associated with this task are for |
| + * other calls deeper on the call stack |
| + */ |
| + break; |
| + } |
| + |
| + kretprobe_assert(ri, orig_ret_address, trampoline_address); |
| + |
| + correct_ret_addr = ri->ret_addr; |
| + hlist_for_each_entry_safe(ri, tmp, head, hlist) { |
| + if (ri->task != current) |
| + /* another task is sharing our hash bucket */ |
| + continue; |
| + |
| + orig_ret_address = (unsigned long)ri->ret_addr; |
| if (ri->rp && ri->rp->handler) { |
| __this_cpu_write(current_kprobe, &ri->rp->kp); |
| get_kprobe_ctlblk()->kprobe_status = KPROBE_HIT_ACTIVE; |
| + ri->ret_addr = correct_ret_addr; |
| ri->rp->handler(ri, regs); |
| __this_cpu_write(current_kprobe, NULL); |
| } |
| |
| - orig_ret_address = (unsigned long)ri->ret_addr; |
| recycle_rp_inst(ri, &empty_rp); |
| |
| if (orig_ret_address != trampoline_address) |
| @@ -474,7 +495,6 @@ static __used __kprobes void *trampoline |
| break; |
| } |
| |
| - kretprobe_assert(ri, orig_ret_address, trampoline_address); |
| kretprobe_hash_unlock(current, &flags); |
| |
| hlist_for_each_entry_safe(ri, tmp, &empty_rp, hlist) { |