diff options
Diffstat (limited to 'arch/s390/kernel/compat_signal.c')
| -rw-r--r-- | arch/s390/kernel/compat_signal.c | 108 | 
1 files changed, 59 insertions, 49 deletions
diff --git a/arch/s390/kernel/compat_signal.c b/arch/s390/kernel/compat_signal.c index 1389b637dae..f204d692036 100644 --- a/arch/s390/kernel/compat_signal.c +++ b/arch/s390/kernel/compat_signal.c @@ -49,7 +49,7 @@ typedef struct  	__u32 gprs_high[NUM_GPRS];  } rt_sigframe32; -int copy_siginfo_to_user32(compat_siginfo_t __user *to, siginfo_t *from) +int copy_siginfo_to_user32(compat_siginfo_t __user *to, const siginfo_t *from)  {  	int err; @@ -99,7 +99,7 @@ int copy_siginfo_to_user32(compat_siginfo_t __user *to, siginfo_t *from)  			break;  		}  	} -	return err; +	return err ? -EFAULT : 0;  }  int copy_siginfo_from_user32(siginfo_t *to, compat_siginfo_t __user *from) @@ -148,63 +148,72 @@ int copy_siginfo_from_user32(siginfo_t *to, compat_siginfo_t __user *from)  			break;  		}  	} -	return err; +	return err ? -EFAULT : 0;  }  static int save_sigregs32(struct pt_regs *regs, _sigregs32 __user *sregs)  { -	_s390_regs_common32 regs32; -	int err, i; +	_sigregs32 user_sregs; +	int i; -	regs32.psw.mask = psw32_user_bits | -		((__u32)(regs->psw.mask >> 32) & PSW32_MASK_USER); -	regs32.psw.addr = (__u32) regs->psw.addr | +	user_sregs.regs.psw.mask = (__u32)(regs->psw.mask >> 32); +	user_sregs.regs.psw.mask &= PSW32_MASK_USER | PSW32_MASK_RI; +	user_sregs.regs.psw.mask |= PSW32_USER_BITS; +	user_sregs.regs.psw.addr = (__u32) regs->psw.addr |  		(__u32)(regs->psw.mask & PSW_MASK_BA);  	for (i = 0; i < NUM_GPRS; i++) -		regs32.gprs[i] = (__u32) regs->gprs[i]; +		user_sregs.regs.gprs[i] = (__u32) regs->gprs[i];  	save_access_regs(current->thread.acrs); -	memcpy(regs32.acrs, current->thread.acrs, sizeof(regs32.acrs)); -	err = __copy_to_user(&sregs->regs, ®s32, sizeof(regs32)); -	if (err) -		return err; -	save_fp_regs(¤t->thread.fp_regs); -	/* s390_fp_regs and _s390_fp_regs32 are the same ! */ -	return __copy_to_user(&sregs->fpregs, ¤t->thread.fp_regs, -			      sizeof(_s390_fp_regs32)); +	memcpy(&user_sregs.regs.acrs, current->thread.acrs, +	       sizeof(user_sregs.regs.acrs)); +	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(user_sregs.fpregs)); +	if (__copy_to_user(sregs, &user_sregs, sizeof(_sigregs32))) +		return -EFAULT; +	return 0;  }  static int restore_sigregs32(struct pt_regs *regs,_sigregs32 __user *sregs)  { -	_s390_regs_common32 regs32; -	int err, i; +	_sigregs32 user_sregs; +	int i;  	/* Alwys make any pending restarted system call return -EINTR */  	current_thread_info()->restart_block.fn = do_no_restart_syscall; -	err = __copy_from_user(®s32, &sregs->regs, sizeof(regs32)); -	if (err) -		return err; -	regs->psw.mask = (regs->psw.mask & ~PSW_MASK_USER) | -		(__u64)(regs32.psw.mask & PSW32_MASK_USER) << 32 | -		(__u64)(regs32.psw.addr & PSW32_ADDR_AMODE); +	if (__copy_from_user(&user_sregs, &sregs->regs, sizeof(user_sregs))) +		return -EFAULT; + +	if (!is_ri_task(current) && (user_sregs.regs.psw.mask & PSW32_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)) | +		(__u64)(user_sregs.regs.psw.mask & PSW32_MASK_USER) << 32 | +		(__u64)(user_sregs.regs.psw.mask & PSW32_MASK_RI) << 32 | +		(__u64)(user_sregs.regs.psw.addr & PSW32_ADDR_AMODE);  	/* 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); -	regs->psw.addr = (__u64)(regs32.psw.addr & PSW32_ADDR_INSN); +	regs->psw.addr = (__u64)(user_sregs.regs.psw.addr & PSW32_ADDR_INSN);  	for (i = 0; i < NUM_GPRS; i++) -		regs->gprs[i] = (__u64) regs32.gprs[i]; -	memcpy(current->thread.acrs, regs32.acrs, sizeof(current->thread.acrs)); +		regs->gprs[i] = (__u64) user_sregs.regs.gprs[i]; +	memcpy(¤t->thread.acrs, &user_sregs.regs.acrs, +	       sizeof(current->thread.acrs));  	restore_access_regs(current->thread.acrs); -	err = __copy_from_user(¤t->thread.fp_regs, &sregs->fpregs, -			       sizeof(_s390_fp_regs32)); -	current->thread.fp_regs.fpc &= FPC_VALID_MASK; -	if (err) -		return err; +	memcpy(¤t->thread.fp_regs, &user_sregs.fpregs, +	       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;  } @@ -215,24 +224,24 @@ static int save_sigregs_gprs_high(struct pt_regs *regs, __u32 __user *uregs)  	for (i = 0; i < NUM_GPRS; i++)  		gprs_high[i] = regs->gprs[i] >> 32; - -	return __copy_to_user(uregs, &gprs_high, sizeof(gprs_high)); +	if (__copy_to_user(uregs, &gprs_high, sizeof(gprs_high))) +		return -EFAULT; +	return 0;  }  static int restore_sigregs_gprs_high(struct pt_regs *regs, __u32 __user *uregs)  {  	__u32 gprs_high[NUM_GPRS]; -	int err, i; +	int i; -	err = __copy_from_user(&gprs_high, uregs, sizeof(gprs_high)); -	if (err) -		return err; +	if (__copy_from_user(&gprs_high, uregs, sizeof(gprs_high))) +		return -EFAULT;  	for (i = 0; i < NUM_GPRS; i++)  		*(__u32 *)®s->gprs[i] = gprs_high[i];  	return 0;  } -asmlinkage long sys32_sigreturn(void) +COMPAT_SYSCALL_DEFINE0(sigreturn)  {  	struct pt_regs *regs = task_pt_regs(current);  	sigframe32 __user *frame = (sigframe32 __user *)regs->gprs[15]; @@ -251,7 +260,7 @@ badframe:  	return 0;  } -asmlinkage long sys32_rt_sigreturn(void) +COMPAT_SYSCALL_DEFINE0(rt_sigreturn)  {  	struct pt_regs *regs = task_pt_regs(current);  	rt_sigframe32 __user *frame = (rt_sigframe32 __user *)regs->gprs[15]; @@ -348,7 +357,7 @@ static int setup_frame32(int sig, struct k_sigaction *ka,  	regs->gprs[15] = (__force __u64) frame;  	/* Force 31 bit amode and default user address space control. */  	regs->psw.mask = PSW_MASK_BA | -		(psw_user_bits & PSW_MASK_ASC) | +		(PSW_USER_BITS & PSW_MASK_ASC) |  		(regs->psw.mask & ~PSW_MASK_ASC);  	regs->psw.addr = (__force __u64) ka->sa.sa_handler; @@ -403,8 +412,9 @@ static int setup_rt_frame32(int sig, struct k_sigaction *ka, siginfo_t *info,  		regs->gprs[14] = (__u64 __force) ka->sa.sa_restorer | PSW32_ADDR_AMODE;  	} else {  		regs->gprs[14] = (__u64 __force) frame->retcode | PSW32_ADDR_AMODE; -		err |= __put_user(S390_SYSCALL_OPCODE | __NR_rt_sigreturn, -				  (u16 __force __user *)(frame->retcode)); +		if (__put_user(S390_SYSCALL_OPCODE | __NR_rt_sigreturn, +			       (u16 __force __user *)(frame->retcode))) +			goto give_sigsegv;  	}  	/* Set up backchain. */ @@ -415,7 +425,7 @@ static int setup_rt_frame32(int sig, struct k_sigaction *ka, siginfo_t *info,  	regs->gprs[15] = (__force __u64) frame;  	/* Force 31 bit amode and default user address space control. */  	regs->psw.mask = PSW_MASK_BA | -		(psw_user_bits & PSW_MASK_ASC) | +		(PSW_USER_BITS & PSW_MASK_ASC) |  		(regs->psw.mask & ~PSW_MASK_ASC);  	regs->psw.addr = (__u64 __force) ka->sa.sa_handler;  | 
