Skip to content

Commit a2a4d4a

Browse files
MattBystrinpalmer-dabbelt
authored andcommitted
riscv: stacktrace: fixed walk_stackframe()
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/[email protected]/ [2] https://lore.kernel.org/linux-riscv/[email protected]/ Fixes: f766f77 ("riscv/stacktrace: Fix stack output without ra on the stack top") Signed-off-by: Matthew Bystrin <[email protected]> Reviewed-by: Samuel Holland <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Palmer Dabbelt <[email protected]>
1 parent 7caa976 commit a2a4d4a

File tree

1 file changed

+14
-6
lines changed

1 file changed

+14
-6
lines changed

arch/riscv/kernel/stacktrace.c

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,16 @@
1818

1919
extern asmlinkage void ret_from_exception(void);
2020

21+
static inline int fp_is_valid(unsigned long fp, unsigned long sp)
22+
{
23+
unsigned long low, high;
24+
25+
low = sp + sizeof(struct stackframe);
26+
high = ALIGN(sp, THREAD_SIZE);
27+
28+
return !(fp < low || fp > high || fp & 0x07);
29+
}
30+
2131
void notrace walk_stackframe(struct task_struct *task, struct pt_regs *regs,
2232
bool (*fn)(void *, unsigned long), void *arg)
2333
{
@@ -41,21 +51,19 @@ void notrace walk_stackframe(struct task_struct *task, struct pt_regs *regs,
4151
}
4252

4353
for (;;) {
44-
unsigned long low, high;
4554
struct stackframe *frame;
4655

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

50-
/* Validate frame pointer */
51-
low = sp + sizeof(struct stackframe);
52-
high = ALIGN(sp, THREAD_SIZE);
53-
if (unlikely(fp < low || fp > high || fp & 0x7))
59+
if (unlikely(!fp_is_valid(fp, sp)))
5460
break;
61+
5562
/* Unwind stack frame */
5663
frame = (struct stackframe *)fp - 1;
5764
sp = fp;
58-
if (regs && (regs->epc == pc) && (frame->fp & 0x7)) {
65+
if (regs && (regs->epc == pc) && fp_is_valid(frame->ra, sp)) {
66+
/* We hit function where ra is not saved on the stack */
5967
fp = frame->ra;
6068
pc = regs->ra;
6169
} else {

0 commit comments

Comments
 (0)