All of lore.kernel.org
 help / color / mirror / Atom feed
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

  reply	other threads:[~2026-11-10  2:20 UTC|newest]

Thread overview: 71+ 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 ` Shuai Xue
2026-09-14  9:26 ` [PATCH v6 1/7] riscv: stacktrace: Add frame record metadata Shuai Xue
2026-09-14  9:26   ` 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:26   ` 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:26   ` 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:26   ` 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:26   ` Shuai Xue
2026-09-14  9:39   ` sashiko-bot
2026-10-09 18:59   ` Karl Mehltretter
2026-10-09 18:59     ` Karl Mehltretter
2026-10-10  3:38     ` Shuai Xue
2026-10-10  3:38       ` Shuai Xue
2026-10-13 20:18   ` Martin Kaiser
2026-10-13 20:18     ` Martin Kaiser
2026-10-14 13:09     ` Shuai Xue
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:26   ` 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:26   ` 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-09-29  7:28   ` Martin Kaiser
2026-10-08 11:42   ` Shuai Xue
2026-10-08 11:42     ` Shuai Xue
2026-10-12  8:44     ` Martin Kaiser
2026-10-12  8:44       ` Martin Kaiser
2026-11-10  1:37 ` [PATCH v7 00/10] " Shuai Xue
2026-11-10  1:37   ` 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:37     ` 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:37     ` 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     ` Shuai Xue
2026-11-10  1:56     ` sashiko-bot
2026-11-10  1:37   ` [PATCH v7 04/10] riscv: stacktrace: Add frame record metadata Shuai Xue
2026-11-10  1:37     ` 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     ` Shuai Xue
2026-11-10  1:42     ` sashiko-bot
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     ` Shuai Xue
2026-11-10  1:42     ` sashiko-bot
2026-11-10  1:37   ` [PATCH v7 07/10] riscv: stacktrace: introduce stack-bound tracking helpers Shuai Xue
2026-11-10  1:37     ` Shuai Xue
2026-11-10  1:47     ` sashiko-bot
2026-11-10  1:37   ` [PATCH v7 08/10] riscv: stacktrace: switch to frame-pointer based unwinder Shuai Xue
2026-11-10  1:37     ` 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  1:37     ` 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  1:37     ` Shuai Xue
2026-11-10  1:48     ` sashiko-bot
2026-11-10  6:35   ` [PATCH v7 00/10] riscv: Add reliable stack unwinding for livepatch Karl Mehltretter
2026-11-10  6:35     ` 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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.