diff options
Diffstat (limited to 'arch/powerpc/kernel/signal_64.c')
-rw-r--r-- | arch/powerpc/kernel/signal_64.c | 43 |
1 files changed, 29 insertions, 14 deletions
diff --git a/arch/powerpc/kernel/signal_64.c b/arch/powerpc/kernel/signal_64.c index 1decf2785530e..7b9d999e21150 100644 --- a/arch/powerpc/kernel/signal_64.c +++ b/arch/powerpc/kernel/signal_64.c @@ -96,8 +96,10 @@ long sys_rt_sigsuspend(sigset_t __user *unewset, size_t sigsetsize, int p3, int while (1) { current->state = TASK_INTERRUPTIBLE; schedule(); - if (do_signal(&saveset, regs)) + if (do_signal(&saveset, regs)) { + set_thread_flag(TIF_RESTOREALL); return 0; + } } } @@ -152,6 +154,14 @@ static long setup_sigcontext(struct sigcontext __user *sc, struct pt_regs *regs, err |= __put_user(0, &sc->v_regs); #endif /* CONFIG_ALTIVEC */ err |= __put_user(&sc->gp_regs, &sc->regs); + if (!FULL_REGS(regs)) { + /* Zero out the unsaved GPRs to avoid information + leak, and set TIF_SAVE_NVGPRS to ensure that the + registers do actually get saved later. */ + memset(®s->gpr[14], 0, 18 * sizeof(unsigned long)); + set_thread_flag(TIF_SAVE_NVGPRS); + current_thread_info()->nvgprs_frame = &sc->gp_regs; + } err |= __copy_to_user(&sc->gp_regs, regs, GP_REGS_SIZE); err |= __copy_to_user(&sc->fp_regs, ¤t->thread.fpr, FP_REGS_SIZE); err |= __put_user(signr, &sc->signal); @@ -197,10 +207,20 @@ static long restore_sigcontext(struct pt_regs *regs, sigset_t *set, int sig, if (!sig) regs->gpr[13] = save_r13; - err |= __copy_from_user(¤t->thread.fpr, &sc->fp_regs, FP_REGS_SIZE); if (set != NULL) err |= __get_user(set->sig[0], &sc->oldmask); + /* + * Do this before updating the thread state in + * current->thread.fpr/vr. That way, if we get preempted + * and another task grabs the FPU/Altivec, it won't be + * tempted to save the current CPU state into the thread_struct + * and corrupt what we are writing there. + */ + discard_lazy_cpu_state(); + + err |= __copy_from_user(¤t->thread.fpr, &sc->fp_regs, FP_REGS_SIZE); + #ifdef CONFIG_ALTIVEC err |= __get_user(v_regs, &sc->v_regs); err |= __get_user(msr, &sc->gp_regs[PT_MSR]); @@ -219,14 +239,6 @@ static long restore_sigcontext(struct pt_regs *regs, sigset_t *set, int sig, current->thread.vrsave = 0; #endif /* CONFIG_ALTIVEC */ -#ifndef CONFIG_SMP - preempt_disable(); - if (last_task_used_math == current) - last_task_used_math = NULL; - if (last_task_used_altivec == current) - last_task_used_altivec = NULL; - preempt_enable(); -#endif /* Force reload of FP/VEC */ regs->msr &= ~(MSR_FP | MSR_FE0 | MSR_FE1 | MSR_VEC); @@ -340,6 +352,7 @@ int sys_swapcontext(struct ucontext __user *old_ctx, do_exit(SIGSEGV); /* This returns like rt_sigreturn */ + set_thread_flag(TIF_RESTOREALL); return 0; } @@ -372,7 +385,8 @@ int sys_rt_sigreturn(unsigned long r3, unsigned long r4, unsigned long r5, */ do_sigaltstack(&uc->uc_stack, NULL, regs->gpr[1]); - return regs->result; + set_thread_flag(TIF_RESTOREALL); + return 0; badframe: #if DEBUG_SIG @@ -454,9 +468,6 @@ static int setup_rt_frame(int signr, struct k_sigaction *ka, siginfo_t *info, if (err) goto badframe; - if (test_thread_flag(TIF_SINGLESTEP)) - ptrace_notify(SIGTRAP); - return 1; badframe: @@ -502,6 +513,8 @@ static inline void syscall_restart(struct pt_regs *regs, struct k_sigaction *ka) * we only get here if there is a handler, we dont restart. */ regs->result = -EINTR; + regs->gpr[3] = EINTR; + regs->ccr |= 0x10000000; break; case -ERESTARTSYS: /* ERESTARTSYS means to restart the syscall if there is no @@ -509,6 +522,8 @@ static inline void syscall_restart(struct pt_regs *regs, struct k_sigaction *ka) */ if (!(ka->sa.sa_flags & SA_RESTART)) { regs->result = -EINTR; + regs->gpr[3] = EINTR; + regs->ccr |= 0x10000000; break; } /* fallthrough */ |