Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

s390/unwind: recover kretprobe modified return address in stacktrace

Based on commit cd9bc2c92588 ("arm64: Recover kretprobe modified return
address in stacktrace").

"""
Since the kretprobe replaces the function return address with
the __kretprobe_trampoline on the stack, stack unwinder shows it
instead of the correct return address.

This checks whether the next return address is the
__kretprobe_trampoline(), and if so, try to find the correct
return address from the kretprobe instance list.
"""

Original patch series:
https://lore.kernel.org/all/163163030719.489837.2236069935502195491.stgit@devnote2/

Reviewed-by: Tobias Huschle <huschle@linux.ibm.com>
Signed-off-by: Vasily Gorbik <gor@linux.ibm.com>

+15 -6
+13
arch/s390/include/asm/unwind.h
··· 4 4 5 5 #include <linux/sched.h> 6 6 #include <linux/ftrace.h> 7 + #include <linux/kprobes.h> 8 + #include <linux/llist.h> 7 9 #include <asm/ptrace.h> 8 10 #include <asm/stacktrace.h> 9 11 ··· 38 36 struct pt_regs *regs; 39 37 unsigned long sp, ip; 40 38 int graph_idx; 39 + struct llist_node *kr_cur; 41 40 bool reliable; 42 41 bool error; 43 42 }; 43 + 44 + /* Recover the return address modified by kretprobe and ftrace_graph. */ 45 + static inline unsigned long unwind_recover_ret_addr(struct unwind_state *state, 46 + unsigned long ip) 47 + { 48 + ip = ftrace_graph_ret_addr(state->task, &state->graph_idx, ip, NULL); 49 + if (is_kretprobe_trampoline(ip)) 50 + ip = kretprobe_find_ret_addr(state->task, (void *)state->sp, &state->kr_cur); 51 + return ip; 52 + } 44 53 45 54 void __unwind_start(struct unwind_state *state, struct task_struct *task, 46 55 struct pt_regs *regs, unsigned long first_frame);
+2 -6
arch/s390/kernel/unwind_bc.c
··· 103 103 if (sp & 0x7) 104 104 goto out_err; 105 105 106 - ip = ftrace_graph_ret_addr(state->task, &state->graph_idx, ip, (void *) sp); 107 - 108 106 /* Update unwind state */ 109 107 state->sp = sp; 110 - state->ip = ip; 111 108 state->regs = regs; 112 109 state->reliable = reliable; 110 + state->ip = unwind_recover_ret_addr(state, ip); 113 111 return true; 114 112 115 113 out_err: ··· 159 161 ip = READ_ONCE_NOCHECK(sf->gprs[8]); 160 162 } 161 163 162 - ip = ftrace_graph_ret_addr(state->task, &state->graph_idx, ip, NULL); 163 - 164 164 /* Update unwind state */ 165 165 state->sp = sp; 166 - state->ip = ip; 167 166 state->reliable = true; 167 + state->ip = unwind_recover_ret_addr(state, ip); 168 168 169 169 if (!first_frame) 170 170 return;