[RFC PATCH v2 4/4] arm64: Mark stack trace as unreliable if kretprobed functions are present

From: madvenka
Date: Mon Apr 05 2021 - 16:43:51 EST


From: "Madhavan T. Venkataraman" <madvenka@xxxxxxxxxxxxxxxxxxx>

When a kretprobe is active for a function, the function's return address
in its stack frame is modified to point to the kretprobe trampoline. When
the function returns, the frame is popped and control is transferred
to the trampoline. The trampoline eventually returns to the original return
address.

If a stack walk is done within the function (or any functions that get
called from there), the stack trace will only show the trampoline and the
not the original caller.

Also, if the trampoline itself and the functions it calls do a stack trace,
that stack trace will also have the same problem. Detect this as well.

If the trampoline is detected in the stack trace, mark the stack trace
as unreliable.

Reviewed-by: Mark Brown <broonie@xxxxxxxxxx>
Signed-off-by: Madhavan T. Venkataraman <madvenka@xxxxxxxxxxxxxxxxxxx>
---
arch/arm64/kernel/stacktrace.c | 37 ++++++++++++++++++++++++++++++++++
1 file changed, 37 insertions(+)

diff --git a/arch/arm64/kernel/stacktrace.c b/arch/arm64/kernel/stacktrace.c
index 7a3c638d4aeb..926bd91ffb3f 100644
--- a/arch/arm64/kernel/stacktrace.c
+++ b/arch/arm64/kernel/stacktrace.c
@@ -97,6 +97,36 @@ struct function_range {
* if return_to_handler is detected on the stack.
*
* NOTE: The unwinder must do (1) before (2).
+ *
+ * KPROBES
+ * =======
+ *
+ * There are two types of kprobes:
+ *
+ * (1) Regular kprobes that are placed anywhere in a probed function.
+ * This is implemented by replacing the probed instruction with a
+ * breakpoint. When the breakpoint is hit, the kprobe code emulates
+ * the original instruction in-situ and returns to the next
+ * instruction.
+ *
+ * Breakpoints are EL1 exceptions. When the unwinder detects them,
+ * the stack trace is marked as unreliable as it does not know where
+ * exactly the exception happened. Detection of EL1 exceptions in
+ * a stack trace will be done separately.
+ *
+ * (2) Return kprobes that are placed on the return of a probed function.
+ * In this case, Kprobes sets up an initial breakpoint at the
+ * beginning of the probed function. When the breakpoint is hit,
+ * Kprobes replaces the return address in the stack frame with the
+ * kretprobe_trampoline and records the original return address.
+ * When the probed function returns, control goes to the trampoline
+ * which eventually returns to the original return address.
+ *
+ * Stack traces taken while in the probed function or while in the
+ * trampoline will show kretprobe_trampoline instead of the original
+ * return address. Detect this and mark the stack trace unreliable.
+ * The detection is done by checking if the return PC falls anywhere
+ * in kretprobe_trampoline.
*/
static struct function_range special_functions[] = {
/*
@@ -125,6 +155,13 @@ static struct function_range special_functions[] = {
{ (unsigned long) return_to_handler, 0 },
#endif

+ /*
+ * Kprobe trampolines.
+ */
+#ifdef CONFIG_KRETPROBES
+ { (unsigned long) kretprobe_trampoline, 0 },
+#endif
+
{ /* sentinel */ }
};

--
2.25.1