From nobody Mon Jun 8 07:26:15 2026 Received: from va-1-115.ptr.blmpb.com (va-1-115.ptr.blmpb.com [209.127.230.115]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 6CA133CCFA9 for ; Fri, 5 Jun 2026 08:21:05 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.127.230.115 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780647667; cv=none; b=SbrwnIUBOu8SmgHA8iwEUGOtAubs/B18rulC+9CvS8toIGYl5j3GrJqg78FbBEuyd4iQ1vxOKc6IRzJDGqkvTAAHpC0Ay11Qt8gSXyjjN4ZUT6CEzTQbjQTSXFryOvsR+1j8kOcItGnCIMgbUyF52d+I2ZCF6RynUBFDc1lemk4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780647667; c=relaxed/simple; bh=XOvsd6h+jIZ2iJRSjD46U5o0MdzI7/ozxfiQMlpEXII=; h=References:Content-Type:Subject:From:Mime-Version:To:In-Reply-To: Message-Id:Date:Cc; b=sFdMFNJc3wm4fJpPzlbDivp8DSNfJygBlbTsE0ImKDMla9t4rD3i4QD39A3sVkM4cOH/i9UXs2vd8DSsR7zENx82eawEz5SSTHsvpUIQBSz4fDKjmtOKWJwgCyDD2ZeKdkvdUk/aA8iJ4OkYif6kWUmfAFC460jgccvN725qdHk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=bytedance.com; spf=pass smtp.mailfrom=bytedance.com; dkim=pass (2048-bit key) header.d=bytedance.com header.i=@bytedance.com header.b=OmOHZw7b; arc=none smtp.client-ip=209.127.230.115 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=bytedance.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=bytedance.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=bytedance.com header.i=@bytedance.com header.b="OmOHZw7b" DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; s=2212171451; d=bytedance.com; t=1780647660; h=from:subject: mime-version:from:date:message-id:subject:to:cc:reply-to:content-type: mime-version:in-reply-to:message-id; bh=joVqT0sbeZstg7foR7ZySXhhdOS5ZDwWEcSwH6D6Mjs=; b=OmOHZw7bGeEhePfGfGJ0Q2BdkX0A83G8XQ5OZh5G36mHYp+wx+54qazVmY7oHWObacR9iq UKzGRa3bDyNUBnIWEJoQKem4iIrKKj5VMJHVdV14mXzGAV5ah14ZZJiW15BzK5HGP27UK5 /hvsJDg8i+3sIk2zzcnBIXQZY9uLSyBB5hO7eYBxOfo9ekYcODUABWK/9/cj1tsVjRnDbm cPqLTbRLkVPhJ3iKfpurIVwU7N7FTane3sltpD8Z9rOtrC0iSWt2PpqpUjJG9t89fVQ7lY CtfKfNlF7xXSSJamr3vcA72kjDAu+tq13J+B8YO6WEz0c/uy00WV7yUVFT4olA== References: <20260603115329.791603-1-qirui.001@bytedance.com> <20260605082034.1426923-1-qirui.001@bytedance.com> Content-Transfer-Encoding: quoted-printable Subject: [PATCH v2 1/2] riscv: stacktrace: Remove bogus -0x4 offset in non-FP walk_stackframe From: "Rui Qi" Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 X-Lms-Return-Path: To: "Paul Walmsley" , "Palmer Dabbelt" In-Reply-To: <20260605082034.1426923-1-qirui.001@bytedance.com> X-Original-From: Rui Qi X-Mailer: git-send-email 2.20.1 Message-Id: <20260605082034.1426923-2-qirui.001@bytedance.com> Date: Fri, 5 Jun 2026 16:20:33 +0800 Cc: "Albert Ou" , "Alexandre Ghiti" , , , "Rui Qi" Content-Type: text/plain; charset="utf-8" In the non-frame-pointer version of walk_stackframe, each value read from the stack is treated as a potential return address and has 0x4 subtracted before being used as the program counter. This was intended to convert the return address (the instruction after a call) back to the call site, but it is incorrect: 1. RISC-V has variable-length instructions due to the RVC (compressed instruction) extension. A call instruction can be either 4 bytes (regular) or 2 bytes (compressed, e.g. c.jal). Subtracting a fixed 0x4 assumes all call instructions are 4 bytes, which is wrong for compressed instructions. 2. Stack traces conventionally report return addresses, not call sites. Other architectures (ARM64, x86, ARM) do not subtract instruction size from return addresses in their stack unwinding code. 3. The frame-pointer version of walk_stackframe already dropped the -0x4 offset. Commit b785ec129bd9 ("riscv/ftrace: Add HAVE_FUNCTION_GRAPH_RET_ADDR_PTR support") replaced "pc =3D frame->ra - 0x4" with ftrace_graph_ret_addr(), and the commit message explicitly noted that "the original calculation, pc =3D frame->ra - 4, is buggy when the instruction at the return address happened to be a compressed inst." The non-FP version was simply overlooked. Remove the bogus -0x4 offset to match the FP version and the conventions used by other architectures. Fixes: 5d8544e2d007 ("RISC-V: Generic library routines and assembly") Signed-off-by: Rui Qi --- arch/riscv/kernel/stacktrace.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/riscv/kernel/stacktrace.c b/arch/riscv/kernel/stacktrace.c index 2692d3a06afa..c7555447149b 100644 --- a/arch/riscv/kernel/stacktrace.c +++ b/arch/riscv/kernel/stacktrace.c @@ -129,7 +129,7 @@ void notrace walk_stackframe(struct task_struct *task, while (!kstack_end(ksp)) { if (__kernel_text_address(pc) && unlikely(!fn(arg, pc))) break; - pc =3D READ_ONCE_NOCHECK(*ksp++) - 0x4; + pc =3D READ_ONCE_NOCHECK(*ksp++); } } =20 --=20 2.20.1 From nobody Mon Jun 8 07:26:15 2026 Received: from va-1-114.ptr.blmpb.com (va-1-114.ptr.blmpb.com [209.127.230.114]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B4CEF3C1F24 for ; Fri, 5 Jun 2026 08:21:20 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.127.230.114 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780647682; cv=none; b=L62V7PIeGVuziwykyBL1P8SB/rqaGzRfGxN73OmBR3fSxKfENVAO8cmvI63+mB0yFj3eg/FtC+xaqEPtGQVsiSp/mHI2V0jo7LPd1ZSN5uOAp8zmcwCVgdLzJDFMwA8+TyvJHQe7Ud0BWjRAV17ABm5vn2fgLQMPe0SxnVYN+1M= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780647682; c=relaxed/simple; bh=bcI9a6sR0HgotRbFOAWTbupdnzNS9+yWtN8g6X4lHVw=; h=Date:Content-Type:Subject:Message-Id:To:In-Reply-To:Mime-Version: From:References:Cc; b=QapBGGkyfLHOPXZ99zqxG9wdUhZPX65OhxB6w8xfr/RNnVWzLeBqBDn1x2Xt87+qV0fXiLSQ37i+IJfjs7Z6AZENmxYfbA36uQE4OiWaFCrVjellA4u7JatSlDjnp3wBcTYAEp905GAecNkQNCYilnSQ3EDgjuz1+nG2y5/bqfs= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=bytedance.com; spf=pass smtp.mailfrom=bytedance.com; dkim=pass (2048-bit key) header.d=bytedance.com header.i=@bytedance.com header.b=ZvFDLtF1; arc=none smtp.client-ip=209.127.230.114 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=bytedance.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=bytedance.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=bytedance.com header.i=@bytedance.com header.b="ZvFDLtF1" DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; s=2212171451; d=bytedance.com; t=1780647670; h=from:subject: mime-version:from:date:message-id:subject:to:cc:reply-to:content-type: mime-version:in-reply-to:message-id; bh=BGE6tDrPFGqUs4MaoKsNPVSUNEuhQZ4U3v9K5VelGYg=; b=ZvFDLtF1sdhMqTVSaADYT7xVBozdbwAySXCnAKSHNRB4SIBHz/RmyKnDmDxOq8G++wcAxM EKEzUK9QAfwDc2Usyj1VLiPB1kDp+QE+H/JyAy3i2RUJHDWRJ7Goc1RwCHsnlHr5KQUfig YrEh35FY307oq521dsVHN2G7L3VjRwApG6MQ7ZunFku+F7IDWDAhofRZQ4nQbmbg1P+Ggq cCVqTtLSor/ImuVqADVSLneseLfo3FF+LgIFjUH/5AsI/c/gI+WLlxnws3/uCCGvukdbVK 5kVJ/vYiNg2dNFDRe/FsjQwOWqxnrJwjhqEOdI38fRCuZOqvJW9tiysjgztQKg== Date: Fri, 5 Jun 2026 16:20:34 +0800 Content-Transfer-Encoding: quoted-printable Subject: [PATCH v2 2/2] riscv: stacktrace: Use %pB for return addresses and %pS for exact IPs Message-Id: <20260605082034.1426923-3-qirui.001@bytedance.com> X-Mailer: git-send-email 2.20.1 X-Lms-Return-Path: To: "Paul Walmsley" , "Palmer Dabbelt" In-Reply-To: <20260605082034.1426923-1-qirui.001@bytedance.com> X-Original-From: Rui Qi Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 From: "Rui Qi" References: <20260603115329.791603-1-qirui.001@bytedance.com> <20260605082034.1426923-1-qirui.001@bytedance.com> Cc: "Albert Ou" , "Alexandre Ghiti" , , , "Rui Qi" Content-Type: text/plain; charset="utf-8" The print_trace_address callback uses print_ip_sym which formats addresses with %pS. This does not adjust the address before symbol lookup, so when a noreturn function (e.g. panic, do_exit) is the last call in a function, the saved return address can point to the start of the next function, and kallsyms resolves it to the wrong symbol. The kernel provides %pB (sprint_backtrace) specifically for this purpose: it subtracts 1 from the address before symbol lookup, which is sufficient to fall back into the calling function range. However, %pB must only be used for return addresses. walk_stackframe passes two kinds of addresses to its callback: - Exact instruction pointers: instruction_pointer(regs) and the epc from exception frames on the stack. These should use %pS since they are not return addresses and subtracting 1 could cause kallsyms to resolve them to the preceding function. - Return addresses: frame->ra, regs->ra, and task->thread.ra. These should use %pB for correct symbol resolution, especially after noreturn calls. Add an 'is_ra' parameter to the walk_stackframe callback to indicate whether the address is a return address, then use %pB for return addresses and %pS for exact instruction pointers. This matches the x86 approach which uses %pS for regs->ip via show_ip() and %pB for stack return addresses via printk_stack_address(). Also update the walk_stackframe() declaration in asm/stacktrace.h and the fill_callchain() callback in perf_callchain.c to match the new signature. Signed-off-by: Rui Qi --- arch/riscv/include/asm/stacktrace.h | 2 +- arch/riscv/kernel/perf_callchain.c | 7 +++- arch/riscv/kernel/stacktrace.c | 54 ++++++++++++++++++++++++----- 3 files changed, 52 insertions(+), 11 deletions(-) diff --git a/arch/riscv/include/asm/stacktrace.h b/arch/riscv/include/asm/s= tacktrace.h index b1495a7e06ce..6771d0e0f656 100644 --- a/arch/riscv/include/asm/stacktrace.h +++ b/arch/riscv/include/asm/stacktrace.h @@ -12,7 +12,7 @@ struct stackframe { }; =20 extern void notrace walk_stackframe(struct task_struct *task, struct pt_re= gs *regs, - bool (*fn)(void *, unsigned long), void *arg); + bool (*fn)(void *, unsigned long, bool), void *arg); extern void dump_backtrace(struct pt_regs *regs, struct task_struct *task, const char *loglvl); =20 diff --git a/arch/riscv/kernel/perf_callchain.c b/arch/riscv/kernel/perf_ca= llchain.c index b465bc9eb870..0a5678f0d400 100644 --- a/arch/riscv/kernel/perf_callchain.c +++ b/arch/riscv/kernel/perf_callchain.c @@ -11,6 +11,11 @@ static bool fill_callchain(void *entry, unsigned long pc) return perf_callchain_store(entry, pc) =3D=3D 0; } =20 +static bool fill_callchain_walk(void *entry, unsigned long pc, bool is_ra) +{ + return fill_callchain(entry, pc); +} + /* * This will be called when the target is in user mode * This function will only be called when we use @@ -44,5 +49,5 @@ void perf_callchain_kernel(struct perf_callchain_entry_ct= x *entry, return; } =20 - walk_stackframe(NULL, regs, fill_callchain, entry); + walk_stackframe(NULL, regs, fill_callchain_walk, entry); } diff --git a/arch/riscv/kernel/stacktrace.c b/arch/riscv/kernel/stacktrace.c index c7555447149b..502729ab257b 100644 --- a/arch/riscv/kernel/stacktrace.c +++ b/arch/riscv/kernel/stacktrace.c @@ -46,32 +46,36 @@ static inline int fp_is_valid(unsigned long fp, unsigne= d long sp) } =20 void notrace walk_stackframe(struct task_struct *task, struct pt_regs *reg= s, - bool (*fn)(void *, unsigned long), void *arg) + bool (*fn)(void *, unsigned long, bool), void *arg) { unsigned long fp, sp, pc; int graph_idx =3D 0; int level =3D 0; + bool is_ra; =20 if (regs) { fp =3D frame_pointer(regs); sp =3D user_stack_pointer(regs); pc =3D instruction_pointer(regs); + is_ra =3D false; /* exact instruction pointer */ } else if (task =3D=3D NULL || task =3D=3D current) { fp =3D (unsigned long)__builtin_frame_address(0); sp =3D current_stack_pointer; pc =3D (unsigned long)walk_stackframe; + is_ra =3D false; /* function entry address */ level =3D -1; } else { /* task blocked in __switch_to */ fp =3D task->thread.s[0]; sp =3D task->thread.sp; pc =3D task->thread.ra; + is_ra =3D true; /* return address */ } =20 for (;;) { struct stackframe *frame; =20 - if (unlikely(!__kernel_text_address(pc) || (level++ >=3D 0 && !fn(arg, p= c)))) + if (unlikely(!__kernel_text_address(pc) || (level++ >=3D 0 && !fn(arg, p= c, is_ra)))) break; =20 if (unlikely(!fp_is_valid(fp, sp))) @@ -84,18 +88,21 @@ void notrace walk_stackframe(struct task_struct *task, = struct pt_regs *regs, /* We hit function where ra is not saved on the stack */ fp =3D frame->ra; pc =3D regs->ra; + is_ra =3D true; /* return address */ } else { fp =3D READ_ONCE_TASK_STACK(task, frame->fp); pc =3D READ_ONCE_TASK_STACK(task, frame->ra); pc =3D ftrace_graph_ret_addr(task, &graph_idx, pc, &frame->ra); + is_ra =3D true; /* return address */ if (pc >=3D (unsigned long)handle_exception && pc < (unsigned long)&ret_from_exception_end) { - if (unlikely(!fn(arg, pc))) + if (unlikely(!fn(arg, pc, is_ra))) break; =20 pc =3D ((struct pt_regs *)sp)->epc; fp =3D ((struct pt_regs *)sp)->s0; + is_ra =3D false; /* exact instruction pointer */ } } =20 @@ -105,41 +112,58 @@ void notrace walk_stackframe(struct task_struct *task= , struct pt_regs *regs, #else /* !CONFIG_FRAME_POINTER */ =20 void notrace walk_stackframe(struct task_struct *task, - struct pt_regs *regs, bool (*fn)(void *, unsigned long), void *arg) + struct pt_regs *regs, bool (*fn)(void *, unsigned long, bool), void *arg) { unsigned long sp, pc; unsigned long *ksp; + bool is_ra; =20 if (regs) { sp =3D user_stack_pointer(regs); pc =3D instruction_pointer(regs); + is_ra =3D false; /* exact instruction pointer */ } else if (task =3D=3D NULL || task =3D=3D current) { sp =3D current_stack_pointer; pc =3D (unsigned long)walk_stackframe; + is_ra =3D false; /* function entry address */ } else { /* task blocked in __switch_to */ sp =3D task->thread.sp; pc =3D task->thread.ra; + is_ra =3D true; /* return address */ } =20 if (unlikely(sp & 0x7)) return; =20 + /* + * Scan the stack for kernel text addresses. Without frame pointers + * we cannot distinguish return addresses from other saved text + * addresses (e.g. pt_regs->epc), so all stack values are treated + * as return addresses (is_ra =3D true). This means pt_regs->epc + * values found during the scan may be displayed with %pB instead + * of the correct %pS, which can cause misattribution when the + * exact IP is at offset 0 of a function. + */ ksp =3D (unsigned long *)sp; while (!kstack_end(ksp)) { - if (__kernel_text_address(pc) && unlikely(!fn(arg, pc))) + if (__kernel_text_address(pc) && unlikely(!fn(arg, pc, is_ra))) break; pc =3D READ_ONCE_NOCHECK(*ksp++); + is_ra =3D true; } } =20 #endif /* CONFIG_FRAME_POINTER */ =20 -static bool print_trace_address(void *arg, unsigned long pc) +static bool print_trace_address(void *arg, unsigned long pc, bool is_ra) { const char *loglvl =3D arg; =20 - print_ip_sym(loglvl, pc); + if (is_ra) + printk("%s[<%px>] %pB\n", loglvl, (void *)pc, (void *)pc); + else + printk("%s[<%px>] %pS\n", loglvl, (void *)pc, (void *)pc); return true; } =20 @@ -155,7 +179,7 @@ void show_stack(struct task_struct *task, unsigned long= *sp, const char *loglvl) dump_backtrace(NULL, task, loglvl); } =20 -static bool save_wchan(void *arg, unsigned long pc) +static bool save_wchan(void *arg, unsigned long pc, bool is_ra) { if (!in_sched_functions(pc)) { unsigned long *p =3D arg; @@ -176,10 +200,22 @@ unsigned long __get_wchan(struct task_struct *task) return pc; } =20 +struct stack_walk_cookie { + stack_trace_consume_fn consume; + void *cookie; +}; + +static bool stack_walk_wrapper(void *arg, unsigned long pc, bool is_ra) +{ + struct stack_walk_cookie *ctx =3D arg; + return ctx->consume(ctx->cookie, pc); +} + 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); + struct stack_walk_cookie ctx =3D { .consume =3D consume_entry, .cookie = =3D cookie }; + walk_stackframe(task, regs, stack_walk_wrapper, &ctx); } =20 /* --=20 2.20.1