live-patching.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: madvenka@linux.microsoft.com
To: jpoimboe@redhat.com, peterz@infradead.org,
	chenzhongjin@huawei.com, mark.rutland@arm.com,
	broonie@kernel.org, nobuta.keiya@fujitsu.com,
	sjitindarsingh@gmail.com, catalin.marinas@arm.com,
	will@kernel.org, jamorris@linux.microsoft.com,
	linux-arm-kernel@lists.infradead.org,
	live-patching@vger.kernel.org, linux-kernel@vger.kernel.org,
	madvenka@linux.microsoft.com
Subject: [RFC PATCH v3 19/22] arm64: unwinder: Add a reliability check in the unwinder based on ORC
Date: Thu,  2 Feb 2023 01:40:33 -0600	[thread overview]
Message-ID: <20230202074036.507249-20-madvenka@linux.microsoft.com> (raw)
In-Reply-To: <20230202074036.507249-1-madvenka@linux.microsoft.com>

From: "Madhavan T. Venkataraman" <madvenka@linux.microsoft.com>

Introduce a reliability flag in struct unwind_state. This will be set to
false if the PC does not have a valid ORC or if the frame pointer computed
from the ORC does not match the actual frame pointer.

Now that the unwinder can validate the frame pointer, introduce
arch_stack_walk_reliable().

Signed-off-by: Madhavan T. Venkataraman <madvenka@linux.microsoft.com>
---
 arch/arm64/include/asm/stacktrace/common.h |  15 ++
 arch/arm64/kernel/stacktrace.c             | 167 ++++++++++++++++++++-
 2 files changed, 175 insertions(+), 7 deletions(-)

diff --git a/arch/arm64/include/asm/stacktrace/common.h b/arch/arm64/include/asm/stacktrace/common.h
index 508f734de46e..064aaf5dc3a0 100644
--- a/arch/arm64/include/asm/stacktrace/common.h
+++ b/arch/arm64/include/asm/stacktrace/common.h
@@ -11,6 +11,7 @@
 
 #include <linux/kprobes.h>
 #include <linux/types.h>
+#include <linux/objtool.h>
 
 struct stack_info {
 	unsigned long low;
@@ -23,6 +24,7 @@ struct stack_info {
  * @fp:          The fp value in the frame record (or the real fp)
  * @pc:          The lr value in the frame record (or the real lr)
  *
+ * @prev_pc:     The lr value in the previous frame record.
  * @kr_cur:      When KRETPROBES is selected, holds the kretprobe instance
  *               associated with the most recently encountered replacement lr
  *               value.
@@ -32,10 +34,15 @@ struct stack_info {
  * @stack:       The stack currently being unwound.
  * @stacks:      An array of stacks which can be unwound.
  * @nr_stacks:   The number of stacks in @stacks.
+ *
+ * @cfa:         The sp value at the call site of the current function.
+ * @unwind_type  The previous frame's unwind type.
+ * @reliable:    Stack trace is reliable.
  */
 struct unwind_state {
 	unsigned long fp;
 	unsigned long pc;
+	unsigned long prev_pc;
 #ifdef CONFIG_KRETPROBES
 	struct llist_node *kr_cur;
 #endif
@@ -44,6 +51,9 @@ struct unwind_state {
 	struct stack_info stack;
 	struct stack_info *stacks;
 	int nr_stacks;
+	unsigned long cfa;
+	int unwind_type;
+	bool reliable;
 };
 
 static inline struct stack_info stackinfo_get_unknown(void)
@@ -70,11 +80,15 @@ static inline void unwind_init_common(struct unwind_state *state,
 				      struct task_struct *task)
 {
 	state->task = task;
+	state->prev_pc = 0;
 #ifdef CONFIG_KRETPROBES
 	state->kr_cur = NULL;
 #endif
 
 	state->stack = stackinfo_get_unknown();
+	state->reliable = true;
+	state->cfa = 0;
+	state->unwind_type = UNWIND_HINT_TYPE_CALL;
 }
 
 static struct stack_info *unwind_find_next_stack(const struct unwind_state *state,
@@ -167,6 +181,7 @@ unwind_next_frame_record(struct unwind_state *state)
 	/*
 	 * Record this frame record's values.
 	 */
+	state->prev_pc = state->pc;
 	state->fp = READ_ONCE(*(unsigned long *)(fp));
 	state->pc = READ_ONCE(*(unsigned long *)(fp + 8));
 
diff --git a/arch/arm64/kernel/stacktrace.c b/arch/arm64/kernel/stacktrace.c
index 634279b3b03d..fbcb14539816 100644
--- a/arch/arm64/kernel/stacktrace.c
+++ b/arch/arm64/kernel/stacktrace.c
@@ -5,6 +5,8 @@
  * Copyright (C) 2012 ARM Ltd.
  */
 #include <linux/kernel.h>
+#include <asm/unwind_hints.h>
+#include <asm-generic/orc_lookup.h>
 #include <linux/export.h>
 #include <linux/ftrace.h>
 #include <linux/sched.h>
@@ -16,6 +18,122 @@
 #include <asm/stack_pointer.h>
 #include <asm/stacktrace.h>
 
+static inline bool unwind_completed(struct unwind_state *state)
+{
+	if (state->fp == (unsigned long)task_pt_regs(state->task)->stackframe) {
+		/* Final frame; nothing to unwind */
+		return true;
+	}
+	return false;
+}
+
+#ifdef CONFIG_FRAME_POINTER_VALIDATION
+
+static void unwind_check_reliable(struct unwind_state *state)
+{
+	unsigned long pc, fp;
+	struct orc_entry *orc;
+	bool adjust_pc = false;
+
+	if (unwind_completed(state))
+		return;
+
+	/*
+	 * If a previous frame was unreliable, the CFA cannot be reliably
+	 * computed anymore.
+	 */
+	if (!state->reliable)
+		return;
+
+	pc = state->pc;
+
+	/* Don't let modules unload while we're reading their ORC data. */
+	preempt_disable();
+
+	orc = orc_find(pc);
+	if (!orc || (!orc->fp_offset && orc->type == UNWIND_HINT_TYPE_CALL)) {
+		/*
+		 * If the final instruction in a function happens to be a call
+		 * instruction, the return address would fall outside of the
+		 * function. That could be the case here. This can happen, for
+		 * instance, if the called function is a "noreturn" function.
+		 * The compiler can optimize away the instructions after the
+		 * call. So, adjust the PC so it falls inside the function and
+		 * retry.
+		 *
+		 * We only do this if the current and the previous frames
+		 * are call frames and not hint frames.
+		 */
+		if (state->unwind_type == UNWIND_HINT_TYPE_CALL) {
+			pc -= 4;
+			adjust_pc = true;
+			orc = orc_find(pc);
+		}
+	}
+	if (!orc) {
+		state->reliable = false;
+		goto out;
+	}
+	state->unwind_type = orc->type;
+
+	if (!state->cfa) {
+		/* Set up the initial CFA and return. */
+		state->cfa = state->fp - orc->fp_offset;
+		goto out;
+	}
+
+	/* Compute the next CFA and FP. */
+	switch (orc->type) {
+	case UNWIND_HINT_TYPE_CALL:
+		/* Normal call */
+		state->cfa += orc->sp_offset;
+		fp = state->cfa + orc->fp_offset;
+		break;
+
+	case UNWIND_HINT_TYPE_REGS:
+		/*
+		 * pt_regs hint: The frame pointer points to either the
+		 * synthetic frame within pt_regs or to the place where
+		 * x29 and x30 are saved in the register save area in
+		 * pt_regs.
+		 */
+		state->cfa += orc->sp_offset;
+		fp = state->cfa + offsetof(struct pt_regs, stackframe) -
+		     sizeof(struct pt_regs);
+		if (state->fp != fp) {
+			fp = state->cfa + offsetof(struct pt_regs, regs[29]) -
+			     sizeof(struct pt_regs);
+		}
+		break;
+
+	case UNWIND_HINT_TYPE_IRQ_STACK:
+		/* Hint to unwind from the IRQ stack to the task stack. */
+		state->cfa = state->fp + orc->sp_offset;
+		fp = state->fp;
+		break;
+
+	default:
+		fp = 0;
+		break;
+	}
+
+	/* Validate the actual FP with the computed one. */
+	if (state->fp != fp)
+		state->reliable = false;
+out:
+	if (state->reliable && adjust_pc)
+		state->pc = pc;
+	preempt_enable();
+}
+
+#else /* !CONFIG_FRAME_POINTER_VALIDATION */
+
+static void unwind_check_reliable(struct unwind_state *state)
+{
+}
+
+#endif /* CONFIG_FRAME_POINTER_VALIDATION */
+
 /*
  * Start an unwind from a pt_regs.
  *
@@ -77,11 +195,9 @@ static inline void unwind_init_from_task(struct unwind_state *state,
 static int notrace unwind_next(struct unwind_state *state)
 {
 	struct task_struct *tsk = state->task;
-	unsigned long fp = state->fp;
 	int err;
 
-	/* Final frame; nothing to unwind */
-	if (fp == (unsigned long)task_pt_regs(tsk)->stackframe)
+	if (unwind_completed(state))
 		return -ENOENT;
 
 	err = unwind_next_frame_record(state);
@@ -116,18 +232,23 @@ static int notrace unwind_next(struct unwind_state *state)
 }
 NOKPROBE_SYMBOL(unwind_next);
 
-static void notrace unwind(struct unwind_state *state,
+static int notrace unwind(struct unwind_state *state, bool need_reliable,
 			   stack_trace_consume_fn consume_entry, void *cookie)
 {
-	while (1) {
-		int ret;
+	int ret = 0;
 
+	while (1) {
+		if (need_reliable && !state->reliable)
+			return -EINVAL;
 		if (!consume_entry(cookie, state->pc))
 			break;
 		ret = unwind_next(state);
+		if (need_reliable && !ret)
+			unwind_check_reliable(state);
 		if (ret < 0)
 			break;
 	}
+	return ret;
 }
 NOKPROBE_SYMBOL(unwind);
 
@@ -216,5 +337,37 @@ noinline notrace void arch_stack_walk(stack_trace_consume_fn consume_entry,
 		unwind_init_from_task(&state, task);
 	}
 
-	unwind(&state, consume_entry, cookie);
+	unwind(&state, false, consume_entry, cookie);
+}
+
+noinline notrace int arch_stack_walk_reliable(
+				stack_trace_consume_fn consume_entry,
+				void *cookie, struct task_struct *task)
+{
+	struct stack_info stacks[] = {
+		stackinfo_get_task(task),
+		STACKINFO_CPU(irq),
+#if defined(CONFIG_VMAP_STACK)
+		STACKINFO_CPU(overflow),
+#endif
+#if defined(CONFIG_VMAP_STACK) && defined(CONFIG_ARM_SDE_INTERFACE)
+		STACKINFO_SDEI(normal),
+		STACKINFO_SDEI(critical),
+#endif
+	};
+	struct unwind_state state = {
+		.stacks = stacks,
+		.nr_stacks = ARRAY_SIZE(stacks),
+	};
+	int ret;
+
+	if (task == current)
+		unwind_init_from_caller(&state);
+	else
+		unwind_init_from_task(&state, task);
+	unwind_check_reliable(&state);
+
+	ret = unwind(&state, true, consume_entry, cookie);
+
+	return ret == -ENOENT ? 0 : -EINVAL;
 }
-- 
2.25.1


  parent reply	other threads:[~2023-02-02  7:43 UTC|newest]

Thread overview: 55+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
     [not found] <0337266cf19f4c98388e3f6d09f590d9de258dc7>
2023-02-02  7:40 ` [RFC PATCH v3 00/22] arm64: livepatch: Use ORC for dynamic frame pointer validation madvenka
2023-02-02  7:40   ` [RFC PATCH v3 01/22] objtool: Reorganize CFI code madvenka
2023-02-02  7:40   ` [RFC PATCH v3 02/22] objtool: Reorganize instruction-related code madvenka
2023-02-02  7:40   ` [RFC PATCH v3 03/22] objtool: Move decode_instructions() to a separate file madvenka
2023-02-02  7:40   ` [RFC PATCH v3 04/22] objtool: Reorganize Unwind hint code madvenka
2023-02-02  7:40   ` [RFC PATCH v3 05/22] objtool: Reorganize ORC types madvenka
2023-02-18  9:30     ` Suraj Jitindar Singh
2023-03-06 16:45       ` Madhavan T. Venkataraman
2023-02-02  7:40   ` [RFC PATCH v3 06/22] objtool: Reorganize ORC code madvenka
2023-02-02  7:40   ` [RFC PATCH v3 07/22] objtool: Reorganize ORC kernel code madvenka
2023-02-02  7:40   ` [RFC PATCH v3 08/22] objtool: Introduce STATIC_CHECK madvenka
2023-02-02  7:40   ` [RFC PATCH v3 09/22] objtool: arm64: Add basic definitions and compile madvenka
2023-02-02  7:40   ` [RFC PATCH v3 10/22] objtool: arm64: Implement decoder for Dynamic FP validation madvenka
2023-02-02  7:40   ` [RFC PATCH v3 11/22] objtool: arm64: Invoke the decoder madvenka
2023-02-02  7:40   ` [RFC PATCH v3 12/22] objtool: arm64: Compute destinations for call and jump instructions madvenka
2023-02-02  7:40   ` [RFC PATCH v3 13/22] objtool: arm64: Walk instructions and compute CFI for each instruction madvenka
2023-02-02  7:40   ` [RFC PATCH v3 14/22] objtool: arm64: Generate ORC data from CFI for object files madvenka
2023-02-02  7:40   ` [RFC PATCH v3 15/22] objtool: arm64: Add unwind hint support madvenka
2023-02-02  7:40   ` [RFC PATCH v3 16/22] arm64: Add unwind hints to exception handlers madvenka
2023-02-02  7:40   ` [RFC PATCH v3 17/22] arm64: Add kernel and module support for ORC madvenka
2023-02-02  7:40   ` [RFC PATCH v3 18/22] arm64: Build the kernel with ORC information madvenka
2023-02-10  7:52     ` Tomohiro Misono (Fujitsu)
2023-02-11  4:34       ` Madhavan T. Venkataraman
2023-02-02  7:40   ` madvenka [this message]
2023-02-23  4:07     ` [RFC PATCH v3 19/22] arm64: unwinder: Add a reliability check in the unwinder based on ORC Suraj Jitindar Singh
2023-03-06 16:52       ` Madhavan T. Venkataraman
2023-02-02  7:40   ` [RFC PATCH v3 20/22] arm64: Define HAVE_DYNAMIC_FTRACE_WITH_ARGS madvenka
2023-02-02  7:40   ` [RFC PATCH v3 21/22] arm64: Define TIF_PATCH_PENDING for livepatch madvenka
2023-02-02  7:40   ` [RFC PATCH v3 22/22] arm64: Enable livepatch for ARM64 madvenka
2023-03-01  3:12   ` [RFC PATCH v3 00/22] arm64: livepatch: Use ORC for dynamic frame pointer validation Tomohiro Misono (Fujitsu)
2023-03-02 16:23     ` Petr Mladek
2023-03-03  9:40       ` Tomohiro Misono (Fujitsu)
2023-03-06 16:58       ` Madhavan T. Venkataraman
2023-03-06 16:57     ` Madhavan T. Venkataraman
2023-03-23 17:17   ` Mark Rutland
2023-04-08  3:40     ` Madhavan T. Venkataraman
2023-04-11 13:25       ` Mark Rutland
2023-04-12  4:17         ` Josh Poimboeuf
2023-04-12  4:48           ` Madhavan T. Venkataraman
2023-04-12  4:50             ` Madhavan T. Venkataraman
2023-04-12  5:01             ` Josh Poimboeuf
2023-04-12 14:50               ` Madhavan T. Venkataraman
2023-04-12 15:52                 ` Josh Poimboeuf
2023-04-13 14:59                   ` Madhavan T. Venkataraman
2023-04-13 16:30                     ` Josh Poimboeuf
2023-04-15  4:27                       ` Madhavan T. Venkataraman
2023-04-15  5:05                         ` Josh Poimboeuf
2023-04-15 16:15                           ` Madhavan T. Venkataraman
2023-04-16  8:21                       ` Indu Bhagat
2023-04-13 17:04     ` Nick Desaulniers
2023-04-13 18:15       ` Jose E. Marchesi
2023-04-15  4:14         ` Madhavan T. Venkataraman
2023-12-14 20:49     ` ARM64 Livepatch based on SFrame Madhavan T. Venkataraman
2023-12-15 13:04       ` Mark Rutland
2023-12-15 15:15         ` Madhavan T. Venkataraman

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20230202074036.507249-20-madvenka@linux.microsoft.com \
    --to=madvenka@linux.microsoft.com \
    --cc=broonie@kernel.org \
    --cc=catalin.marinas@arm.com \
    --cc=chenzhongjin@huawei.com \
    --cc=jamorris@linux.microsoft.com \
    --cc=jpoimboe@redhat.com \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=live-patching@vger.kernel.org \
    --cc=mark.rutland@arm.com \
    --cc=nobuta.keiya@fujitsu.com \
    --cc=peterz@infradead.org \
    --cc=sjitindarsingh@gmail.com \
    --cc=will@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).