aboutsummaryrefslogtreecommitdiffstats
path: root/arch/powerpc/kernel/signal_64.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/powerpc/kernel/signal_64.c')
-rw-r--r--arch/powerpc/kernel/signal_64.c43
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(&regs->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, &current->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(&current->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(&current->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 */