Commit d81675b6 authored by Vasily Gorbik's avatar Vasily Gorbik
Browse files

s390/unwind: recover kretprobe modified return address in stacktrace

Based on commit cd9bc2c9 ("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: default avatarTobias Huschle <huschle@linux.ibm.com>
Signed-off-by: default avatarVasily Gorbik <gor@linux.ibm.com>
parent 09bc20c8
Loading
Loading
Loading
Loading
+13 −0
Original line number Diff line number Diff line
@@ -4,6 +4,8 @@

#include <linux/sched.h>
#include <linux/ftrace.h>
#include <linux/kprobes.h>
#include <linux/llist.h>
#include <asm/ptrace.h>
#include <asm/stacktrace.h>

@@ -36,10 +38,21 @@ struct unwind_state {
	struct pt_regs *regs;
	unsigned long sp, ip;
	int graph_idx;
	struct llist_node *kr_cur;
	bool reliable;
	bool error;
};

/* Recover the return address modified by kretprobe and ftrace_graph. */
static inline unsigned long unwind_recover_ret_addr(struct unwind_state *state,
						    unsigned long ip)
{
	ip = ftrace_graph_ret_addr(state->task, &state->graph_idx, ip, NULL);
	if (is_kretprobe_trampoline(ip))
		ip = kretprobe_find_ret_addr(state->task, (void *)state->sp, &state->kr_cur);
	return ip;
}

void __unwind_start(struct unwind_state *state, struct task_struct *task,
		    struct pt_regs *regs, unsigned long first_frame);
bool unwind_next_frame(struct unwind_state *state);
+2 −6
Original line number Diff line number Diff line
@@ -103,13 +103,11 @@ bool unwind_next_frame(struct unwind_state *state)
	if (sp & 0x7)
		goto out_err;

	ip = ftrace_graph_ret_addr(state->task, &state->graph_idx, ip, (void *) sp);

	/* Update unwind state */
	state->sp = sp;
	state->ip = ip;
	state->regs = regs;
	state->reliable = reliable;
	state->ip = unwind_recover_ret_addr(state, ip);
	return true;

out_err:
@@ -161,12 +159,10 @@ void __unwind_start(struct unwind_state *state, struct task_struct *task,
		ip = READ_ONCE_NOCHECK(sf->gprs[8]);
	}

	ip = ftrace_graph_ret_addr(state->task, &state->graph_idx, ip, NULL);

	/* Update unwind state */
	state->sp = sp;
	state->ip = ip;
	state->reliable = true;
	state->ip = unwind_recover_ret_addr(state, ip);

	if (!first_frame)
		return;