diff options
Diffstat (limited to 'kernel/rseq.c')
| -rw-r--r-- | kernel/rseq.c | 65 | 
1 files changed, 52 insertions, 13 deletions
diff --git a/kernel/rseq.c b/kernel/rseq.c index d38ab944105d..9de6e35fe679 100644 --- a/kernel/rseq.c +++ b/kernel/rseq.c @@ -18,6 +18,9 @@  #define CREATE_TRACE_POINTS  #include <trace/events/rseq.h> +/* The original rseq structure size (including padding) is 32 bytes. */ +#define ORIG_RSEQ_SIZE		32 +  #define RSEQ_CS_NO_RESTART_FLAGS (RSEQ_CS_FLAG_NO_RESTART_ON_PREEMPT | \  				  RSEQ_CS_FLAG_NO_RESTART_ON_SIGNAL | \  				  RSEQ_CS_FLAG_NO_RESTART_ON_MIGRATE) @@ -82,15 +85,25 @@   *   F1. <failure>   */ -static int rseq_update_cpu_id(struct task_struct *t) +static int rseq_update_cpu_node_id(struct task_struct *t)  { -	u32 cpu_id = raw_smp_processor_id();  	struct rseq __user *rseq = t->rseq; +	u32 cpu_id = raw_smp_processor_id(); +	u32 node_id = cpu_to_node(cpu_id); +	u32 mm_cid = task_mm_cid(t); -	if (!user_write_access_begin(rseq, sizeof(*rseq))) +	WARN_ON_ONCE((int) mm_cid < 0); +	if (!user_write_access_begin(rseq, t->rseq_len))  		goto efault;  	unsafe_put_user(cpu_id, &rseq->cpu_id_start, efault_end);  	unsafe_put_user(cpu_id, &rseq->cpu_id, efault_end); +	unsafe_put_user(node_id, &rseq->node_id, efault_end); +	unsafe_put_user(mm_cid, &rseq->mm_cid, efault_end); +	/* +	 * Additional feature fields added after ORIG_RSEQ_SIZE +	 * need to be conditionally updated only if +	 * t->rseq_len != ORIG_RSEQ_SIZE. +	 */  	user_write_access_end();  	trace_rseq_update(t);  	return 0; @@ -101,9 +114,10 @@ efault:  	return -EFAULT;  } -static int rseq_reset_rseq_cpu_id(struct task_struct *t) +static int rseq_reset_rseq_cpu_node_id(struct task_struct *t)  { -	u32 cpu_id_start = 0, cpu_id = RSEQ_CPU_ID_UNINITIALIZED; +	u32 cpu_id_start = 0, cpu_id = RSEQ_CPU_ID_UNINITIALIZED, node_id = 0, +	    mm_cid = 0;  	/*  	 * Reset cpu_id_start to its initial state (0). @@ -117,6 +131,21 @@ static int rseq_reset_rseq_cpu_id(struct task_struct *t)  	 */  	if (put_user(cpu_id, &t->rseq->cpu_id))  		return -EFAULT; +	/* +	 * Reset node_id to its initial state (0). +	 */ +	if (put_user(node_id, &t->rseq->node_id)) +		return -EFAULT; +	/* +	 * Reset mm_cid to its initial state (0). +	 */ +	if (put_user(mm_cid, &t->rseq->mm_cid)) +		return -EFAULT; +	/* +	 * Additional feature fields added after ORIG_RSEQ_SIZE +	 * need to be conditionally reset only if +	 * t->rseq_len != ORIG_RSEQ_SIZE. +	 */  	return 0;  } @@ -301,7 +330,7 @@ void __rseq_handle_notify_resume(struct ksignal *ksig, struct pt_regs *regs)  		if (unlikely(ret < 0))  			goto error;  	} -	if (unlikely(rseq_update_cpu_id(t))) +	if (unlikely(rseq_update_cpu_node_id(t)))  		goto error;  	return; @@ -344,15 +373,16 @@ SYSCALL_DEFINE4(rseq, struct rseq __user *, rseq, u32, rseq_len,  		/* Unregister rseq for current thread. */  		if (current->rseq != rseq || !current->rseq)  			return -EINVAL; -		if (rseq_len != sizeof(*rseq)) +		if (rseq_len != current->rseq_len)  			return -EINVAL;  		if (current->rseq_sig != sig)  			return -EPERM; -		ret = rseq_reset_rseq_cpu_id(current); +		ret = rseq_reset_rseq_cpu_node_id(current);  		if (ret)  			return ret;  		current->rseq = NULL;  		current->rseq_sig = 0; +		current->rseq_len = 0;  		return 0;  	} @@ -365,7 +395,7 @@ SYSCALL_DEFINE4(rseq, struct rseq __user *, rseq, u32, rseq_len,  		 * the provided address differs from the prior  		 * one.  		 */ -		if (current->rseq != rseq || rseq_len != sizeof(*rseq)) +		if (current->rseq != rseq || rseq_len != current->rseq_len)  			return -EINVAL;  		if (current->rseq_sig != sig)  			return -EPERM; @@ -374,15 +404,24 @@ SYSCALL_DEFINE4(rseq, struct rseq __user *, rseq, u32, rseq_len,  	}  	/* -	 * If there was no rseq previously registered, -	 * ensure the provided rseq is properly aligned and valid. +	 * If there was no rseq previously registered, ensure the provided rseq +	 * is properly aligned, as communcated to user-space through the ELF +	 * auxiliary vector AT_RSEQ_ALIGN. If rseq_len is the original rseq +	 * size, the required alignment is the original struct rseq alignment. +	 * +	 * In order to be valid, rseq_len is either the original rseq size, or +	 * large enough to contain all supported fields, as communicated to +	 * user-space through the ELF auxiliary vector AT_RSEQ_FEATURE_SIZE.  	 */ -	if (!IS_ALIGNED((unsigned long)rseq, __alignof__(*rseq)) || -	    rseq_len != sizeof(*rseq)) +	if (rseq_len < ORIG_RSEQ_SIZE || +	    (rseq_len == ORIG_RSEQ_SIZE && !IS_ALIGNED((unsigned long)rseq, ORIG_RSEQ_SIZE)) || +	    (rseq_len != ORIG_RSEQ_SIZE && (!IS_ALIGNED((unsigned long)rseq, __alignof__(*rseq)) || +					    rseq_len < offsetof(struct rseq, end))))  		return -EINVAL;  	if (!access_ok(rseq, rseq_len))  		return -EFAULT;  	current->rseq = rseq; +	current->rseq_len = rseq_len;  	current->rseq_sig = sig;  	/*  	 * If rseq was previously inactive, and has just been  |