| From 304ef4e8367244b547734143c792a2ab764831e8 Mon Sep 17 00:00:00 2001 |
| From: Will Deacon <will.deacon@arm.com> |
| Date: Fri, 23 Nov 2012 12:34:13 +0000 |
| Subject: arm64: signal: push the unwinding prologue on the signal stack |
| |
| From: Will Deacon <will.deacon@arm.com> |
| |
| commit 304ef4e8367244b547734143c792a2ab764831e8 upstream. |
| |
| To allow debuggers to unwind through signal frames, we create a fake |
| stack unwinding prologue containing the link register and frame pointer |
| of the interrupted context. The signal frame is then offset by 16 bytes |
| to make room for the two saved registers which are pushed onto the frame |
| of the *interrupted* context, rather than placed directly above the |
| signal stack. |
| |
| This doesn't work when an alternative signal stack is set up for a SEGV |
| handler, which is raised in response to RLIMIT_STACK being reached. In |
| this case, we try to push the unwinding prologue onto the full stack and |
| subsequently take a fault which we fail to resolve, causing setup_return |
| to return -EFAULT and handle_signal to force_sigsegv on the current task. |
| |
| This patch fixes the problem by including the unwinding prologue as part |
| of the rt_sigframe definition, which is populated during setup_sigframe, |
| ensuring that it always ends up on the signal stack. |
| |
| Signed-off-by: Will Deacon <will.deacon@arm.com> |
| Signed-off-by: Catalin Marinas <catalin.marinas@arm.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| arch/arm64/kernel/signal.c | 37 ++++++++++++++++--------------------- |
| 1 file changed, 16 insertions(+), 21 deletions(-) |
| |
| --- a/arch/arm64/kernel/signal.c |
| +++ b/arch/arm64/kernel/signal.c |
| @@ -41,6 +41,8 @@ |
| struct rt_sigframe { |
| struct siginfo info; |
| struct ucontext uc; |
| + u64 fp; |
| + u64 lr; |
| }; |
| |
| static int preserve_fpsimd_context(struct fpsimd_context __user *ctx) |
| @@ -175,6 +177,10 @@ static int setup_sigframe(struct rt_sigf |
| struct aux_context __user *aux = |
| (struct aux_context __user *)sf->uc.uc_mcontext.__reserved; |
| |
| + /* set up the stack frame for unwinding */ |
| + __put_user_error(regs->regs[29], &sf->fp, err); |
| + __put_user_error(regs->regs[30], &sf->lr, err); |
| + |
| for (i = 0; i < 31; i++) |
| __put_user_error(regs->regs[i], &sf->uc.uc_mcontext.regs[i], |
| err); |
| @@ -210,9 +216,6 @@ static void __user *get_sigframe(struct |
| if ((ka->sa.sa_flags & SA_ONSTACK) && !sas_ss_flags(sp)) |
| sp = sp_top = current->sas_ss_sp + current->sas_ss_size; |
| |
| - /* room for stack frame (FP, LR) */ |
| - sp -= 16; |
| - |
| sp = (sp - framesize) & ~15; |
| frame = (void __user *)sp; |
| |
| @@ -225,20 +228,14 @@ static void __user *get_sigframe(struct |
| return frame; |
| } |
| |
| -static int setup_return(struct pt_regs *regs, struct k_sigaction *ka, |
| - void __user *frame, int usig) |
| +static void setup_return(struct pt_regs *regs, struct k_sigaction *ka, |
| + void __user *frame, int usig) |
| { |
| - int err = 0; |
| __sigrestore_t sigtramp; |
| - unsigned long __user *sp = (unsigned long __user *)regs->sp; |
| - |
| - /* set up the stack frame */ |
| - __put_user_error(regs->regs[29], sp - 2, err); |
| - __put_user_error(regs->regs[30], sp - 1, err); |
| |
| regs->regs[0] = usig; |
| - regs->regs[29] = regs->sp - 16; |
| regs->sp = (unsigned long)frame; |
| + regs->regs[29] = regs->sp + offsetof(struct rt_sigframe, fp); |
| regs->pc = (unsigned long)ka->sa.sa_handler; |
| |
| if (ka->sa.sa_flags & SA_RESTORER) |
| @@ -247,8 +244,6 @@ static int setup_return(struct pt_regs * |
| sigtramp = VDSO_SYMBOL(current->mm->context.vdso, sigtramp); |
| |
| regs->regs[30] = (unsigned long)sigtramp; |
| - |
| - return err; |
| } |
| |
| static int setup_rt_frame(int usig, struct k_sigaction *ka, siginfo_t *info, |
| @@ -272,13 +267,13 @@ static int setup_rt_frame(int usig, stru |
| err |= __copy_to_user(&frame->uc.uc_stack, &stack, sizeof(stack)); |
| |
| err |= setup_sigframe(frame, regs, set); |
| - if (err == 0) |
| - err = setup_return(regs, ka, frame, usig); |
| - |
| - if (err == 0 && ka->sa.sa_flags & SA_SIGINFO) { |
| - err |= copy_siginfo_to_user(&frame->info, info); |
| - regs->regs[1] = (unsigned long)&frame->info; |
| - regs->regs[2] = (unsigned long)&frame->uc; |
| + if (err == 0) { |
| + setup_return(regs, ka, frame, usig); |
| + if (ka->sa.sa_flags & SA_SIGINFO) { |
| + err |= copy_siginfo_to_user(&frame->info, info); |
| + regs->regs[1] = (unsigned long)&frame->info; |
| + regs->regs[2] = (unsigned long)&frame->uc; |
| + } |
| } |
| |
| return err; |