Commit 96d7a4e0 authored by Daniel Axtens's avatar Daniel Axtens Committed by Michael Ellerman
Browse files

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: default avatarChristopher M. Riedl <cmr@codefail.de>
Signed-off-by: default avatarDaniel Axtens <dja@axtens.net>
Signed-off-by: default avatarChristopher M. Riedl <cmr@codefail.de>
Signed-off-by: default avatarMichael Ellerman <mpe@ellerman.id.au>
Link: https://lore.kernel.org/r/20210227011259.11992-9-cmr@codefail.de
parent 193323e1
Loading
Loading
Loading
Loading
+36 −21
Original line number Diff line number Diff line
@@ -854,45 +854,53 @@ int handle_rt_signal64(struct ksignal *ksig, sigset_t *set,
	unsigned long msr = regs->msr;

	frame = get_sigframe(ksig, tsk, sizeof(*frame), 0);
	if (!access_ok(frame, sizeof(*frame)))
		goto badframe;

	err |= __put_user(&frame->info, &frame->pinfo);
	err |= __put_user(&frame->uc, &frame->puc);
	err |= copy_siginfo_to_user(&frame->info, &ksig->info);
	if (err)
	/*
	 * 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;

	unsafe_put_user(&frame->info, &frame->pinfo, badframe_block);
	unsafe_put_user(&frame->uc, &frame->puc, badframe_block);

	/* Create the ucontext.  */
	err |= __put_user(0, &frame->uc.uc_flags);
	err |= __save_altstack(&frame->uc.uc_stack, regs->gpr[1]);
	unsafe_put_user(0, &frame->uc.uc_flags, badframe_block);
	unsafe_save_altstack(&frame->uc.uc_stack, regs->gpr[1], badframe_block);

	if (MSR_TM_ACTIVE(msr)) {
#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
		/* The ucontext_t passed to userland points to the second
		 * 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,
					    &frame->uc_transact.uc_mcontext,
					    tsk, ksig->sig, NULL,
					    (unsigned long)ksig->ka.sa.sa_handler,
					    msr);

		if (!user_write_access_begin(&frame->uc.uc_sigmask,
					     sizeof(frame->uc.uc_sigmask)))
			goto badframe;

#endif
	} else {
		err |= __put_user(0, &frame->uc.uc_link);
		prepare_setup_sigcontext(tsk);
		if (!user_write_access_begin(&frame->uc.uc_mcontext,
					     sizeof(frame->uc.uc_mcontext)))
			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();
		unsafe_put_user(0, &frame->uc.uc_link, badframe_block);
		unsafe_setup_sigcontext(&frame->uc.uc_mcontext, tsk, ksig->sig,
					NULL, (unsigned long)ksig->ka.sa.sa_handler,
					1, badframe_block);
	}
	err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set));
	if (err)
		goto badframe;

	unsafe_copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set), badframe_block);
	user_write_access_end();

	/* Make sure signal handler doesn't get spurious FP exceptions */
	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];
	}


	/* 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. */
	newsp = ((unsigned long)frame) - __SIGNAL_FRAMESIZE;
	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;

badframe_block:
	user_write_access_end();
badframe:
	signal_fault(current, regs, "handle_rt_signal64", frame);