diff mbox series

[SRU,jammy,v2,4/5] s390/unwind: recover kretprobe modified return address in stacktrace

Message ID 20230331140041.2112510-5-john.cabaj@canonical.com
State New
Headers show
Series ftrace graph return address recovery support for s390x Livepatch | expand

Commit Message

John Cabaj March 31, 2023, 2 p.m. UTC
From: Vasily Gorbik <gor@linux.ibm.com>

BugLink: https://bugs.launchpad.net/bugs/2013603 (Kernel livepatch ftrace graph fix)

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>
(cherry picked from commit d81675b60d0959cfa3727f03d5b90558fb457011)
Signed-off-by: John Cabaj <john.cabaj@canonical.com>
---
 arch/s390/include/asm/unwind.h | 13 +++++++++++++
 arch/s390/kernel/unwind_bc.c   |  8 ++------
 2 files changed, 15 insertions(+), 6 deletions(-)
diff mbox series

Patch

diff --git a/arch/s390/include/asm/unwind.h b/arch/s390/include/asm/unwind.h
index 5ebf534ef753..0bf06f1682d8 100644
--- a/arch/s390/include/asm/unwind.h
+++ b/arch/s390/include/asm/unwind.h
@@ -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);
diff --git a/arch/s390/kernel/unwind_bc.c b/arch/s390/kernel/unwind_bc.c
index 707fd99f6734..984970389bab 100644
--- a/arch/s390/kernel/unwind_bc.c
+++ b/arch/s390/kernel/unwind_bc.c
@@ -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;