From: sashiko-bot@kernel.org
To: "Shuai Xue" <xueshuai@linux.alibaba.com>,
palmer@dabbelt.com, pjw@kernel.org, aou@eecs.berkeley.edu
Cc: alex@ghiti.fr, oleg@redhat.com, rostedt@goodmis.org,
mhiramat@kernel.org, mark.rutland@arm.com, peterz@infradead.org,
mingo@redhat.com, acme@kernel.org, namhyung@kernel.org,
alexander.shishkin@linux.intel.com, jolsa@kernel.org,
irogers@google.com, adrian.hunter@intel.com,
james.clark@linaro.org, jpoimboe@kernel.org, jikos@kernel.org,
mbenes@suse.cz, pmladek@suse.com, joe.lawrence@redhat.com,
shuah@kernel.org, mpdesouza@suse.com, martin@kaiser.cx,
kmehltretter@gmail.com, oliver.yang@linux.alibaba.com,
zhuo.song@linux.alibaba.com, jkchen@linux.alibaba.com,
linux-trace-kernel@vger.kernel.org,
linux-perf-users@vger.kernel.org, live-patching@vger.kernel.org
Subject: Re: [PATCH v7 08/10] riscv: stacktrace: switch to frame-pointer based unwinder
Date: Tue, 10 Nov 2026 02:20:51 +0000 [thread overview]
Message-ID: <sashiko-outbox-206040@kernel.org> (raw)
In-Reply-To: <20261110013750.36800-9-xueshuai@linux.alibaba.com>
Thank you for your contribution! Sashiko AI review found 4 potential
issues to consider:
- [High] riscv: stacktrace: broken leaf-function unwind in
kunwind_next_frame_record()
- [High] riscv: stacktrace: broken unwind across ftrace_caller and
return_to_handler
- [Medium] riscv: stacktrace: bogus walk_stackframe entry in
__walk_stackframe()
- [Low] riscv: stacktrace: inaccurate metadata type offset comment
--
commit 263985113a916e8cd08cc4485092812e49e28303
Author: Wang Han <wanghan@linux.alibaba.com>
riscv: stacktrace: switch to frame-pointer based unwinder
This commit replaces the open-coded frame-pointer stack walker with a
kunwind state machine, adds arch_stack_walk_reliable(), and routes
perf_callchain_kernel(), dump_backtrace(), and __get_wchan() through
arch_stack_walk().
> diff --git a/arch/riscv/kernel/stacktrace.c b/arch/riscv/kernel/stacktrace.c
> index a5abb705d43ba..490092c28d612 100644
> --- a/arch/riscv/kernel/stacktrace.c
> +++ b/arch/riscv/kernel/stacktrace.c
[ ... ]
> +/*
> + * Unwind from one frame record to the next.
> + *
> + * On RISC-V, the frame record sits at fp - sizeof(struct frame_record),
> + * immediately below the address pointed to by fp/s0. This applies to both
> + * normal frame records and metadata frame records (embedded in pt_regs).
> + *
> + * A metadata record is identified by both fp and ra being zero in the
> + * frame_record portion, with a type value following at fp + 16.
> + */
[Severity: Low]
This isn't a bug, but since fp points immediately above struct
frame_record on RISC-V (meta_base = fp - sizeof(struct frame_record)),
meta->type is located at fp rather than fp + 16.
> +static __always_inline int
> +kunwind_next_frame_record(struct kunwind_state *state)
> +{
> + unsigned long fp = state->common.fp;
> + struct frame_record *record;
> + struct stack_info *info;
> + unsigned long new_fp, new_pc;
> + unsigned long record_base;
> +
> + if (fp & 0x7)
> + return -EINVAL;
> +
> + record_base = fp - sizeof(*record);
> +
> + info = unwind_find_stack(&state->common, record_base, sizeof(*record));
> + if (!info)
> + return -EINVAL;
> +
> + record = (struct frame_record *)record_base;
> + new_fp = READ_ONCE(record->fp);
> + new_pc = READ_ONCE(record->ra);
> +
> + if (!new_fp && !new_pc)
> + return kunwind_next_frame_record_meta(state);
> +
> + unwind_consume_stack(&state->common, info, record_base,
> + sizeof(*record));
> +
> + state->common.fp = new_fp;
> + state->common.pc = new_pc;
> + state->source = KUNWIND_SOURCE_FRAME;
> +
> + return 0;
> +}
[Severity: High]
Does unwinding from KUNWIND_SOURCE_REGS_PC corrupt the unwind and leak
kernel stack addresses when an exception or interrupt hits a leaf
function?
On RISC-V with CONFIG_FRAME_POINTER=y, leaf functions do not push ra onto
the stack: they save only the caller's s0 at fp - sizeof(unsigned long)
(which aliases record->ra), leave the return address in regs->ra, and
leave fp - 2 * sizeof(unsigned long) (record->fp) as uninitialized stack
padding or local storage.
The previous unwinder checked for this case, whereas kunwind_init_from_regs()
and kunwind_next_regs_pc() store regs in state->regs without ever reading
it back, and kunwind_next_frame_record() unconditionally reads record->ra
as new_pc and record->fp as new_fp. Because __kernel_text_address()
filtering is also removed and perf_callchain_kernel() now calls
arch_stack_walk(), a PMU sample in a leaf function passes the caller's
saved kernel stack pointer in record->ra to fill_callchain() ->
perf_callchain_store(), exposing it to userspace, while regs->ra is lost
and the uninitialized word in record->fp is used as the next frame
pointer.
[ ... ]
> +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 ra 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);
> +}
[Severity: High]
Can kunwind_next_frame_record() and arch_stack_walk_reliable() unwind
across ftrace_caller and return_to_handler?
Unlike arm64, RISC-V's ftrace trampolines do not maintain a valid struct
frame_record chain at s0 - sizeof(struct frame_record):
arch/riscv/kernel/mcount-dyn.S:SAVE_ABI_REGS {
addi sp, sp, -FREGS_SIZE_ON_STACK
REG_S t0, FREGS_EPC(sp)
REG_S x1, FREGS_RA(sp)
REG_S x8, FREGS_S0(sp)
...
}
arch/riscv/kernel/mcount.S:SAVE_RET_ABI_STATE {
addi sp, sp, -FREGS_SIZE_ON_STACK
REG_S ra, FREGS_RA(sp)
REG_S s0, FREGS_S0(sp)
REG_S a0, FREGS_A0(sp)
REG_S a1, FREGS_A1(sp)
addi s0, sp, FREGS_SIZE_ON_STACK
}
In ftrace_caller, SAVE_ABI_REGS saves ftrace_regs without updating s0 or
creating a frame record, so a C tracer callback saves the traced
function's caller s0 alongside a return address in ftrace_caller. Walking
past the callback jumps straight from ftrace_caller to the caller of the
traced function, silently skipping both the traced function and its
immediate caller while arch_stack_walk_reliable() still succeeds. (Commit
"riscv: ftrace: always preserve s0 in dynamic ftrace register frame" noted
that the reliable unwinder would read ftrace_regs_get_frame_pointer(fregs)
directly, but kunwind has no ftrace_regs handling.)
In return_to_handler, SAVE_RET_ABI_STATE sets s0 to sp +
FREGS_SIZE_ON_STACK after saving only ra, s0, a0, and a1. When unwinding
past ftrace_return_to_handler(), kunwind_next_frame_record() reads
s0 - sizeof(struct frame_record), which sits in the uninitialized upper
words of the __arch_ftrace_regs allocation rather than at FREGS_S0(sp).
[ ... ]
> +/*
> * The generic code expects the first entry for the current task to be the
> * return address of arch_stack_walk() itself, the way x86 and arm64 report
> - * it, and stack_trace_save() sizes its skip count accordingly. Walking from
> - * inside a called walk_stackframe() adds that function's own frame on top,
> - * so inline the walker here and start from this frame instead.
> + * it, and stack_trace_save() sizes its skip count accordingly. Walking
> + * from inside a called walk_stackframe() adds that function's own frame
> + * on top, so call the always-inlined walker here and start from this
> + * frame instead.
> */
> -noinline noinstr void arch_stack_walk(stack_trace_consume_fn consume_entry, void *cookie,
> - struct task_struct *task, struct pt_regs *regs)
> +noinline noinstr void arch_stack_walk(stack_trace_consume_fn consume_entry,
> + void *cookie, struct task_struct *task,
> + struct pt_regs *regs)
> {
> __walk_stackframe(task, regs, consume_entry, cookie);
> }
[Severity: Medium]
On !CONFIG_FRAME_POINTER builds, doesn't __walk_stackframe() still report
walk_stackframe as the first entry when unwinding the current task?
arch/riscv/kernel/stacktrace.c:__walk_stackframe() {
...
} else if (task == NULL || task == current) {
sp = current_stack_pointer;
pc = (unsigned long)walk_stackframe;
} else {
...
ksp = (unsigned long *)sp;
while (!kstack_end(ksp)) {
if (__kernel_text_address(pc) && unlikely(!fn(arg, pc)))
break;
pc = READ_ONCE_NOCHECK(*ksp++);
}
}
Unlike the removed CONFIG_FRAME_POINTER version (which started at
level = -1 to skip the placeholder pc), the !CONFIG_FRAME_POINTER loop
calls fn(arg, pc) before reading *ksp++. As a result, arch_stack_walk()
reports walk_stackframe before scanning its own stack frame, shifting
stack_trace_save() and return_address() off by one and printing a bogus
walk_stackframe entry in dump_backtrace().
Also, since all callers of walk_stackframe() are converted to
arch_stack_walk() by this patch, walk_stackframe() has no remaining
callers and its extern declaration in arch/riscv/include/asm/stacktrace.h
is left without a definition when CONFIG_FRAME_POINTER=y.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261110013750.36800-1-xueshuai@linux.alibaba.com?part=8
next prev parent reply other threads:[~2026-11-10 2:20 UTC|newest]
Thread overview: 39+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-14 9:26 [PATCH v6 0/7] riscv: Add reliable stack unwinding for livepatch Shuai Xue
2026-09-14 9:26 ` [PATCH v6 1/7] riscv: stacktrace: Add frame record metadata Shuai Xue
2026-09-14 9:40 ` sashiko-bot
2026-09-14 9:26 ` [PATCH v6 2/7] riscv: stacktrace: disable KASAN and KCOV instrumentation for stacktrace.o Shuai Xue
2026-09-14 9:32 ` sashiko-bot
2026-09-14 9:26 ` [PATCH v6 3/7] riscv: ftrace: always preserve s0 in dynamic ftrace register frame Shuai Xue
2026-09-14 9:32 ` sashiko-bot
2026-09-14 9:26 ` [PATCH v6 4/7] riscv: stacktrace: introduce stack-bound tracking helpers Shuai Xue
2026-09-14 9:33 ` sashiko-bot
2026-09-14 9:26 ` [PATCH v6 5/7] riscv: stacktrace: switch to frame-pointer based unwinder Shuai Xue
2026-09-14 9:39 ` sashiko-bot
2026-10-09 18:59 ` Karl Mehltretter
2026-10-10 3:38 ` Shuai Xue
2026-10-13 20:18 ` Martin Kaiser
2026-10-14 13:09 ` Shuai Xue
2026-09-14 9:26 ` [PATCH v6 6/7] riscv: Kconfig: enable HAVE_RELIABLE_STACKTRACE and HAVE_LIVEPATCH Shuai Xue
2026-09-14 9:38 ` sashiko-bot
2026-09-14 9:26 ` [PATCH v6 7/7] selftests/livepatch: Add RISC-V syscall wrapper prefix Shuai Xue
2026-09-14 9:38 ` sashiko-bot
2026-09-29 7:28 ` [PATCH v6 0/7] riscv: Add reliable stack unwinding for livepatch Martin Kaiser
2026-10-08 11:42 ` Shuai Xue
2026-10-12 8:44 ` Martin Kaiser
2026-11-10 1:37 ` [PATCH v7 00/10] " Shuai Xue
2026-11-10 1:37 ` [PATCH v7 01/10] riscv: stacktrace: do not report the walker's own frame from arch_stack_walk() Shuai Xue
2026-11-10 1:52 ` sashiko-bot
2026-11-10 1:37 ` [PATCH v7 02/10] riscv: Add KUnit test for stack trace start frames Shuai Xue
2026-11-10 1:50 ` sashiko-bot
2026-11-10 1:37 ` [PATCH v7 03/10] riscv: ftrace: use the frame pointer as the function graph return-address cookie Shuai Xue
2026-11-10 1:37 ` [PATCH v7 04/10] riscv: stacktrace: Add frame record metadata Shuai Xue
2026-11-10 1:54 ` sashiko-bot
2026-11-10 1:37 ` [PATCH v7 05/10] riscv: stacktrace: disable KASAN and KCOV instrumentation for stacktrace.o Shuai Xue
2026-11-10 1:37 ` [PATCH v7 06/10] riscv: ftrace: always preserve s0 in dynamic ftrace register frame Shuai Xue
2026-11-10 1:37 ` [PATCH v7 07/10] riscv: stacktrace: introduce stack-bound tracking helpers Shuai Xue
2026-11-10 1:37 ` [PATCH v7 08/10] riscv: stacktrace: switch to frame-pointer based unwinder Shuai Xue
2026-11-10 2:20 ` sashiko-bot [this message]
2026-11-10 1:37 ` [PATCH v7 09/10] riscv: Kconfig: enable HAVE_RELIABLE_STACKTRACE and HAVE_LIVEPATCH Shuai Xue
2026-11-10 2:10 ` sashiko-bot
2026-11-10 1:37 ` [PATCH v7 10/10] selftests/livepatch: Add RISC-V syscall wrapper prefix Shuai Xue
2026-11-10 6:35 ` [PATCH v7 00/10] riscv: Add reliable stack unwinding for livepatch Karl Mehltretter
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=sashiko-outbox-206040@kernel.org \
--to=sashiko-bot@kernel.org \
--cc=acme@kernel.org \
--cc=adrian.hunter@intel.com \
--cc=alex@ghiti.fr \
--cc=alexander.shishkin@linux.intel.com \
--cc=aou@eecs.berkeley.edu \
--cc=irogers@google.com \
--cc=james.clark@linaro.org \
--cc=jikos@kernel.org \
--cc=jkchen@linux.alibaba.com \
--cc=joe.lawrence@redhat.com \
--cc=jolsa@kernel.org \
--cc=jpoimboe@kernel.org \
--cc=kmehltretter@gmail.com \
--cc=linux-perf-users@vger.kernel.org \
--cc=linux-trace-kernel@vger.kernel.org \
--cc=live-patching@vger.kernel.org \
--cc=mark.rutland@arm.com \
--cc=martin@kaiser.cx \
--cc=mbenes@suse.cz \
--cc=mhiramat@kernel.org \
--cc=mingo@redhat.com \
--cc=mpdesouza@suse.com \
--cc=namhyung@kernel.org \
--cc=oleg@redhat.com \
--cc=oliver.yang@linux.alibaba.com \
--cc=palmer@dabbelt.com \
--cc=peterz@infradead.org \
--cc=pjw@kernel.org \
--cc=pmladek@suse.com \
--cc=rostedt@goodmis.org \
--cc=sashiko-reviews@lists.linux.dev \
--cc=shuah@kernel.org \
--cc=xueshuai@linux.alibaba.com \
--cc=zhuo.song@linux.alibaba.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