diff options
Diffstat (limited to 'kernel/rseq.c')
| -rw-r--r-- | kernel/rseq.c | 48 | 
1 files changed, 29 insertions, 19 deletions
| diff --git a/kernel/rseq.c b/kernel/rseq.c index ae306f90c514..c6242d8594dc 100644 --- a/kernel/rseq.c +++ b/kernel/rseq.c @@ -85,9 +85,9 @@ static int rseq_update_cpu_id(struct task_struct *t)  {  	u32 cpu_id = raw_smp_processor_id(); -	if (__put_user(cpu_id, &t->rseq->cpu_id_start)) +	if (put_user(cpu_id, &t->rseq->cpu_id_start))  		return -EFAULT; -	if (__put_user(cpu_id, &t->rseq->cpu_id)) +	if (put_user(cpu_id, &t->rseq->cpu_id))  		return -EFAULT;  	trace_rseq_update(t);  	return 0; @@ -100,14 +100,14 @@ static int rseq_reset_rseq_cpu_id(struct task_struct *t)  	/*  	 * Reset cpu_id_start to its initial state (0).  	 */ -	if (__put_user(cpu_id_start, &t->rseq->cpu_id_start)) +	if (put_user(cpu_id_start, &t->rseq->cpu_id_start))  		return -EFAULT;  	/*  	 * Reset cpu_id to RSEQ_CPU_ID_UNINITIALIZED, so any user coming  	 * in after unregistration can figure out that rseq needs to be  	 * registered again.  	 */ -	if (__put_user(cpu_id, &t->rseq->cpu_id)) +	if (put_user(cpu_id, &t->rseq->cpu_id))  		return -EFAULT;  	return 0;  } @@ -115,29 +115,36 @@ static int rseq_reset_rseq_cpu_id(struct task_struct *t)  static int rseq_get_rseq_cs(struct task_struct *t, struct rseq_cs *rseq_cs)  {  	struct rseq_cs __user *urseq_cs; -	unsigned long ptr; +	u64 ptr;  	u32 __user *usig;  	u32 sig;  	int ret; -	ret = __get_user(ptr, &t->rseq->rseq_cs); -	if (ret) -		return ret; +	if (copy_from_user(&ptr, &t->rseq->rseq_cs.ptr64, sizeof(ptr))) +		return -EFAULT;  	if (!ptr) {  		memset(rseq_cs, 0, sizeof(*rseq_cs));  		return 0;  	} -	urseq_cs = (struct rseq_cs __user *)ptr; +	if (ptr >= TASK_SIZE) +		return -EINVAL; +	urseq_cs = (struct rseq_cs __user *)(unsigned long)ptr;  	if (copy_from_user(rseq_cs, urseq_cs, sizeof(*rseq_cs)))  		return -EFAULT; -	if (rseq_cs->version > 0) -		return -EINVAL; +	if (rseq_cs->start_ip >= TASK_SIZE || +	    rseq_cs->start_ip + rseq_cs->post_commit_offset >= TASK_SIZE || +	    rseq_cs->abort_ip >= TASK_SIZE || +	    rseq_cs->version > 0) +		return -EINVAL; +	/* Check for overflow. */ +	if (rseq_cs->start_ip + rseq_cs->post_commit_offset < rseq_cs->start_ip) +		return -EINVAL;  	/* Ensure that abort_ip is not in the critical section. */  	if (rseq_cs->abort_ip - rseq_cs->start_ip < rseq_cs->post_commit_offset)  		return -EINVAL; -	usig = (u32 __user *)(rseq_cs->abort_ip - sizeof(u32)); +	usig = (u32 __user *)(unsigned long)(rseq_cs->abort_ip - sizeof(u32));  	ret = get_user(sig, usig);  	if (ret)  		return ret; @@ -146,7 +153,7 @@ static int rseq_get_rseq_cs(struct task_struct *t, struct rseq_cs *rseq_cs)  		printk_ratelimited(KERN_WARNING  			"Possible attack attempt. Unexpected rseq signature 0x%x, expecting 0x%x (pid=%d, addr=%p).\n",  			sig, current->rseq_sig, current->pid, usig); -		return -EPERM; +		return -EINVAL;  	}  	return 0;  } @@ -157,7 +164,7 @@ static int rseq_need_restart(struct task_struct *t, u32 cs_flags)  	int ret;  	/* Get thread flags. */ -	ret = __get_user(flags, &t->rseq->flags); +	ret = get_user(flags, &t->rseq->flags);  	if (ret)  		return ret; @@ -195,9 +202,11 @@ static int clear_rseq_cs(struct task_struct *t)  	 * of code outside of the rseq assembly block. This performs  	 * a lazy clear of the rseq_cs field.  	 * -	 * Set rseq_cs to NULL with single-copy atomicity. +	 * Set rseq_cs to NULL.  	 */ -	return __put_user(0UL, &t->rseq->rseq_cs); +	if (clear_user(&t->rseq->rseq_cs.ptr64, sizeof(t->rseq->rseq_cs.ptr64))) +		return -EFAULT; +	return 0;  }  /* @@ -251,10 +260,10 @@ static int rseq_ip_fixup(struct pt_regs *regs)   * respect to other threads scheduled on the same CPU, and with respect   * to signal handlers.   */ -void __rseq_handle_notify_resume(struct pt_regs *regs) +void __rseq_handle_notify_resume(struct ksignal *ksig, struct pt_regs *regs)  {  	struct task_struct *t = current; -	int ret; +	int ret, sig;  	if (unlikely(t->flags & PF_EXITING))  		return; @@ -268,7 +277,8 @@ void __rseq_handle_notify_resume(struct pt_regs *regs)  	return;  error: -	force_sig(SIGSEGV, t); +	sig = ksig ? ksig->sig : 0; +	force_sigsegv(sig, t);  }  #ifdef CONFIG_DEBUG_RSEQ |