riscv: stacktrace: fixed walk_stackframe()
PATCH v3 1/1 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 0xffffffff8047fe9aThe assembler dump of the function preambula:
add sp,sp,-16
sd s0,8(sp)
add s0,sp,16In 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]/
--- arch/riscv/kernel/stacktrace.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-)
arch/riscv/kernel/stacktrace.c +14 -6
@@ -18,6 +18,16 @@ extern asmlinkage void ret_from_exception(void); +static inline int fp_is_valid (unsigned long fp,unsigned long sp) +{ +unsigned long low, high; + + low = sp +sizeof (struct stackframe ); + high =ALIGN (sp,THREAD_SIZE ); + +return !(fp < low || fp > high || fp &0x07 ); +} + void notrace walk_stackframe(struct task_struct *task, struct pt_regs *regs,bool (*fn)(void *,unsigned long ),void *arg) {
@@ -41,21 +51,19 @@ void notrace walk_stackframe(struct task_struct *task, struct pt_regs *regs, }for (;;) { -unsigned long low, high;struct stackframe *frame;if (unlikely (!__kernel_text_address (pc) || (level++ >=0 && !fn (arg, pc))))break ; -/* Validate frame pointer */ - low = sp +sizeof (struct stackframe ); - high =ALIGN (sp,THREAD_SIZE ); -if (unlikely (fp < low || fp > high || fp &0x7 )) +if (unlikely (!fp_is_valid (fp, sp)))break ; +/* Unwind stack frame */ frame = (struct stackframe *)fp -1 ; sp = fp; -if (regs && (regs->epc == pc) && (frame->fp &0x7 )) { +if (regs && (regs->epc == pc) &&fp_is_valid (frame->ra, sp)) { +/* We hit function where ra is not saved on the stack */ fp = frame->ra; pc = regs->ra; }else {
Signature · 7 lines
2.43.0 _______________________________________________ linux-riscv mailing list [email protected] http://lists.infradead.org/mailman/listinfo/linux-riscv
Quoted 74 lines · Matthew Bystrin
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 0xffffffff8047fe9aThe assembler dump of the function preambula:
add sp,sp,-16 sd s0,8(sp) add s0,sp,16In 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->fpvalue at all, because it is can be uninitialized value on the stack. A better way is to checkframe->rato 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 braceChanges since v1 [1]:
- Instead of just droppingframe->fpcheck, replace it with validation offrame->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 f766f77a74f5 ("riscv/stacktrace: Fix stack output without ra on the stack top")Signed-off-by Matthew Bystrin <[email protected]>--- arch/riscv/kernel/stacktrace.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-)arch/riscv/kernel/stacktrace.c +0
-0
arch/riscv/kernel/stacktrace.c line 21
Quoted 4 lines
arch/riscv/kernel/stacktrace.c +1
-0@@ -18,6 +18,16 @@ extern asmlinkage void ret_from_exception(void); +static inline int fp_is_valid (unsigned long fp,unsigned long sp)
It would make sense for this function to return bool, but the generated code would be the same, so:
Quoted 28 lines
arch/riscv/kernel/stacktrace.c +10
-5+{ +unsigned long low, high; + + low = sp +sizeof (struct stackframe ); + high =ALIGN (sp,THREAD_SIZE ); + +return !(fp < low || fp > high || fp &0x07 ); +} + void notrace walk_stackframe(struct task_struct *task, struct pt_regs *regs,bool (*fn)(void *,unsigned long ),void *arg) {@@ -41,21 +51,19 @@ void notrace walk_stackframe(struct task_struct *task, struct pt_regs *regs, }for (;;) { -unsigned long low, high;struct stackframe *frame;if (unlikely (!__kernel_text_address (pc) || (level++ >=0 && !fn (arg, pc))))break ; -/* Validate frame pointer */ - low = sp +sizeof (struct stackframe ); - high =ALIGN (sp,THREAD_SIZE ); -if (unlikely (fp < low || fp > high || fp &0x7 )) +if (unlikely (!fp_is_valid (fp, sp)))break ;
arch/riscv/kernel/stacktrace.c void notrace walk_stackframe(struct task_struct *task, struct pt_regs *regs, · line 69
Quoted 10 lines
arch/riscv/kernel/stacktrace.c +3
-1+/* Unwind stack frame */ frame = (struct stackframe *)fp -1 ; sp = fp; -if (regs && (regs->epc == pc) && (frame->fp &0x7 )) { +if (regs && (regs->epc == pc) &&fp_is_valid (frame->ra, sp)) { +/* We hit function where ra is not saved on the stack */ fp = frame->ra; pc = regs->ra; }else {
_______________________________________________
linux-riscv mailing list
[email protected]
http://lists.infradead.org/mailman/listinfo/linux-riscv
This patch was applied to riscv/linux.git (for-next) by Palmer Dabbelt <[email protected]>:
Hello:
This patch was applied to riscv/linux.git (for-next) by Palmer Dabbelt <[email protected]>:
Quoted 6 lines · On Tue, 21 May 2024 22:13:13 +0300 you wrote:
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 ]---
Quoted 7 lines
Registers dump:
ra 0xffffffff80485758 <regmap_mmio_read+36> sp 0xffffffc80200b9a0 fp 0xffffffc80200b9b0 pc 0xffffffff804853ba <regmap_mmio_read32le+6>[...]
Here is the summary with links:
- [v3] riscv: stacktrace: fixed walk_stackframe()
https://git.kernel.org/riscv/c/348212546af0
You are awesome, thank you!
Signature · 9 lines
Deet-doot-dot, I am a bot. https://korg.docs.kernel.org/patchwork/pwbot.html _______________________________________________ linux-riscv mailing list [email protected] http://lists.infradead.org/mailman/listinfo/linux-riscv