Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1bd79336 authored by Paul Mackerras's avatar Paul Mackerras
Browse files

powerpc: Fix various syscall/signal/swapcontext bugs



A careful reading of the recent changes to the system call entry/exit
paths revealed several problems, plus some things that could be
simplified and improved:

* 32-bit wasn't testing the _TIF_NOERROR bit in the syscall fast exit
  path, so it was only doing anything with it once it saw some other
  bit being set.  In other words, the noerror behaviour would apply to
  the next system call where we had to reschedule or deliver a signal,
  which is not necessarily the current system call.

* 32-bit wasn't doing the call to ptrace_notify in the syscall exit
  path when the _TIF_SINGLESTEP bit was set.

* _TIF_RESTOREALL was in both _TIF_USER_WORK_MASK and
  _TIF_PERSYSCALL_MASK, which is odd since _TIF_RESTOREALL is only set
  by system calls.  I took it out of _TIF_USER_WORK_MASK.

* On 64-bit, _TIF_RESTOREALL wasn't causing the non-volatile registers
  to be restored (unless perhaps a signal was delivered or the syscall
  was traced or single-stepped).  Thus the non-volatile registers
  weren't restored on exit from a signal handler.  We probably got
  away with it mostly because signal handlers written in C wouldn't
  alter the non-volatile registers.

* On 32-bit I simplified the code and made it more like 64-bit by
  making the syscall exit path jump to ret_from_except to handle
  preemption and signal delivery.

* 32-bit was calling do_signal unnecessarily when _TIF_RESTOREALL was
  set - but I think because of that 32-bit was actually restoring the
  non-volatile registers on exit from a signal handler.

* I changed the order of enabling interrupts and saving the
  non-volatile registers before calling do_syscall_trace_leave; now we
  enable interrupts first.

Signed-off-by: default avatarPaul Mackerras <paulus@samba.org>
parent ab1b55e2
Loading
Loading
Loading
Loading
+0 −1
Original line number Diff line number Diff line
@@ -92,7 +92,6 @@ int main(void)

	DEFINE(TI_FLAGS, offsetof(struct thread_info, flags));
	DEFINE(TI_PREEMPT, offsetof(struct thread_info, preempt_count));
	DEFINE(TI_SIGFRAME, offsetof(struct thread_info, nvgprs_frame));
	DEFINE(TI_TASK, offsetof(struct thread_info, task));
#ifdef CONFIG_PPC32
	DEFINE(TI_EXECDOMAIN, offsetof(struct thread_info, exec_domain));
+24 −71
Original line number Diff line number Diff line
@@ -227,7 +227,7 @@ ret_from_syscall:
	MTMSRD(r10)
	lwz	r9,TI_FLAGS(r12)
	li	r8,-_LAST_ERRNO
	andi.	r0,r9,(_TIF_SYSCALL_T_OR_A|_TIF_SIGPENDING|_TIF_NEED_RESCHED|_TIF_RESTOREALL|_TIF_RESTORE_SIGMASK)
	andi.	r0,r9,(_TIF_SYSCALL_T_OR_A|_TIF_SINGLESTEP|_TIF_USER_WORK_MASK|_TIF_PERSYSCALL_MASK)
	bne-	syscall_exit_work
	cmplw	0,r3,r8
	blt+	syscall_exit_cont
@@ -287,8 +287,10 @@ syscall_dotrace:

syscall_exit_work:
	andi.	r0,r9,_TIF_RESTOREALL
	bne-	2f
	cmplw	0,r3,r8
	beq+	0f
	REST_NVGPRS(r1)
	b	2f
0:	cmplw	0,r3,r8
	blt+	1f
	andi.	r0,r9,_TIF_NOERROR
	bne-	1f
@@ -302,9 +304,7 @@ syscall_exit_work:
2:	andi.	r0,r9,(_TIF_PERSYSCALL_MASK)
	beq	4f

	/* Clear per-syscall TIF flags if any are set, but _leave_
	_TIF_SAVE_NVGPRS set in r9 since we haven't dealt with that
	yet.  */
	/* Clear per-syscall TIF flags if any are set.  */

	li	r11,_TIF_PERSYSCALL_MASK
	addi	r12,r12,TI_FLAGS
@@ -318,8 +318,13 @@ syscall_exit_work:
	subi	r12,r12,TI_FLAGS
	
4:	/* Anything which requires enabling interrupts? */
	andi.	r0,r9,(_TIF_SYSCALL_T_OR_A|_TIF_SINGLESTEP|_TIF_SAVE_NVGPRS)
	beq	7f
	andi.	r0,r9,(_TIF_SYSCALL_T_OR_A|_TIF_SINGLESTEP)
	beq	ret_from_except

	/* Re-enable interrupts */
	ori	r10,r10,MSR_EE
	SYNC
	MTMSRD(r10)

	/* Save NVGPRS if they're not saved already */
	lwz	r4,_TRAP(r1)
@@ -328,70 +333,10 @@ syscall_exit_work:
	SAVE_NVGPRS(r1)
	li	r4,0xc00
	stw	r4,_TRAP(r1)

	/* Re-enable interrupts */
5:	ori	r10,r10,MSR_EE
	SYNC
	MTMSRD(r10)

	andi.	r0,r9,_TIF_SAVE_NVGPRS
	bne	save_user_nvgprs

save_user_nvgprs_cont:
	andi.	r0,r9,(_TIF_SYSCALL_T_OR_A|_TIF_SINGLESTEP)
	beq	7f

5:
	addi	r3,r1,STACK_FRAME_OVERHEAD
	bl	do_syscall_trace_leave
	REST_NVGPRS(r1)

6:	lwz	r3,GPR3(r1)
	LOAD_MSR_KERNEL(r10,MSR_KERNEL)	/* doesn't include MSR_EE */
	SYNC
	MTMSRD(r10)		/* disable interrupts again */
	rlwinm	r12,r1,0,0,(31-THREAD_SHIFT)	/* current_thread_info() */
	lwz	r9,TI_FLAGS(r12)
7:
	andi.	r0,r9,_TIF_NEED_RESCHED
	bne	8f
	lwz	r5,_MSR(r1)
	andi.	r5,r5,MSR_PR
	beq	ret_from_except
	andi.	r0,r9,_TIF_SIGPENDING|_TIF_RESTORE_SIGMASK
	beq	ret_from_except
	b	do_user_signal
8:
	ori	r10,r10,MSR_EE
	SYNC
	MTMSRD(r10)		/* re-enable interrupts */
	bl	schedule
	b	6b

save_user_nvgprs:
	lwz	r8,TI_SIGFRAME(r12)

.macro savewords start, end
  1:	stw \start,4*(\start)(r8)
	.section __ex_table,"a"
	.align	2
	.long	1b,save_user_nvgprs_fault
	.previous
	.if \end - \start
	savewords "(\start+1)",\end
	.endif
.endm	
	savewords 14,31
	b	save_user_nvgprs_cont

	
save_user_nvgprs_fault:
	li	r3,11		/* SIGSEGV */
	lwz	r4,TI_TASK(r12)
	bl	force_sigsegv

	rlwinm	r12,r1,0,0,(31-THREAD_SHIFT)	/* current_thread_info() */
	lwz	r9,TI_FLAGS(r12)
	b	save_user_nvgprs_cont
	b	ret_from_except_full

#ifdef SHOW_SYSCALLS
do_show_syscall:
@@ -490,6 +435,14 @@ ppc_clone:
	stw	r0,_TRAP(r1)		/* register set saved */
	b	sys_clone

	.globl	ppc_swapcontext
ppc_swapcontext:
	SAVE_NVGPRS(r1)
	lwz	r0,_TRAP(r1)
	rlwinm	r0,r0,0,0,30		/* clear LSB to indicate full */
	stw	r0,_TRAP(r1)		/* register set saved */
	b	sys_swapcontext

/*
 * Top-level page fault handling.
 * This is in assembler because if do_page_fault tells us that
@@ -683,7 +636,7 @@ user_exc_return: /* r10 contains MSR_KERNEL here */
	/* Check current_thread_info()->flags */
	rlwinm	r9,r1,0,0,(31-THREAD_SHIFT)
	lwz	r9,TI_FLAGS(r9)
	andi.	r0,r9,(_TIF_SIGPENDING|_TIF_NEED_RESCHED|_TIF_RESTOREALL|_TIF_RESTORE_SIGMASK)
	andi.	r0,r9,(_TIF_SIGPENDING|_TIF_RESTORE_SIGMASK|_TIF_NEED_RESCHED)
	bne	do_work

restore_user:
+21 −73
Original line number Diff line number Diff line
@@ -160,7 +160,7 @@ syscall_exit:
	mtmsrd	r10,1
	ld	r9,TI_FLAGS(r12)
	li	r11,-_LAST_ERRNO
	andi.	r0,r9,(_TIF_SYSCALL_T_OR_A|_TIF_SINGLESTEP|_TIF_SIGPENDING|_TIF_NEED_RESCHED|_TIF_RESTOREALL|_TIF_SAVE_NVGPRS|_TIF_NOERROR|_TIF_RESTORE_SIGMASK)
	andi.	r0,r9,(_TIF_SYSCALL_T_OR_A|_TIF_SINGLESTEP|_TIF_USER_WORK_MASK|_TIF_PERSYSCALL_MASK)
	bne-	syscall_exit_work
	cmpld	r3,r11
	ld	r5,_CCR(r1)
@@ -216,8 +216,10 @@ syscall_exit_work:
	 If TIF_NOERROR is set, just save r3 as it is. */

	andi.	r0,r9,_TIF_RESTOREALL
	bne-	2f
	cmpld	r3,r11		/* r10 is -LAST_ERRNO */
	beq+	0f
	REST_NVGPRS(r1)
	b	2f
0:	cmpld	r3,r11		/* r10 is -LAST_ERRNO */
	blt+	1f
	andi.	r0,r9,_TIF_NOERROR
	bne-	1f
@@ -229,9 +231,7 @@ syscall_exit_work:
2:	andi.	r0,r9,(_TIF_PERSYSCALL_MASK)
	beq	4f

	/* Clear per-syscall TIF flags if any are set, but _leave_
	_TIF_SAVE_NVGPRS set in r9 since we haven't dealt with that
	yet.  */
	/* Clear per-syscall TIF flags if any are set.  */

	li	r11,_TIF_PERSYSCALL_MASK
	addi	r12,r12,TI_FLAGS
@@ -241,9 +241,8 @@ syscall_exit_work:
	bne-	3b
	subi	r12,r12,TI_FLAGS

4:	bl	.save_nvgprs
	/* Anything else left to do? */
	andi.	r0,r9,(_TIF_SYSCALL_T_OR_A|_TIF_SINGLESTEP|_TIF_SAVE_NVGPRS)
4:	/* Anything else left to do? */
	andi.	r0,r9,(_TIF_SYSCALL_T_OR_A|_TIF_SINGLESTEP)
	beq	.ret_from_except_lite

	/* Re-enable interrupts */
@@ -251,26 +250,10 @@ syscall_exit_work:
	ori	r10,r10,MSR_EE
	mtmsrd	r10,1

	andi.	r0,r9,_TIF_SAVE_NVGPRS
	bne	save_user_nvgprs

	/* If tracing, re-enable interrupts and do it */
save_user_nvgprs_cont:	
	andi.	r0,r9,(_TIF_SYSCALL_T_OR_A|_TIF_SINGLESTEP)
	beq	5f
	
	bl	.save_nvgprs
	addi	r3,r1,STACK_FRAME_OVERHEAD
	bl	.do_syscall_trace_leave
	REST_NVGPRS(r1)
	clrrdi	r12,r1,THREAD_SHIFT

	/* Disable interrupts again and handle other work if any */
5:	mfmsr	r10
	rldicl	r10,r10,48,1
	rotldi	r10,r10,16
	mtmsrd	r10,1

	b	.ret_from_except_lite
	b	.ret_from_except

/* Save non-volatile GPRs, if not already saved. */
_GLOBAL(save_nvgprs)
@@ -283,51 +266,6 @@ _GLOBAL(save_nvgprs)
	blr

	
save_user_nvgprs:
	ld	r10,TI_SIGFRAME(r12)
	andi.	r0,r9,_TIF_32BIT
	beq-	save_user_nvgprs_64

	/* 32-bit save to userspace */

.macro savewords start, end
  1:	stw \start,4*(\start)(r10)
	.section __ex_table,"a"
	.align	3
	.llong	1b,save_user_nvgprs_fault
	.previous
	.if \end - \start
	savewords "(\start+1)",\end
	.endif
.endm	
	savewords 14,31
	b	save_user_nvgprs_cont

save_user_nvgprs_64:
	/* 64-bit save to userspace */

.macro savelongs start, end
  1:	std \start,8*(\start)(r10)
	.section __ex_table,"a"
	.align	3
	.llong	1b,save_user_nvgprs_fault
	.previous
	.if \end - \start
	savelongs "(\start+1)",\end
	.endif
.endm	
	savelongs 14,31
	b	save_user_nvgprs_cont

save_user_nvgprs_fault:
	li	r3,11		/* SIGSEGV */
	ld	r4,TI_TASK(r12)
	bl	.force_sigsegv

	clrrdi	r12,r1,THREAD_SHIFT
	ld	r9,TI_FLAGS(r12)
	b	save_user_nvgprs_cont
	
/*
 * The sigsuspend and rt_sigsuspend system calls can call do_signal
 * and thus put the process into the stopped state where we might
@@ -352,6 +290,16 @@ _GLOBAL(ppc_clone)
	bl	.sys_clone
	b	syscall_exit

_GLOBAL(ppc32_swapcontext)
	bl	.save_nvgprs
	bl	.compat_sys_swapcontext
	b	syscall_exit

_GLOBAL(ppc64_swapcontext)
	bl	.save_nvgprs
	bl	.sys_swapcontext
	b	syscall_exit

_GLOBAL(ret_from_fork)
	bl	.schedule_tail
	REST_NVGPRS(r1)
+1 −4
Original line number Diff line number Diff line
@@ -561,10 +561,7 @@ void do_syscall_trace_leave(struct pt_regs *regs)
				   regs->result);

	if ((test_thread_flag(TIF_SYSCALL_TRACE)
#ifdef CONFIG_PPC64
	     || test_thread_flag(TIF_SINGLESTEP)
#endif
	     )
	     || test_thread_flag(TIF_SINGLESTEP))
	    && (current->ptrace & PT_PTRACED))
		do_syscall_trace();
}
+4 −15
Original line number Diff line number Diff line
@@ -151,10 +151,7 @@ static inline int save_general_regs(struct pt_regs *regs,
	elf_greg_t64 *gregs = (elf_greg_t64 *)regs;
	int i;

	if (!FULL_REGS(regs)) {
		set_thread_flag(TIF_SAVE_NVGPRS);
		current_thread_info()->nvgprs_frame = frame->mc_gregs;
	}
	WARN_ON(!FULL_REGS(regs));

	for (i = 0; i <= PT_RESULT; i ++) {
		if (i == 14 && !FULL_REGS(regs))
@@ -215,15 +212,7 @@ static inline int get_old_sigaction(struct k_sigaction *new_ka,
static inline int save_general_regs(struct pt_regs *regs,
		struct mcontext __user *frame)
{
	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));
		current_thread_info()->nvgprs_frame = &frame->mc_gregs;
		set_thread_flag(TIF_SAVE_NVGPRS);
	}

	WARN_ON(!FULL_REGS(regs));
	return __copy_to_user(&frame->mc_gregs, regs, GP_REGS_SIZE);
}

Loading