powerpc/signal64: Rewrite handle_rt_signal64() to minimise uaccess switches

Add uaccess blocks and use the 'unsafe' versions of functions doing user
access where possible to reduce the number of times uaccess has to be
opened/closed.

There is no 'unsafe' version of copy_siginfo_to_user, so move it
slightly to allow for a "longer" uaccess block.

Co-developed-by: Christopher M. Riedl <cmr@codefail.de>
Signed-off-by: Daniel Axtens <dja@axtens.net>
Signed-off-by: Christopher M. Riedl <cmr@codefail.de>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Link: https://lore.kernel.org/r/20210227011259.11992-9-cmr@codefail.de
This commit is contained in:
Daniel Axtens 2021-02-26 19:12:57 -06:00 committed by Michael Ellerman
parent 193323e100
commit 96d7a4e06f
1 changed files with 36 additions and 21 deletions

View File

@ -854,45 +854,53 @@ int handle_rt_signal64(struct ksignal *ksig, sigset_t *set,
unsigned long msr = regs->msr; unsigned long msr = regs->msr;
frame = get_sigframe(ksig, tsk, sizeof(*frame), 0); frame = get_sigframe(ksig, tsk, sizeof(*frame), 0);
if (!access_ok(frame, sizeof(*frame)))
/*
* This only applies when calling unsafe_setup_sigcontext() and must be
* called before opening the uaccess window.
*/
if (!MSR_TM_ACTIVE(msr))
prepare_setup_sigcontext(tsk);
if (!user_write_access_begin(frame, sizeof(*frame)))
goto badframe; goto badframe;
err |= __put_user(&frame->info, &frame->pinfo); unsafe_put_user(&frame->info, &frame->pinfo, badframe_block);
err |= __put_user(&frame->uc, &frame->puc); unsafe_put_user(&frame->uc, &frame->puc, badframe_block);
err |= copy_siginfo_to_user(&frame->info, &ksig->info);
if (err)
goto badframe;
/* Create the ucontext. */ /* Create the ucontext. */
err |= __put_user(0, &frame->uc.uc_flags); unsafe_put_user(0, &frame->uc.uc_flags, badframe_block);
err |= __save_altstack(&frame->uc.uc_stack, regs->gpr[1]); unsafe_save_altstack(&frame->uc.uc_stack, regs->gpr[1], badframe_block);
if (MSR_TM_ACTIVE(msr)) { if (MSR_TM_ACTIVE(msr)) {
#ifdef CONFIG_PPC_TRANSACTIONAL_MEM #ifdef CONFIG_PPC_TRANSACTIONAL_MEM
/* The ucontext_t passed to userland points to the second /* The ucontext_t passed to userland points to the second
* ucontext_t (for transactional state) with its uc_link ptr. * ucontext_t (for transactional state) with its uc_link ptr.
*/ */
err |= __put_user(&frame->uc_transact, &frame->uc.uc_link); unsafe_put_user(&frame->uc_transact, &frame->uc.uc_link, badframe_block);
user_write_access_end();
err |= setup_tm_sigcontexts(&frame->uc.uc_mcontext, err |= setup_tm_sigcontexts(&frame->uc.uc_mcontext,
&frame->uc_transact.uc_mcontext, &frame->uc_transact.uc_mcontext,
tsk, ksig->sig, NULL, tsk, ksig->sig, NULL,
(unsigned long)ksig->ka.sa.sa_handler, (unsigned long)ksig->ka.sa.sa_handler,
msr); msr);
if (!user_write_access_begin(&frame->uc.uc_sigmask,
sizeof(frame->uc.uc_sigmask)))
goto badframe;
#endif #endif
} else { } else {
err |= __put_user(0, &frame->uc.uc_link); unsafe_put_user(0, &frame->uc.uc_link, badframe_block);
prepare_setup_sigcontext(tsk); unsafe_setup_sigcontext(&frame->uc.uc_mcontext, tsk, ksig->sig,
if (!user_write_access_begin(&frame->uc.uc_mcontext, NULL, (unsigned long)ksig->ka.sa.sa_handler,
sizeof(frame->uc.uc_mcontext))) 1, badframe_block);
return -EFAULT;
err |= __unsafe_setup_sigcontext(&frame->uc.uc_mcontext, tsk,
ksig->sig, NULL,
(unsigned long)ksig->ka.sa.sa_handler, 1);
user_write_access_end();
} }
err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set));
if (err) unsafe_copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set), badframe_block);
goto badframe; user_write_access_end();
/* Make sure signal handler doesn't get spurious FP exceptions */ /* Make sure signal handler doesn't get spurious FP exceptions */
tsk->thread.fp_state.fpscr = 0; tsk->thread.fp_state.fpscr = 0;
@ -907,6 +915,11 @@ int handle_rt_signal64(struct ksignal *ksig, sigset_t *set,
regs->nip = (unsigned long) &frame->tramp[0]; regs->nip = (unsigned long) &frame->tramp[0];
} }
/* Save the siginfo outside of the unsafe block. */
if (copy_siginfo_to_user(&frame->info, &ksig->info))
goto badframe;
/* Allocate a dummy caller frame for the signal handler. */ /* Allocate a dummy caller frame for the signal handler. */
newsp = ((unsigned long)frame) - __SIGNAL_FRAMESIZE; newsp = ((unsigned long)frame) - __SIGNAL_FRAMESIZE;
err |= put_user(regs->gpr[1], (unsigned long __user *)newsp); err |= put_user(regs->gpr[1], (unsigned long __user *)newsp);
@ -946,6 +959,8 @@ int handle_rt_signal64(struct ksignal *ksig, sigset_t *set,
return 0; return 0;
badframe_block:
user_write_access_end();
badframe: badframe:
signal_fault(current, regs, "handle_rt_signal64", frame); signal_fault(current, regs, "handle_rt_signal64", frame);