[PATCH v2] ARM: unwind: set frame.pc correctly for current-thread unwinding
Ard Biesheuvel
ardb at kernel.org
Wed Mar 9 04:29:59 PST 2022
On Wed, 9 Mar 2022 at 13:19, Russell King (Oracle)
<rmk+kernel at armlinux.org.uk> wrote:
>
> When e.g. a WARN_ON() is encountered, we attempt to unwind the current
> thread. To do this, we set frame.pc to unwind_backtrace, which means it
> points at the beginning of the function. However, the rest of the state
> is initialised from within the function, which means the function
> prologue has already been run.
>
> This can be confusing, and with a recent patch from Ard, can result in
> the unwinder misbehaving if we want to be strict about the PC value.
>
> If we correctly initialise the state so it is self-consistent (in other
> words, set frame.pc to the location we are initialising it) then we
> eliminate this confusion, and avoid possible future issues.
>
> Signed-off-by: Russell King (Oracle) <rmk+kernel at armlinux.org.uk>
Reviewed-by: Ard Biesheuvel <ardb at kernel.org>
> ---
> arch/arm/kernel/return_address.c | 3 ++-
> arch/arm/kernel/stacktrace.c | 3 ++-
> arch/arm/kernel/unwind.c | 7 ++++++-
> 3 files changed, 10 insertions(+), 3 deletions(-)
>
> diff --git a/arch/arm/kernel/return_address.c b/arch/arm/kernel/return_address.c
> index 00c11579406c..8aac1e10b117 100644
> --- a/arch/arm/kernel/return_address.c
> +++ b/arch/arm/kernel/return_address.c
> @@ -41,7 +41,8 @@ void *return_address(unsigned int level)
> frame.fp = (unsigned long)__builtin_frame_address(0);
> frame.sp = current_stack_pointer;
> frame.lr = (unsigned long)__builtin_return_address(0);
> - frame.pc = (unsigned long)return_address;
> +here:
> + frame.pc = (unsigned long)&&here;
> #ifdef CONFIG_KRETPROBES
> frame.kr_cur = NULL;
> frame.tsk = current;
> diff --git a/arch/arm/kernel/stacktrace.c b/arch/arm/kernel/stacktrace.c
> index 75e905508f27..b5efecb3d730 100644
> --- a/arch/arm/kernel/stacktrace.c
> +++ b/arch/arm/kernel/stacktrace.c
> @@ -160,7 +160,8 @@ static noinline void __save_stack_trace(struct task_struct *tsk,
> frame.fp = (unsigned long)__builtin_frame_address(0);
> frame.sp = current_stack_pointer;
> frame.lr = (unsigned long)__builtin_return_address(0);
> - frame.pc = (unsigned long)__save_stack_trace;
> +here:
> + frame.pc = (unsigned long)&&here;
> }
> #ifdef CONFIG_KRETPROBES
> frame.kr_cur = NULL;
> diff --git a/arch/arm/kernel/unwind.c b/arch/arm/kernel/unwind.c
> index a007af0f0209..0e2244e26f37 100644
> --- a/arch/arm/kernel/unwind.c
> +++ b/arch/arm/kernel/unwind.c
> @@ -487,7 +487,12 @@ void unwind_backtrace(struct pt_regs *regs, struct task_struct *tsk,
> frame.fp = (unsigned long)__builtin_frame_address(0);
> frame.sp = current_stack_pointer;
> frame.lr = (unsigned long)__builtin_return_address(0);
> - frame.pc = (unsigned long)unwind_backtrace;
> + /* We are saving the stack and execution state at this
> + * point, so we should ensure that frame.pc is within
> + * this block of code.
> + */
> +here:
> + frame.pc = (unsigned long)&&here;
> } else {
> /* task blocked in __switch_to */
> frame.fp = thread_saved_fp(tsk);
> --
> 2.30.2
>
More information about the linux-arm-kernel
mailing list