Skip to content

Commit 8e2ed91

Browse files
MattBystrinroxanan1996
authored andcommitted
riscv: stacktrace: fixed walk_stackframe()
BugLink: https://bugs.launchpad.net/bugs/2072617 [ Upstream commit a2a4d4a ] If the load access fault occures in a leaf function (with CONFIG_FRAME_POINTER=y), when wrong stack trace will be displayed: [<ffffffff804853c2>] regmap_mmio_read32le+0xe/0x1c ---[ end trace 0000000000000000 ]--- Registers dump: ra 0xffffffff80485758 <regmap_mmio_read+36> sp 0xffffffc80200b9a0 fp 0xffffffc80200b9b0 pc 0xffffffff804853ba <regmap_mmio_read32le+6> Stack dump: 0xffffffc80200b9a0: 0xffffffc80200b9e0 0xffffffc80200b9e0 0xffffffc80200b9b0: 0xffffffff8116d7e8 0x0000000000000100 0xffffffc80200b9c0: 0xffffffd8055b9400 0xffffffd8055b9400 0xffffffc80200b9d0: 0xffffffc80200b9f0 0xffffffff8047c526 0xffffffc80200b9e0: 0xffffffc80200ba30 0xffffffff8047fe9a The assembler dump of the function preambula: add sp,sp,-16 sd s0,8(sp) add s0,sp,16 In the fist stack frame, where ra is not stored on the stack we can observe: 0(sp) 8(sp) .---------------------------------------------. sp->| frame->fp | frame->ra (saved fp) | |---------------------------------------------| fp->| .... | .... | |---------------------------------------------| | | | and in the code check is performed: if (regs && (regs->epc == pc) && (frame->fp & 0x7)) I see no reason to check frame->fp value at all, because it is can be uninitialized value on the stack. A better way is to check frame->ra to be an address on the stack. After the stacktrace shows as expect: [<ffffffff804853c2>] regmap_mmio_read32le+0xe/0x1c [<ffffffff80485758>] regmap_mmio_read+0x24/0x52 [<ffffffff8047c526>] _regmap_bus_reg_read+0x1a/0x22 [<ffffffff8047fe9a>] _regmap_read+0x5c/0xea [<ffffffff80480376>] _regmap_update_bits+0x76/0xc0 ... ---[ end trace 0000000000000000 ]--- As pointed by Samuel Holland it is incorrect to remove check of the stackframe entirely. Changes since v2 [2]: - Add accidentally forgotten curly brace Changes since v1 [1]: - Instead of just dropping frame->fp check, replace it with validation of frame->ra, which should be a stack address. - Move frame pointer validation into the separate function. [1] https://lore.kernel.org/linux-riscv/20240426072701.6463-1-dev.mbstr@gmail.com/ [2] https://lore.kernel.org/linux-riscv/20240521131314.48895-1-dev.mbstr@gmail.com/ Fixes: f766f77 ("riscv/stacktrace: Fix stack output without ra on the stack top") Signed-off-by: Matthew Bystrin <dev.mbstr@gmail.com> Reviewed-by: Samuel Holland <samuel.holland@sifive.com> Link: https://lore.kernel.org/r/20240521191727.62012-1-dev.mbstr@gmail.com Signed-off-by: Palmer Dabbelt <palmer@rivosinc.com> Signed-off-by: Sasha Levin <sashal@kernel.org> Signed-off-by: Portia Stephens <portia.stephens@canonical.com> Signed-off-by: Stefan Bader <stefan.bader@canonical.com>
1 parent 6ff97b3 commit 8e2ed91

1 file changed

Lines changed: 14 additions & 6 deletions

File tree

arch/riscv/kernel/stacktrace.c

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,16 @@ register unsigned long sp_in_global __asm__("sp");
2020

2121
extern asmlinkage void ret_from_exception(void);
2222

23+
static inline int fp_is_valid(unsigned long fp, unsigned long sp)
24+
{
25+
unsigned long low, high;
26+
27+
low = sp + sizeof(struct stackframe);
28+
high = ALIGN(sp, THREAD_SIZE);
29+
30+
return !(fp < low || fp > high || fp & 0x07);
31+
}
32+
2333
void notrace walk_stackframe(struct task_struct *task, struct pt_regs *regs,
2434
bool (*fn)(void *, unsigned long), void *arg)
2535
{
@@ -43,21 +53,19 @@ void notrace walk_stackframe(struct task_struct *task, struct pt_regs *regs,
4353
}
4454

4555
for (;;) {
46-
unsigned long low, high;
4756
struct stackframe *frame;
4857

4958
if (unlikely(!__kernel_text_address(pc) || (level++ >= 0 && !fn(arg, pc))))
5059
break;
5160

52-
/* Validate frame pointer */
53-
low = sp + sizeof(struct stackframe);
54-
high = ALIGN(sp, THREAD_SIZE);
55-
if (unlikely(fp < low || fp > high || fp & 0x7))
61+
if (unlikely(!fp_is_valid(fp, sp)))
5662
break;
63+
5764
/* Unwind stack frame */
5865
frame = (struct stackframe *)fp - 1;
5966
sp = fp;
60-
if (regs && (regs->epc == pc) && (frame->fp & 0x7)) {
67+
if (regs && (regs->epc == pc) && fp_is_valid(frame->ra, sp)) {
68+
/* We hit function where ra is not saved on the stack */
6169
fp = frame->ra;
6270
pc = regs->ra;
6371
} else {

0 commit comments

Comments
 (0)