diff options
Diffstat (limited to 'arch/s390/kernel/signal.c')
| -rw-r--r-- | arch/s390/kernel/signal.c | 63 | 
1 files changed, 35 insertions, 28 deletions
diff --git a/arch/s390/kernel/signal.c b/arch/s390/kernel/signal.c index c45becf82e0..42b49f9e19b 100644 --- a/arch/s390/kernel/signal.c +++ b/arch/s390/kernel/signal.c @@ -57,40 +57,48 @@ static int save_sigregs(struct pt_regs *regs, _sigregs __user *sregs)  	/* Copy a 'clean' PSW mask to the user to avoid leaking  	   information about whether PER is currently on.  */ -	user_sregs.regs.psw.mask = psw_user_bits | -		(regs->psw.mask & PSW_MASK_USER); +	user_sregs.regs.psw.mask = PSW_USER_BITS | +		(regs->psw.mask & (PSW_MASK_USER | PSW_MASK_RI));  	user_sregs.regs.psw.addr = regs->psw.addr;  	memcpy(&user_sregs.regs.gprs, ®s->gprs, sizeof(sregs->regs.gprs));  	memcpy(&user_sregs.regs.acrs, current->thread.acrs, -	       sizeof(sregs->regs.acrs)); +	       sizeof(user_sregs.regs.acrs));  	/*   	 * We have to store the fp registers to current->thread.fp_regs  	 * to merge them with the emulated registers.  	 */ -	save_fp_regs(¤t->thread.fp_regs); +	save_fp_ctl(¤t->thread.fp_regs.fpc); +	save_fp_regs(current->thread.fp_regs.fprs);  	memcpy(&user_sregs.fpregs, ¤t->thread.fp_regs, -	       sizeof(s390_fp_regs)); -	return __copy_to_user(sregs, &user_sregs, sizeof(_sigregs)); +	       sizeof(user_sregs.fpregs)); +	if (__copy_to_user(sregs, &user_sregs, sizeof(_sigregs))) +		return -EFAULT; +	return 0;  } -/* Returns positive number on error */  static int restore_sigregs(struct pt_regs *regs, _sigregs __user *sregs)  { -	int err;  	_sigregs user_sregs;  	/* Alwys make any pending restarted system call return -EINTR */  	current_thread_info()->restart_block.fn = do_no_restart_syscall; -	err = __copy_from_user(&user_sregs, sregs, sizeof(_sigregs)); -	if (err) -		return err; -	/* Use regs->psw.mask instead of psw_user_bits to preserve PER bit. */ -	regs->psw.mask = (regs->psw.mask & ~PSW_MASK_USER) | -		(user_sregs.regs.psw.mask & PSW_MASK_USER); +	if (__copy_from_user(&user_sregs, sregs, sizeof(user_sregs))) +		return -EFAULT; + +	if (!is_ri_task(current) && (user_sregs.regs.psw.mask & PSW_MASK_RI)) +		return -EINVAL; + +	/* Loading the floating-point-control word can fail. Do that first. */ +	if (restore_fp_ctl(&user_sregs.fpregs.fpc)) +		return -EINVAL; + +	/* Use regs->psw.mask instead of PSW_USER_BITS to preserve PER bit. */ +	regs->psw.mask = (regs->psw.mask & ~(PSW_MASK_USER | PSW_MASK_RI)) | +		(user_sregs.regs.psw.mask & (PSW_MASK_USER | PSW_MASK_RI));  	/* Check for invalid user address space control. */ -	if ((regs->psw.mask & PSW_MASK_ASC) >= (psw_kernel_bits & PSW_MASK_ASC)) -		regs->psw.mask = (psw_user_bits & PSW_MASK_ASC) | +	if ((regs->psw.mask & PSW_MASK_ASC) == PSW_ASC_HOME) +		regs->psw.mask = PSW_ASC_PRIMARY |  			(regs->psw.mask & ~PSW_MASK_ASC);  	/* Check for invalid amode */  	if (regs->psw.mask & PSW_MASK_EA) @@ -98,15 +106,14 @@ static int restore_sigregs(struct pt_regs *regs, _sigregs __user *sregs)  	regs->psw.addr = user_sregs.regs.psw.addr;  	memcpy(®s->gprs, &user_sregs.regs.gprs, sizeof(sregs->regs.gprs));  	memcpy(¤t->thread.acrs, &user_sregs.regs.acrs, -	       sizeof(sregs->regs.acrs)); +	       sizeof(current->thread.acrs));  	restore_access_regs(current->thread.acrs);  	memcpy(¤t->thread.fp_regs, &user_sregs.fpregs, -	       sizeof(s390_fp_regs)); -	current->thread.fp_regs.fpc &= FPC_VALID_MASK; +	       sizeof(current->thread.fp_regs)); -	restore_fp_regs(¤t->thread.fp_regs); -	clear_thread_flag(TIF_SYSCALL);	/* No longer in a system call */ +	restore_fp_regs(current->thread.fp_regs.fprs); +	clear_pt_regs_flag(regs, PIF_SYSCALL); /* No longer in a system call */  	return 0;  } @@ -224,7 +231,7 @@ static int setup_frame(int sig, struct k_sigaction *ka,  	regs->gprs[15] = (unsigned long) frame;  	/* Force default amode and default user address space control. */  	regs->psw.mask = PSW_MASK_EA | PSW_MASK_BA | -		(psw_user_bits & PSW_MASK_ASC) | +		(PSW_USER_BITS & PSW_MASK_ASC) |  		(regs->psw.mask & ~PSW_MASK_ASC);  	regs->psw.addr = (unsigned long) ka->sa.sa_handler | PSW_ADDR_AMODE; @@ -295,7 +302,7 @@ static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,  	regs->gprs[15] = (unsigned long) frame;  	/* Force default amode and default user address space control. */  	regs->psw.mask = PSW_MASK_EA | PSW_MASK_BA | -		(psw_user_bits & PSW_MASK_ASC) | +		(PSW_USER_BITS & PSW_MASK_ASC) |  		(regs->psw.mask & ~PSW_MASK_ASC);  	regs->psw.addr = (unsigned long) ka->sa.sa_handler | PSW_ADDR_AMODE; @@ -349,7 +356,7 @@ void do_signal(struct pt_regs *regs)  	 * call information.  	 */  	current_thread_info()->system_call = -		test_thread_flag(TIF_SYSCALL) ? regs->int_code : 0; +		test_pt_regs_flag(regs, PIF_SYSCALL) ? regs->int_code : 0;  	signr = get_signal_to_deliver(&info, &ka, regs, NULL);  	if (signr > 0) { @@ -377,7 +384,7 @@ void do_signal(struct pt_regs *regs)  			}  		}  		/* No longer in a system call */ -		clear_thread_flag(TIF_SYSCALL); +		clear_pt_regs_flag(regs, PIF_SYSCALL);  		if (is_compat_task())  			handle_signal32(signr, &ka, &info, oldset, regs); @@ -387,7 +394,7 @@ void do_signal(struct pt_regs *regs)  	}  	/* No handlers present - check for system call restart */ -	clear_thread_flag(TIF_SYSCALL); +	clear_pt_regs_flag(regs, PIF_SYSCALL);  	if (current_thread_info()->system_call) {  		regs->int_code = current_thread_info()->system_call;  		switch (regs->gprs[2]) { @@ -400,9 +407,9 @@ void do_signal(struct pt_regs *regs)  		case -ERESTARTNOINTR:  			/* Restart system call with magic TIF bit. */  			regs->gprs[2] = regs->orig_gpr2; -			set_thread_flag(TIF_SYSCALL); +			set_pt_regs_flag(regs, PIF_SYSCALL);  			if (test_thread_flag(TIF_SINGLE_STEP)) -				set_thread_flag(TIF_PER_TRAP); +				clear_pt_regs_flag(regs, PIF_PER_TRAP);  			break;  		}  	}  | 
