Commit 578fc9a6 authored by Chen Zhongjin's avatar Chen Zhongjin
Browse files

arm: unwinder: Fix pc off-by-one in arm unwinder

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: default avatarJosh Poimboeuf <jpoimboe@kernel.org>
Link: https://lkml.kernel.org/lkml/20240305175846.qnyiru7uaa7itqba@treble/


Suggested-by: default avatar"Russell King (Oracle)" <linux@armlinux.org.uk>
Link: https://lkml.kernel.org/lkml/Zeg8wRYFemMjcCxG@shell.armlinux.org.uk/


Signed-off-by: default avatarJiangfeng Xiao <xiaojiangfeng@huawei.com>
Signed-off-by: default avatarChen Zhongjin <chenzhongjin@huawei.com>
parent 43993247
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -13,6 +13,7 @@ struct stackframe {
	unsigned long sp;
	unsigned long lr;
	unsigned long pc;
	bool ex_frame;
};

static __always_inline
+1 −0
Original line number Diff line number Diff line
@@ -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 ||
+1 −0
Original line number Diff line number Diff line
@@ -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;

+4 −1
Original line number Diff line number Diff line
@@ -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;