hulk inclusion category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/I9T4EM
----------------------------------------
Due to the fact that the address on the stack points to the return address rather than the call address, for example, when the last instruction of a function is a function call (e.g., to a noreturn function), it can cause the unwinder to incorrectly try to unwind from the function after the callee.
foo: ... bl bar ... end of function and thus next function ...
which results in LR pointing into the next function.
Fixed this by subtracting 1 from frmae->pc in the call frame like ORC on x86 does.
Suggested-by: Josh Poimboeuf jpoimboe@kernel.org Link: https://lkml.kernel.org/lkml/20240305175846.qnyiru7uaa7itqba@treble/ Suggested-by: "Russell King (Oracle)" linux@armlinux.org.uk Link: https://lkml.kernel.org/lkml/Zeg8wRYFemMjcCxG@shell.armlinux.org.uk/ Signed-off-by: Jiangfeng Xiao xiaojiangfeng@huawei.com Signed-off-by: Chen Zhongjin chenzhongjin@huawei.com --- arch/arm/include/asm/stacktrace.h | 1 + arch/arm/kernel/process.c | 1 + arch/arm/kernel/stacktrace.c | 1 + arch/arm/kernel/unwind.c | 5 ++++- 4 files changed, 7 insertions(+), 1 deletion(-)
diff --git a/arch/arm/include/asm/stacktrace.h b/arch/arm/include/asm/stacktrace.h index 2d76a2e29f059..9d082168c6722 100644 --- a/arch/arm/include/asm/stacktrace.h +++ b/arch/arm/include/asm/stacktrace.h @@ -13,6 +13,7 @@ struct stackframe { unsigned long sp; unsigned long lr; unsigned long pc; + bool ex_frame; };
static __always_inline diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c index 2647e48c537e6..1b4cc298574aa 100644 --- a/arch/arm/kernel/process.c +++ b/arch/arm/kernel/process.c @@ -294,6 +294,7 @@ unsigned long get_wchan(struct task_struct *p) frame.lr = 0; /* recovered from the stack */ frame.pc = thread_saved_pc(p); stack_page = (unsigned long)task_stack_page(p); + frame.ex_frame = true; do { if (frame.sp < stack_page || frame.sp >= stack_page + THREAD_SIZE || diff --git a/arch/arm/kernel/stacktrace.c b/arch/arm/kernel/stacktrace.c index 8247749998259..1451893142b76 100644 --- a/arch/arm/kernel/stacktrace.c +++ b/arch/arm/kernel/stacktrace.c @@ -73,6 +73,7 @@ int notrace unwind_frame(struct stackframe *frame) void notrace walk_stackframe(struct stackframe *frame, int (*fn)(struct stackframe *, void *), void *data) { + frame->ex_frame = true; while (1) { int ret;
diff --git a/arch/arm/kernel/unwind.c b/arch/arm/kernel/unwind.c index 9d3a18c1b86c0..ca1f4809f9b66 100644 --- a/arch/arm/kernel/unwind.c +++ b/arch/arm/kernel/unwind.c @@ -35,6 +35,7 @@ #include <asm/stacktrace.h> #include <asm/traps.h> #include <asm/unwind.h> +#include <asm/sections.h>
/* Dummy functions to avoid linker complaints */ void __aeabi_unwind_cpp_pr0(void) @@ -415,7 +416,7 @@ int unwind_frame(struct stackframe *frame) if (!kernel_text_address(frame->pc)) return -URC_FAILURE;
- idx = unwind_find_idx(frame->pc); + idx = unwind_find_idx(frame->ex_frame ? frame->pc : frame->pc - 1); if (!idx) { pr_warn("unwind: Index not found %08lx\n", frame->pc); return -URC_FAILURE; @@ -478,6 +479,7 @@ int unwind_frame(struct stackframe *frame) frame->sp = ctrl.vrs[SP]; frame->lr = ctrl.vrs[LR]; frame->pc = ctrl.vrs[PC]; + frame->ex_frame = in_entry_text(frame->pc);
return URC_OK; } @@ -513,6 +515,7 @@ void unwind_backtrace(struct pt_regs *regs, struct task_struct *tsk, frame.lr = 0; frame.pc = thread_saved_pc(tsk); } + frame.ex_frame = true;
while (1) { int urc;