ARM: unwind: track location of LR value in stack frame
authorArd Biesheuvel <ardb@kernel.org>
Mon, 24 Jan 2022 15:49:09 +0000 (16:49 +0100)
committerArd Biesheuvel <ardb@kernel.org>
Wed, 9 Feb 2022 08:13:43 +0000 (09:13 +0100)
The ftrace graph tracer needs to override the return address of an
instrumented function, in order to install a hook that gets invoked when
the function returns again.

Currently, we only support this when building for ARM using GCC with
frame pointers, as in this case, it is guaranteed that the function will
reload LR from [FP, #-4] in all cases, and we can simply pass that
address to the ftrace code.

In order to support this for configurations that rely on the EABI
unwinder, such as Thumb2 builds, make the unwinder keep track of the
address from which LR was unwound, permitting ftrace to make use of this
in a subsequent patch.

Drop the call to is_kernel_text_address(), which is problematic in terms
of ftrace recursion, given that it may be instrumented itself. The call
is redundant anyway, as no unwind directives will be found unless the PC
points to memory that is known to contain executable code.

Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
Reviewed-by: Nick Desaulniers <ndesaulniers@google.com>
arch/arm/include/asm/stacktrace.h
arch/arm/kernel/Makefile
arch/arm/kernel/unwind.c

index d87d60532b867ef6f0a6ed4833156f0f8a1cf02c..e56503fd9447d00e72fa0e7fe5fa0bd85157b54c 100644 (file)
@@ -14,6 +14,9 @@ struct stackframe {
        unsigned long sp;
        unsigned long lr;
        unsigned long pc;
+
+       /* address of the LR value on the stack */
+       unsigned long *lr_addr;
 #ifdef CONFIG_KRETPROBES
        struct llist_node *kr_cur;
        struct task_struct *tsk;
index ae295a3bcfefddad015d56e79a4af37b80d66487..56511856ff9d2a90c160f9b97b93bdf5fc608148 100644 (file)
@@ -10,6 +10,7 @@ ifdef CONFIG_FUNCTION_TRACER
 CFLAGS_REMOVE_ftrace.o = -pg
 CFLAGS_REMOVE_insn.o = -pg
 CFLAGS_REMOVE_patch.o = -pg
+CFLAGS_REMOVE_unwind.o = -pg
 endif
 
 CFLAGS_REMOVE_return_address.o = -pg
index c5ea328c428d3bb256c567b91c900c26e95f9352..b4e468a7674bcfc6268e57fae90dea51aff08fca 100644 (file)
@@ -55,6 +55,7 @@ struct unwind_ctrl_block {
        const unsigned long *insn;      /* pointer to the current instructions word */
        unsigned long sp_low;           /* lowest value of sp allowed */
        unsigned long sp_high;          /* highest value of sp allowed */
+       unsigned long *lr_addr;         /* address of LR value on the stack */
        /*
         * 1 : check for stack overflow for each register pop.
         * 0 : save overhead if there is plenty of stack remaining.
@@ -239,6 +240,8 @@ static int unwind_pop_register(struct unwind_ctrl_block *ctrl,
         * from being tracked by KASAN.
         */
        ctrl->vrs[reg] = READ_ONCE_NOCHECK(*(*vsp));
+       if (reg == 14)
+               ctrl->lr_addr = *vsp;
        (*vsp)++;
        return URC_OK;
 }
@@ -395,9 +398,6 @@ int unwind_frame(struct stackframe *frame)
        pr_debug("%s(pc = %08lx lr = %08lx sp = %08lx)\n", __func__,
                 frame->pc, frame->lr, frame->sp);
 
-       if (!kernel_text_address(frame->pc))
-               return -URC_FAILURE;
-
        idx = unwind_find_idx(frame->pc);
        if (!idx) {
                pr_warn("unwind: Index not found %08lx\n", frame->pc);
@@ -476,6 +476,7 @@ int unwind_frame(struct stackframe *frame)
        frame->lr = ctrl.vrs[LR];
        frame->pc = ctrl.vrs[PC];
        frame->sp_low = ctrl.sp_low;
+       frame->lr_addr = ctrl.lr_addr;
 
        return URC_OK;
 }