Skip to content

Commit

Permalink
ARM: 8992/1: Fix unwind_frame for clang-built kernels
Browse files Browse the repository at this point in the history
commit b4d5ec9 upstream.

Since clang does not push pc and sp in function prologues, the current
implementation of unwind_frame does not work. By using the previous
frame's lr/fp instead of saved pc/sp we get valid unwinds on clang-built
kernels.

The bounds check on next frame pointer must be changed as well since
there are 8 less bytes between frames.

This fixes /proc/<pid>/stack.

Link: ClangBuiltLinux/linux#912

Reported-by: Miles Chen <miles.chen@mediatek.com>
Tested-by: Miles Chen <miles.chen@mediatek.com>
Cc: stable@vger.kernel.org
Reviewed-by: Nick Desaulniers <ndesaulniers@google.com>
Signed-off-by: Nathan Huckleberry <nhuck@google.com>
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
  • Loading branch information
nhukc authored and gregkh committed Aug 19, 2020
1 parent cd17453 commit 35e1338
Showing 1 changed file with 24 additions and 0 deletions.
24 changes: 24 additions & 0 deletions arch/arm/kernel/stacktrace.c
Expand Up @@ -22,6 +22,19 @@
* A simple function epilogue looks like this:
* ldm sp, {fp, sp, pc}
*
* When compiled with clang, pc and sp are not pushed. A simple function
* prologue looks like this when built with clang:
*
* stmdb {..., fp, lr}
* add fp, sp, #x
* sub sp, sp, #y
*
* A simple function epilogue looks like this when built with clang:
*
* sub sp, fp, #x
* ldm {..., fp, pc}
*
*
* Note that with framepointer enabled, even the leaf functions have the same
* prologue and epilogue, therefore we can ignore the LR value in this case.
*/
Expand All @@ -34,6 +47,16 @@ int notrace unwind_frame(struct stackframe *frame)
low = frame->sp;
high = ALIGN(low, THREAD_SIZE);

#ifdef CONFIG_CC_IS_CLANG
/* check current frame pointer is within bounds */
if (fp < low + 4 || fp > high - 4)
return -EINVAL;

frame->sp = frame->fp;
frame->fp = *(unsigned long *)(fp);
frame->pc = frame->lr;
frame->lr = *(unsigned long *)(fp + 4);
#else
/* check current frame pointer is within bounds */
if (fp < low + 12 || fp > high - 4)
return -EINVAL;
Expand All @@ -42,6 +65,7 @@ int notrace unwind_frame(struct stackframe *frame)
frame->fp = *(unsigned long *)(fp - 12);
frame->sp = *(unsigned long *)(fp - 8);
frame->pc = *(unsigned long *)(fp - 4);
#endif

return 0;
}
Expand Down

0 comments on commit 35e1338

Please sign in to comment.