From: Jens Remus <jremus@linux.ibm.com>
To: Dylan Hatch <dylanbhatch@google.com>,
Roman Gushchin <roman.gushchin@linux.dev>,
Weinan Liu <wnliu@google.com>, Will Deacon <will@kernel.org>,
Josh Poimboeuf <jpoimboe@kernel.org>,
Indu Bhagat <indu.bhagat@oracle.com>,
Peter Zijlstra <peterz@infradead.org>,
Steven Rostedt <rostedt@goodmis.org>,
Catalin Marinas <catalin.marinas@arm.com>,
Jiri Kosina <jikos@kernel.org>
Cc: Mark Rutland <mark.rutland@arm.com>,
Prasanna Kumar T S M <ptsm@linux.microsoft.com>,
Puranjay Mohan <puranjay@kernel.org>, Song Liu <song@kernel.org>,
joe.lawrence@redhat.com, linux-toolchains@vger.kernel.org,
linux-kernel@vger.kernel.org, live-patching@vger.kernel.org,
linux-arm-kernel@lists.infradead.org,
Heiko Carstens <hca@linux.ibm.com>
Subject: Re: [PATCH v3 8/8] unwind: arm64: Use sframe to unwind interrupt frames.
Date: Fri, 17 Apr 2026 17:45:10 +0200 [thread overview]
Message-ID: <95f9d11a-dd92-433e-a8db-cbebe94e1611@linux.ibm.com> (raw)
In-Reply-To: <20260406185000.1378082-9-dylanbhatch@google.com>
Hello Dylan and Weinan!
On 4/6/2026 8:50 PM, Dylan Hatch wrote:
> Add unwind_next_frame_sframe() function to unwind by sframe info if
> present. Use this method at exception boundaries, falling back to
> frame-pointer unwind only on failure. In such failure cases, the
> stacktrace is considered unreliable.
>
> During normal unwind, prefer frame pointer unwind (for better
> performance) with sframe as a backup.
>
> This change restores the LR behavior originally introduced in commit
> c2c6b27b5aa14fa2 ("arm64: stacktrace: unwind exception boundaries"),
> But later removed in commit 32ed1205682e ("arm64: stacktrace: Skip
> reporting LR at exception boundaries")
>
> This can be done because the sframe data can be used to determine
> whether the LR is current for the PC value recovered from pt_regs at the
> exception boundary.
>
> Signed-off-by: Weinan Liu <wnliu@google.com>
> Signed-off-by: Dylan Hatch <dylanbhatch@google.com>
> Reviewed-by: Prasanna Kumar T S M <ptsm@linux.microsoft.com>
> diff --git a/arch/arm64/kernel/stacktrace.c b/arch/arm64/kernel/stacktrace.c
> +/*
> + * Unwind to the next frame according to sframe.
> + */
> +static __always_inline int
> +unwind_next_frame_sframe(struct kunwind_state *state)
> +{
> + struct unwind_frame frame;
> + unsigned long cfa, fp, ra;
> + enum kunwind_source source = KUNWIND_SOURCE_FRAME;
> + struct pt_regs *regs = state->regs;
> +
> + int err;
> +
> + /* FP/SP alignment 8 bytes */
> + if (state->common.fp & 0x7 || state->common.sp & 0x7)
> + return -EINVAL;
> +
> + /*
> + * Most/all outermost functions are not visible to sframe. So, check for
> + * a meta frame record if the sframe lookup fails.
> + */
> + err = sframe_find_kernel(state->common.pc, &frame);
> + if (err)
> + return kunwind_next_frame_record_meta(state);
> +
> + if (frame.outermost)
> + return -ENOENT;
> +
> + /* Get the Canonical Frame Address (CFA) */
> + switch (frame.cfa.rule) {
> + case UNWIND_CFA_RULE_SP_OFFSET:
> + cfa = state->common.sp;
> + break;
> + case UNWIND_CFA_RULE_FP_OFFSET:
> + if (state->common.fp < state->common.sp)
> + return -EINVAL;
I wonder whether that check is valid in kernel? Looking at
call_on_irq_stack() saving SP in FP and then loading SP with the IRQ SP.
Is that condition always true then?
> + cfa = state->common.fp;
> + break;
> + case UNWIND_CFA_RULE_REG_OFFSET:
> + case UNWIND_CFA_RULE_REG_OFFSET_DEREF:
> + if (!regs)
if (!regs || frame.cfa.regnum > 30)
> + return -EINVAL;
> + cfa = regs->regs[frame.cfa.regnum];
In unwind user this is guarded by a topmost frame check, as arbitrary
registers are otherwise not available. Isn't this necessary in the
kernel case?
> + break;
> + default:
> + WARN_ON_ONCE(1);
> + return -EINVAL;
> + }
> + cfa += frame.cfa.offset;
> +
> + /*
> + * CFA typically points to a higher address than RA or FP, so don't
> + * consume from the stack when we read it.
> + */
> + if (frame.cfa.rule & UNWIND_RULE_DEREF &&
> + !get_word(&state->common, &cfa))
> + return -EINVAL;
> +
> + /* CFA alignment 8 bytes */
> + if (cfa & 0x7)
> + return -EINVAL;
> +
> + /* Get the Return Address (RA) */
> + switch (frame.ra.rule) {
> + case UNWIND_RULE_RETAIN:
> + if (!regs)
> + return -EINVAL;
> + ra = regs->regs[30];
Likewise: Topmost frame check not required to access arbitrary registers
(including RA/LR)? Furthermore, provided don't have a thinko, LR may
only be in LR in the topmost frame. In any other frame it must have
been saved. Otherwise there would be an endless return loop.
> + source = KUNWIND_SOURCE_REGS_LR;
> + break;
> + /* UNWIND_USER_RULE_CFA_OFFSET not implemented on purpose */
> + case UNWIND_RULE_CFA_OFFSET_DEREF:
> + ra = cfa + frame.ra.offset;
> + break;
> + case UNWIND_RULE_REG_OFFSET:
> + case UNWIND_RULE_REG_OFFSET_DEREF:
> + if (!regs)
if (!regs || frame.cfa.regnum > 30)
> + return -EINVAL;
> + ra = regs->regs[frame.cfa.regnum];
Likewise: Topmost frame check not required to access arbitrary registers?
> + ra += frame.ra.offset;
> + break;
> + default:
> + WARN_ON_ONCE(1);
> + return -EINVAL;
> + }
> +
> + /* Get the Frame Pointer (FP) */
> + switch (frame.fp.rule) {
> + case UNWIND_RULE_RETAIN:
> + fp = state->common.fp;
> + break;
> + /* UNWIND_USER_RULE_CFA_OFFSET not implemented on purpose */
> + case UNWIND_RULE_CFA_OFFSET_DEREF:
> + fp = cfa + frame.fp.offset;
> + break;
> + case UNWIND_RULE_REG_OFFSET:
> + case UNWIND_RULE_REG_OFFSET_DEREF:
> + if (!regs)
if (!regs || frame.cfa.regnum > 30)
> + return -EINVAL;
> + fp = regs->regs[frame.fp.regnum];
Likewise: Topmost frame check not required to access arbitrary registers?
> + fp += frame.fp.offset;
> + break;
> + default:
> + WARN_ON_ONCE(1);
> + return -EINVAL;
> + }
> +
> + /*
> + * Consume RA and FP from the stack. The frame record puts FP at a lower
> + * address than RA, so we always read FP first.
> + */
> + if (frame.fp.rule & UNWIND_RULE_DEREF &&
> + !get_word(&state->common, &fp))
> + return -EINVAL;
> +
> + if (frame.ra.rule & UNWIND_RULE_DEREF &&
> + get_consume_word(&state->common, &ra))
> + return -EINVAL;
> +
> + state->common.pc = ra;
> + state->common.sp = cfa;
> + state->common.fp = fp;
> +
> + state->source = source;
> +
> + return 0;
> +}
Thanks and regards,
Jens
--
Jens Remus
Linux on Z Development (D3303)
jremus@de.ibm.com / jremus@linux.ibm.com
IBM Deutschland Research & Development GmbH; Vorsitzender des Aufsichtsrats: Wolfgang Wendt; Geschäftsführung: David Faller; Sitz der Gesellschaft: Ehningen; Registergericht: Amtsgericht Stuttgart, HRB 243294
IBM Data Privacy Statement: https://www.ibm.com/privacy/
next prev parent reply other threads:[~2026-04-17 15:45 UTC|newest]
Thread overview: 21+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-04-06 18:49 [PATCH v3 0/8] unwind, arm64: add sframe unwinder for kernel Dylan Hatch
2026-04-06 18:49 ` [PATCH v3 1/8] sframe: Allow kernelspace sframe sections Dylan Hatch
2026-04-14 12:09 ` Jens Remus
2026-04-06 18:49 ` [PATCH v3 2/8] arm64, unwind: build kernel with sframe V3 info Dylan Hatch
2026-04-06 21:36 ` Randy Dunlap
2026-04-14 12:43 ` Jens Remus
2026-04-18 0:20 ` Dylan Hatch
2026-04-06 18:49 ` [PATCH v3 3/8] arm64: entry: add unwind info for various kernel entries Dylan Hatch
2026-04-16 14:09 ` Jens Remus
2026-04-16 16:49 ` Jens Remus
2026-04-06 18:49 ` [PATCH v3 4/8] sframe: Provide PC lookup for vmlinux .sframe section Dylan Hatch
2026-04-16 15:10 ` Jens Remus
2026-04-06 18:49 ` [PATCH v3 5/8] sframe: Allow unsorted FDEs Dylan Hatch
2026-04-16 14:57 ` Jens Remus
2026-04-06 18:49 ` [PATCH v3 6/8] arm64/module, sframe: Add sframe support for modules Dylan Hatch
2026-04-17 14:07 ` Jens Remus
2026-04-06 18:49 ` [PATCH v3 7/8] sframe: Introduce in-kernel SFRAME_VALIDATION Dylan Hatch
2026-04-16 15:04 ` Jens Remus
2026-04-06 18:50 ` [PATCH v3 8/8] unwind: arm64: Use sframe to unwind interrupt frames Dylan Hatch
2026-04-17 15:45 ` Jens Remus [this message]
2026-04-14 16:10 ` [PATCH v3 0/8] unwind, arm64: add sframe unwinder for kernel Jens Remus
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=95f9d11a-dd92-433e-a8db-cbebe94e1611@linux.ibm.com \
--to=jremus@linux.ibm.com \
--cc=catalin.marinas@arm.com \
--cc=dylanbhatch@google.com \
--cc=hca@linux.ibm.com \
--cc=indu.bhagat@oracle.com \
--cc=jikos@kernel.org \
--cc=joe.lawrence@redhat.com \
--cc=jpoimboe@kernel.org \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-toolchains@vger.kernel.org \
--cc=live-patching@vger.kernel.org \
--cc=mark.rutland@arm.com \
--cc=peterz@infradead.org \
--cc=ptsm@linux.microsoft.com \
--cc=puranjay@kernel.org \
--cc=roman.gushchin@linux.dev \
--cc=rostedt@goodmis.org \
--cc=song@kernel.org \
--cc=will@kernel.org \
--cc=wnliu@google.com \
/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