From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id DC444259C8A; Wed, 21 May 2025 11:10:16 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1747825818; cv=none; b=LO4JMFmOlpH6PpFSwvO+JizElLbDGL0t/p7SqMq2WDYxB7LMPDLb4BWU75Ycpx1JVemFCrJhB9wFJL4l48EdgmLBNAlbmrdl3LbH3sFc2sM6jt9QBvOHGrRwqfZaug8caJC2HtlFr0nKG0/SDDA5nBSTlyh03KUnOFGpemmpyd8= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1747825818; c=relaxed/simple; bh=iXupRjhW97bCcPTYw6c79clP2hGoLH+eBswabil0/sU=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=H3C02Jz1rIUOWZU0yQXFglPrgrn5PVXg0zFpUsfG4zY8hVtvD0rcEQvAE/KdxlSY1gm3wjwVU/uMJbqPGv2ACNEujTIZdJbIs25a0OlaMw5Mp0HmfBWgF9Rc6V+GLC0zu/iw3Y40/4OiLQ7DJQV5xGEhDV9hzBWIOG6ZeJ+64Dc= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 62FAD25E9; Wed, 21 May 2025 04:10:02 -0700 (PDT) Received: from lakrids.cambridge.arm.com (usa-sjc-imap-foss1.foss.arm.com [10.121.207.14]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPA id 249DD3F6A8; Wed, 21 May 2025 04:10:14 -0700 (PDT) From: Mark Rutland To: linux-arm-kernel@lists.infradead.org Cc: andrea.porta@suse.com, catalin.marinas@arm.com, jpoimboe@kernel.org, leitao@debian.org, linux-toolchains@vger.kernel.org, live-patching@vger.kernel.org, mark.rutland@arm.com, mbenes@suse.cz, pmladek@suse.com, song@kernel.org, will@kernel.org Subject: [PATCH 2/2] arm64: stacktrace: Implement arch_stack_walk_reliable() Date: Wed, 21 May 2025 12:10:00 +0100 Message-Id: <20250521111000.2237470-3-mark.rutland@arm.com> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20250521111000.2237470-1-mark.rutland@arm.com> References: <20250521111000.2237470-1-mark.rutland@arm.com> Precedence: bulk X-Mailing-List: linux-toolchains@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit From: Song Liu Add arch_stack_walk_reliable(), which will be used during kernel live patching to detect when threads have completed executing old versions of functions. Note that arch_stack_walk_reliable() only needs to guarantee that it returns an error code when it cannot provide a reliable stacktrace. It is not required to provide a reliable stacktrace in all scenarios so long as it returns said error code. At present we can only reliably unwind up to an exception boundary. In future we should be able to improve this with additional data from the compiler (e.g. sframe). Signed-off-by: Song Liu Link: https://lore.kernel.org/r/20250320171559.3423224-2-song@kernel.org [ Mark: Simplify logic, clarify commit message ] Signed-off-by: Mark Rutland Cc: Andrea della Porta Cc: Breno Leitao Cc: Catalin Marinas Cc: Josh Poimboeuf Cc: Miroslav Benes Cc: Petr Mladek Cc: Song Liu Cc: Will Deacon --- arch/arm64/Kconfig | 2 +- arch/arm64/kernel/stacktrace.c | 53 +++++++++++++++++++++++++++------- 2 files changed, 44 insertions(+), 11 deletions(-) diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index a182295e6f08b..7a3463bafb274 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -279,6 +279,7 @@ config ARM64 select HAVE_SOFTIRQ_ON_OWN_STACK select USER_STACKTRACE_SUPPORT select VDSO_GETRANDOM + select HAVE_RELIABLE_STACKTRACE help ARM 64-bit (AArch64) Linux support. @@ -2512,4 +2513,3 @@ endmenu # "CPU Power Management" source "drivers/acpi/Kconfig" source "arch/arm64/kvm/Kconfig" - diff --git a/arch/arm64/kernel/stacktrace.c b/arch/arm64/kernel/stacktrace.c index f6494c0942144..acf682afbd478 100644 --- a/arch/arm64/kernel/stacktrace.c +++ b/arch/arm64/kernel/stacktrace.c @@ -279,21 +279,24 @@ kunwind_next(struct kunwind_state *state) typedef bool (*kunwind_consume_fn)(const struct kunwind_state *state, void *cookie); -static __always_inline void +static __always_inline int do_kunwind(struct kunwind_state *state, kunwind_consume_fn consume_state, void *cookie) { - if (kunwind_recover_return_address(state)) - return; + int ret; - while (1) { - int ret; + ret = kunwind_recover_return_address(state); + if (ret) + return ret; + while (1) { if (!consume_state(state, cookie)) - break; + return -EINVAL; ret = kunwind_next(state); + if (ret == -ENOENT) + return 0; if (ret < 0) - break; + return ret; } } @@ -326,7 +329,7 @@ do_kunwind(struct kunwind_state *state, kunwind_consume_fn consume_state, : stackinfo_get_unknown(); \ }) -static __always_inline void +static __always_inline int kunwind_stack_walk(kunwind_consume_fn consume_state, void *cookie, struct task_struct *task, struct pt_regs *regs) @@ -354,7 +357,7 @@ kunwind_stack_walk(kunwind_consume_fn consume_state, if (regs) { if (task != current) - return; + return -EINVAL; kunwind_init_from_regs(&state, regs); } else if (task == current) { kunwind_init_from_caller(&state); @@ -362,7 +365,7 @@ kunwind_stack_walk(kunwind_consume_fn consume_state, kunwind_init_from_task(&state, task); } - do_kunwind(&state, consume_state, cookie); + return do_kunwind(&state, consume_state, cookie); } struct kunwind_consume_entry_data { @@ -389,6 +392,36 @@ noinline noinstr void arch_stack_walk(stack_trace_consume_fn consume_entry, kunwind_stack_walk(arch_kunwind_consume_entry, &data, task, regs); } +static __always_inline bool +arch_reliable_kunwind_consume_entry(const struct kunwind_state *state, void *cookie) +{ + /* + * At an exception boundary we can reliably consume the saved PC. We do + * not know whether the LR was live when the exception was taken, and + * so we cannot perform the next unwind step reliably. + * + * All that matters is whether the *entire* unwind is reliable, so give + * up as soon as we hit an exception boundary. + */ + if (state->source == KUNWIND_SOURCE_REGS_PC) + return false; + + return arch_kunwind_consume_entry(state, cookie); +} + +noinline noinstr int arch_stack_walk_reliable(stack_trace_consume_fn consume_entry, + void *cookie, + struct task_struct *task) +{ + struct kunwind_consume_entry_data data = { + .consume_entry = consume_entry, + .cookie = cookie, + }; + + return kunwind_stack_walk(arch_reliable_kunwind_consume_entry, &data, + task, NULL); +} + struct bpf_unwind_consume_entry_data { bool (*consume_entry)(void *cookie, u64 ip, u64 sp, u64 fp); void *cookie; -- 2.30.2