diff options
Diffstat (limited to 'arch/x86/kernel/fpu')
| -rw-r--r-- | arch/x86/kernel/fpu/bugs.c | 1 | ||||
| -rw-r--r-- | arch/x86/kernel/fpu/core.c | 155 | ||||
| -rw-r--r-- | arch/x86/kernel/fpu/init.c | 2 | ||||
| -rw-r--r-- | arch/x86/kernel/fpu/regset.c | 49 | ||||
| -rw-r--r-- | arch/x86/kernel/fpu/signal.c | 38 | ||||
| -rw-r--r-- | arch/x86/kernel/fpu/xstate.c | 264 | 
6 files changed, 307 insertions, 202 deletions
| diff --git a/arch/x86/kernel/fpu/bugs.c b/arch/x86/kernel/fpu/bugs.c index d913047f832c..2954fab15e51 100644 --- a/arch/x86/kernel/fpu/bugs.c +++ b/arch/x86/kernel/fpu/bugs.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0  /*   * x86 FPU bug checks:   */ diff --git a/arch/x86/kernel/fpu/core.c b/arch/x86/kernel/fpu/core.c index e1114f070c2d..f92a6593de1e 100644 --- a/arch/x86/kernel/fpu/core.c +++ b/arch/x86/kernel/fpu/core.c @@ -100,7 +100,7 @@ void __kernel_fpu_begin(void)  	kernel_fpu_disable(); -	if (fpu->fpregs_active) { +	if (fpu->initialized) {  		/*  		 * Ignore return value -- we don't care if reg state  		 * is clobbered. @@ -116,7 +116,7 @@ void __kernel_fpu_end(void)  {  	struct fpu *fpu = ¤t->thread.fpu; -	if (fpu->fpregs_active) +	if (fpu->initialized)  		copy_kernel_to_fpregs(&fpu->state);  	kernel_fpu_enable(); @@ -148,7 +148,7 @@ void fpu__save(struct fpu *fpu)  	preempt_disable();  	trace_x86_fpu_before_save(fpu); -	if (fpu->fpregs_active) { +	if (fpu->initialized) {  		if (!copy_fpregs_to_fpstate(fpu)) {  			copy_kernel_to_fpregs(&fpu->state);  		} @@ -189,10 +189,9 @@ EXPORT_SYMBOL_GPL(fpstate_init);  int fpu__copy(struct fpu *dst_fpu, struct fpu *src_fpu)  { -	dst_fpu->fpregs_active = 0;  	dst_fpu->last_cpu = -1; -	if (!src_fpu->fpstate_active || !static_cpu_has(X86_FEATURE_FPU)) +	if (!src_fpu->initialized || !static_cpu_has(X86_FEATURE_FPU))  		return 0;  	WARN_ON_FPU(src_fpu != ¤t->thread.fpu); @@ -206,26 +205,14 @@ int fpu__copy(struct fpu *dst_fpu, struct fpu *src_fpu)  	/*  	 * Save current FPU registers directly into the child  	 * FPU context, without any memory-to-memory copying. -	 * In lazy mode, if the FPU context isn't loaded into -	 * fpregs, CR0.TS will be set and do_device_not_available -	 * will load the FPU context.  	 * -	 * We have to do all this with preemption disabled, -	 * mostly because of the FNSAVE case, because in that -	 * case we must not allow preemption in the window -	 * between the FNSAVE and us marking the context lazy. -	 * -	 * It shouldn't be an issue as even FNSAVE is plenty -	 * fast in terms of critical section length. +	 * ( The function 'fails' in the FNSAVE case, which destroys +	 *   register contents so we have to copy them back. )  	 */ -	preempt_disable();  	if (!copy_fpregs_to_fpstate(dst_fpu)) { -		memcpy(&src_fpu->state, &dst_fpu->state, -		       fpu_kernel_xstate_size); - +		memcpy(&src_fpu->state, &dst_fpu->state, fpu_kernel_xstate_size);  		copy_kernel_to_fpregs(&src_fpu->state);  	} -	preempt_enable();  	trace_x86_fpu_copy_src(src_fpu);  	trace_x86_fpu_copy_dst(dst_fpu); @@ -237,45 +224,48 @@ int fpu__copy(struct fpu *dst_fpu, struct fpu *src_fpu)   * Activate the current task's in-memory FPU context,   * if it has not been used before:   */ -void fpu__activate_curr(struct fpu *fpu) +void fpu__initialize(struct fpu *fpu)  {  	WARN_ON_FPU(fpu != ¤t->thread.fpu); -	if (!fpu->fpstate_active) { +	if (!fpu->initialized) {  		fpstate_init(&fpu->state);  		trace_x86_fpu_init_state(fpu);  		trace_x86_fpu_activate_state(fpu);  		/* Safe to do for the current task: */ -		fpu->fpstate_active = 1; +		fpu->initialized = 1;  	}  } -EXPORT_SYMBOL_GPL(fpu__activate_curr); +EXPORT_SYMBOL_GPL(fpu__initialize);  /*   * This function must be called before we read a task's fpstate.   * - * If the task has not used the FPU before then initialize its - * fpstate. + * There's two cases where this gets called: + * + * - for the current task (when coredumping), in which case we have + *   to save the latest FPU registers into the fpstate, + * + * - or it's called for stopped tasks (ptrace), in which case the + *   registers were already saved by the context-switch code when + *   the task scheduled out - we only have to initialize the registers + *   if they've never been initialized.   *   * If the task has used the FPU before then save it.   */ -void fpu__activate_fpstate_read(struct fpu *fpu) +void fpu__prepare_read(struct fpu *fpu)  { -	/* -	 * If fpregs are active (in the current CPU), then -	 * copy them to the fpstate: -	 */ -	if (fpu->fpregs_active) { +	if (fpu == ¤t->thread.fpu) {  		fpu__save(fpu);  	} else { -		if (!fpu->fpstate_active) { +		if (!fpu->initialized) {  			fpstate_init(&fpu->state);  			trace_x86_fpu_init_state(fpu);  			trace_x86_fpu_activate_state(fpu);  			/* Safe to do for current and for stopped child tasks: */ -			fpu->fpstate_active = 1; +			fpu->initialized = 1;  		}  	}  } @@ -283,17 +273,17 @@ void fpu__activate_fpstate_read(struct fpu *fpu)  /*   * This function must be called before we write a task's fpstate.   * - * If the task has used the FPU before then unlazy it. + * If the task has used the FPU before then invalidate any cached FPU registers.   * If the task has not used the FPU before then initialize its fpstate.   *   * After this function call, after registers in the fpstate are   * modified and the child task has woken up, the child task will   * restore the modified FPU state from the modified context. If we - * didn't clear its lazy status here then the lazy in-registers + * didn't clear its cached status here then the cached in-registers   * state pending on its former CPU could be restored, corrupting   * the modifications.   */ -void fpu__activate_fpstate_write(struct fpu *fpu) +void fpu__prepare_write(struct fpu *fpu)  {  	/*  	 * Only stopped child tasks can be used to modify the FPU @@ -301,8 +291,8 @@ void fpu__activate_fpstate_write(struct fpu *fpu)  	 */  	WARN_ON_FPU(fpu == ¤t->thread.fpu); -	if (fpu->fpstate_active) { -		/* Invalidate any lazy state: */ +	if (fpu->initialized) { +		/* Invalidate any cached state: */  		__fpu_invalidate_fpregs_state(fpu);  	} else {  		fpstate_init(&fpu->state); @@ -310,74 +300,11 @@ void fpu__activate_fpstate_write(struct fpu *fpu)  		trace_x86_fpu_activate_state(fpu);  		/* Safe to do for stopped child tasks: */ -		fpu->fpstate_active = 1; +		fpu->initialized = 1;  	}  }  /* - * This function must be called before we write the current - * task's fpstate. - * - * This call gets the current FPU register state and moves - * it in to the 'fpstate'.  Preemption is disabled so that - * no writes to the 'fpstate' can occur from context - * swiches. - * - * Must be followed by a fpu__current_fpstate_write_end(). - */ -void fpu__current_fpstate_write_begin(void) -{ -	struct fpu *fpu = ¤t->thread.fpu; - -	/* -	 * Ensure that the context-switching code does not write -	 * over the fpstate while we are doing our update. -	 */ -	preempt_disable(); - -	/* -	 * Move the fpregs in to the fpu's 'fpstate'. -	 */ -	fpu__activate_fpstate_read(fpu); - -	/* -	 * The caller is about to write to 'fpu'.  Ensure that no -	 * CPU thinks that its fpregs match the fpstate.  This -	 * ensures we will not be lazy and skip a XRSTOR in the -	 * future. -	 */ -	__fpu_invalidate_fpregs_state(fpu); -} - -/* - * This function must be paired with fpu__current_fpstate_write_begin() - * - * This will ensure that the modified fpstate gets placed back in - * the fpregs if necessary. - * - * Note: This function may be called whether or not an _actual_ - * write to the fpstate occurred. - */ -void fpu__current_fpstate_write_end(void) -{ -	struct fpu *fpu = ¤t->thread.fpu; - -	/* -	 * 'fpu' now has an updated copy of the state, but the -	 * registers may still be out of date.  Update them with -	 * an XRSTOR if they are active. -	 */ -	if (fpregs_active()) -		copy_kernel_to_fpregs(&fpu->state); - -	/* -	 * Our update is done and the fpregs/fpstate are in sync -	 * if necessary.  Context switches can happen again. -	 */ -	preempt_enable(); -} - -/*   * 'fpu__restore()' is called to copy FPU registers from   * the FPU fpstate to the live hw registers and to activate   * access to the hardware registers, so that FPU instructions @@ -389,7 +316,7 @@ void fpu__current_fpstate_write_end(void)   */  void fpu__restore(struct fpu *fpu)  { -	fpu__activate_curr(fpu); +	fpu__initialize(fpu);  	/* Avoid __kernel_fpu_begin() right after fpregs_activate() */  	kernel_fpu_disable(); @@ -414,15 +341,17 @@ void fpu__drop(struct fpu *fpu)  {  	preempt_disable(); -	if (fpu->fpregs_active) { -		/* Ignore delayed exceptions from user space */ -		asm volatile("1: fwait\n" -			     "2:\n" -			     _ASM_EXTABLE(1b, 2b)); -		fpregs_deactivate(fpu); +	if (fpu == ¤t->thread.fpu) { +		if (fpu->initialized) { +			/* Ignore delayed exceptions from user space */ +			asm volatile("1: fwait\n" +				     "2:\n" +				     _ASM_EXTABLE(1b, 2b)); +			fpregs_deactivate(fpu); +		}  	} -	fpu->fpstate_active = 0; +	fpu->initialized = 0;  	trace_x86_fpu_dropped(fpu); @@ -462,9 +391,11 @@ void fpu__clear(struct fpu *fpu)  	 * Make sure fpstate is cleared and initialized.  	 */  	if (static_cpu_has(X86_FEATURE_FPU)) { -		fpu__activate_curr(fpu); +		preempt_disable(); +		fpu__initialize(fpu);  		user_fpu_begin();  		copy_init_fpstate_to_fpregs(); +		preempt_enable();  	}  } diff --git a/arch/x86/kernel/fpu/init.c b/arch/x86/kernel/fpu/init.c index d5d44c452624..7affb7e3d9a5 100644 --- a/arch/x86/kernel/fpu/init.c +++ b/arch/x86/kernel/fpu/init.c @@ -240,7 +240,7 @@ static void __init fpu__init_system_ctx_switch(void)  	WARN_ON_FPU(!on_boot_cpu);  	on_boot_cpu = 0; -	WARN_ON_FPU(current->thread.fpu.fpstate_active); +	WARN_ON_FPU(current->thread.fpu.initialized);  }  /* diff --git a/arch/x86/kernel/fpu/regset.c b/arch/x86/kernel/fpu/regset.c index b188b16841e3..bc02f5144b95 100644 --- a/arch/x86/kernel/fpu/regset.c +++ b/arch/x86/kernel/fpu/regset.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0  /*   * FPU register's regset abstraction, for ptrace, core dumps, etc.   */ @@ -16,14 +17,14 @@ int regset_fpregs_active(struct task_struct *target, const struct user_regset *r  {  	struct fpu *target_fpu = &target->thread.fpu; -	return target_fpu->fpstate_active ? regset->n : 0; +	return target_fpu->initialized ? regset->n : 0;  }  int regset_xregset_fpregs_active(struct task_struct *target, const struct user_regset *regset)  {  	struct fpu *target_fpu = &target->thread.fpu; -	if (boot_cpu_has(X86_FEATURE_FXSR) && target_fpu->fpstate_active) +	if (boot_cpu_has(X86_FEATURE_FXSR) && target_fpu->initialized)  		return regset->n;  	else  		return 0; @@ -38,7 +39,7 @@ int xfpregs_get(struct task_struct *target, const struct user_regset *regset,  	if (!boot_cpu_has(X86_FEATURE_FXSR))  		return -ENODEV; -	fpu__activate_fpstate_read(fpu); +	fpu__prepare_read(fpu);  	fpstate_sanitize_xstate(fpu);  	return user_regset_copyout(&pos, &count, &kbuf, &ubuf, @@ -55,7 +56,7 @@ int xfpregs_set(struct task_struct *target, const struct user_regset *regset,  	if (!boot_cpu_has(X86_FEATURE_FXSR))  		return -ENODEV; -	fpu__activate_fpstate_write(fpu); +	fpu__prepare_write(fpu);  	fpstate_sanitize_xstate(fpu);  	ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, @@ -89,10 +90,13 @@ int xstateregs_get(struct task_struct *target, const struct user_regset *regset,  	xsave = &fpu->state.xsave; -	fpu__activate_fpstate_read(fpu); +	fpu__prepare_read(fpu);  	if (using_compacted_format()) { -		ret = copyout_from_xsaves(pos, count, kbuf, ubuf, xsave); +		if (kbuf) +			ret = copy_xstate_to_kernel(kbuf, xsave, pos, count); +		else +			ret = copy_xstate_to_user(ubuf, xsave, pos, count);  	} else {  		fpstate_sanitize_xstate(fpu);  		/* @@ -129,28 +133,29 @@ int xstateregs_set(struct task_struct *target, const struct user_regset *regset,  	xsave = &fpu->state.xsave; -	fpu__activate_fpstate_write(fpu); +	fpu__prepare_write(fpu); -	if (boot_cpu_has(X86_FEATURE_XSAVES)) -		ret = copyin_to_xsaves(kbuf, ubuf, xsave); -	else +	if (using_compacted_format()) { +		if (kbuf) +			ret = copy_kernel_to_xstate(xsave, kbuf); +		else +			ret = copy_user_to_xstate(xsave, ubuf); +	} else {  		ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, xsave, 0, -1); - -	/* -	 * In case of failure, mark all states as init: -	 */ -	if (ret) -		fpstate_init(&fpu->state); +		if (!ret) +			ret = validate_xstate_header(&xsave->header); +	}  	/*  	 * mxcsr reserved bits must be masked to zero for security reasons.  	 */  	xsave->i387.mxcsr &= mxcsr_feature_mask; -	xsave->header.xfeatures &= xfeatures_mask; +  	/* -	 * These bits must be zero. +	 * In case of failure, mark all states as init:  	 */ -	memset(&xsave->header.reserved, 0, 48); +	if (ret) +		fpstate_init(&fpu->state);  	return ret;  } @@ -299,7 +304,7 @@ int fpregs_get(struct task_struct *target, const struct user_regset *regset,  	struct fpu *fpu = &target->thread.fpu;  	struct user_i387_ia32_struct env; -	fpu__activate_fpstate_read(fpu); +	fpu__prepare_read(fpu);  	if (!boot_cpu_has(X86_FEATURE_FPU))  		return fpregs_soft_get(target, regset, pos, count, kbuf, ubuf); @@ -329,7 +334,7 @@ int fpregs_set(struct task_struct *target, const struct user_regset *regset,  	struct user_i387_ia32_struct env;  	int ret; -	fpu__activate_fpstate_write(fpu); +	fpu__prepare_write(fpu);  	fpstate_sanitize_xstate(fpu);  	if (!boot_cpu_has(X86_FEATURE_FPU)) @@ -369,7 +374,7 @@ int dump_fpu(struct pt_regs *regs, struct user_i387_struct *ufpu)  	struct fpu *fpu = &tsk->thread.fpu;  	int fpvalid; -	fpvalid = fpu->fpstate_active; +	fpvalid = fpu->initialized;  	if (fpvalid)  		fpvalid = !fpregs_get(tsk, NULL,  				      0, sizeof(struct user_i387_ia32_struct), diff --git a/arch/x86/kernel/fpu/signal.c b/arch/x86/kernel/fpu/signal.c index 83c23c230b4c..23f1691670b6 100644 --- a/arch/x86/kernel/fpu/signal.c +++ b/arch/x86/kernel/fpu/signal.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0  /*   * FPU signal frame handling routines.   */ @@ -155,7 +156,8 @@ static inline int copy_fpregs_to_sigframe(struct xregs_state __user *buf)   */  int copy_fpstate_to_sigframe(void __user *buf, void __user *buf_fx, int size)  { -	struct xregs_state *xsave = ¤t->thread.fpu.state.xsave; +	struct fpu *fpu = ¤t->thread.fpu; +	struct xregs_state *xsave = &fpu->state.xsave;  	struct task_struct *tsk = current;  	int ia32_fxstate = (buf != buf_fx); @@ -170,13 +172,13 @@ int copy_fpstate_to_sigframe(void __user *buf, void __user *buf_fx, int size)  			sizeof(struct user_i387_ia32_struct), NULL,  			(struct _fpstate_32 __user *) buf) ? -1 : 1; -	if (fpregs_active() || using_compacted_format()) { +	if (fpu->initialized || using_compacted_format()) {  		/* Save the live register state to the user directly. */  		if (copy_fpregs_to_sigframe(buf_fx))  			return -1;  		/* Update the thread's fxstate to save the fsave header. */  		if (ia32_fxstate) -			copy_fxregs_to_kernel(&tsk->thread.fpu); +			copy_fxregs_to_kernel(fpu);  	} else {  		/*  		 * It is a *bug* if kernel uses compacted-format for xsave @@ -189,7 +191,7 @@ int copy_fpstate_to_sigframe(void __user *buf, void __user *buf_fx, int size)  			return -1;  		} -		fpstate_sanitize_xstate(&tsk->thread.fpu); +		fpstate_sanitize_xstate(fpu);  		if (__copy_to_user(buf_fx, xsave, fpu_user_xstate_size))  			return -1;  	} @@ -213,8 +215,11 @@ sanitize_restored_xstate(struct task_struct *tsk,  	struct xstate_header *header = &xsave->header;  	if (use_xsave()) { -		/* These bits must be zero. */ -		memset(header->reserved, 0, 48); +		/* +		 * Note: we don't need to zero the reserved bits in the +		 * xstate_header here because we either didn't copy them at all, +		 * or we checked earlier that they aren't set. +		 */  		/*  		 * Init the state that is not present in the memory @@ -223,7 +228,7 @@ sanitize_restored_xstate(struct task_struct *tsk,  		if (fx_only)  			header->xfeatures = XFEATURE_MASK_FPSSE;  		else -			header->xfeatures &= (xfeatures_mask & xfeatures); +			header->xfeatures &= xfeatures;  	}  	if (use_fxsr()) { @@ -279,7 +284,7 @@ static int __fpu__restore_sig(void __user *buf, void __user *buf_fx, int size)  	if (!access_ok(VERIFY_READ, buf, size))  		return -EACCES; -	fpu__activate_curr(fpu); +	fpu__initialize(fpu);  	if (!static_cpu_has(X86_FEATURE_FPU))  		return fpregs_soft_set(current, NULL, @@ -307,28 +312,29 @@ static int __fpu__restore_sig(void __user *buf, void __user *buf_fx, int size)  		/*  		 * For 32-bit frames with fxstate, copy the user state to the  		 * thread's fpu state, reconstruct fxstate from the fsave -		 * header. Sanitize the copied state etc. +		 * header. Validate and sanitize the copied state.  		 */  		struct fpu *fpu = &tsk->thread.fpu;  		struct user_i387_ia32_struct env;  		int err = 0;  		/* -		 * Drop the current fpu which clears fpu->fpstate_active. This ensures +		 * Drop the current fpu which clears fpu->initialized. This ensures  		 * that any context-switch during the copy of the new state,  		 * avoids the intermediate state from getting restored/saved.  		 * Thus avoiding the new restored state from getting corrupted.  		 * We will be ready to restore/save the state only after -		 * fpu->fpstate_active is again set. +		 * fpu->initialized is again set.  		 */  		fpu__drop(fpu);  		if (using_compacted_format()) { -			err = copyin_to_xsaves(NULL, buf_fx, -					       &fpu->state.xsave); +			err = copy_user_to_xstate(&fpu->state.xsave, buf_fx);  		} else { -			err = __copy_from_user(&fpu->state.xsave, -					       buf_fx, state_size); +			err = __copy_from_user(&fpu->state.xsave, buf_fx, state_size); + +			if (!err && state_size > offsetof(struct xregs_state, header)) +				err = validate_xstate_header(&fpu->state.xsave.header);  		}  		if (err || __copy_from_user(&env, buf, sizeof(env))) { @@ -339,7 +345,7 @@ static int __fpu__restore_sig(void __user *buf, void __user *buf_fx, int size)  			sanitize_restored_xstate(tsk, &env, xfeatures, fx_only);  		} -		fpu->fpstate_active = 1; +		fpu->initialized = 1;  		preempt_disable();  		fpu__restore(fpu);  		preempt_enable(); diff --git a/arch/x86/kernel/fpu/xstate.c b/arch/x86/kernel/fpu/xstate.c index c24ac1efb12d..f1d5476c9022 100644 --- a/arch/x86/kernel/fpu/xstate.c +++ b/arch/x86/kernel/fpu/xstate.c @@ -483,6 +483,30 @@ int using_compacted_format(void)  	return boot_cpu_has(X86_FEATURE_XSAVES);  } +/* Validate an xstate header supplied by userspace (ptrace or sigreturn) */ +int validate_xstate_header(const struct xstate_header *hdr) +{ +	/* No unknown or supervisor features may be set */ +	if (hdr->xfeatures & (~xfeatures_mask | XFEATURE_MASK_SUPERVISOR)) +		return -EINVAL; + +	/* Userspace must use the uncompacted format */ +	if (hdr->xcomp_bv) +		return -EINVAL; + +	/* +	 * If 'reserved' is shrunken to add a new field, make sure to validate +	 * that new field here! +	 */ +	BUILD_BUG_ON(sizeof(hdr->reserved) != 48); + +	/* No reserved bits may be set */ +	if (memchr_inv(hdr->reserved, 0, sizeof(hdr->reserved))) +		return -EINVAL; + +	return 0; +} +  static void __xstate_dump_leaves(void)  {  	int i; @@ -867,7 +891,7 @@ const void *get_xsave_field_ptr(int xsave_state)  {  	struct fpu *fpu = ¤t->thread.fpu; -	if (!fpu->fpstate_active) +	if (!fpu->initialized)  		return NULL;  	/*  	 * fpu__save() takes the CPU's xstate registers @@ -921,38 +945,129 @@ int arch_set_user_pkey_access(struct task_struct *tsk, int pkey,  #endif /* ! CONFIG_ARCH_HAS_PKEYS */  /* + * Weird legacy quirk: SSE and YMM states store information in the + * MXCSR and MXCSR_FLAGS fields of the FP area. That means if the FP + * area is marked as unused in the xfeatures header, we need to copy + * MXCSR and MXCSR_FLAGS if either SSE or YMM are in use. + */ +static inline bool xfeatures_mxcsr_quirk(u64 xfeatures) +{ +	if (!(xfeatures & (XFEATURE_MASK_SSE|XFEATURE_MASK_YMM))) +		return false; + +	if (xfeatures & XFEATURE_MASK_FP) +		return false; + +	return true; +} + +/*   * This is similar to user_regset_copyout(), but will not add offset to   * the source data pointer or increment pos, count, kbuf, and ubuf.   */ -static inline int xstate_copyout(unsigned int pos, unsigned int count, -				 void *kbuf, void __user *ubuf, -				 const void *data, const int start_pos, -				 const int end_pos) +static inline void +__copy_xstate_to_kernel(void *kbuf, const void *data, +			unsigned int offset, unsigned int size, unsigned int size_total)  { -	if ((count == 0) || (pos < start_pos)) -		return 0; +	if (offset < size_total) { +		unsigned int copy = min(size, size_total - offset); -	if (end_pos < 0 || pos < end_pos) { -		unsigned int copy = (end_pos < 0 ? count : min(count, end_pos - pos)); +		memcpy(kbuf + offset, data, copy); +	} +} -		if (kbuf) { -			memcpy(kbuf + pos, data, copy); -		} else { -			if (__copy_to_user(ubuf + pos, data, copy)) -				return -EFAULT; +/* + * Convert from kernel XSAVES compacted format to standard format and copy + * to a kernel-space ptrace buffer. + * + * It supports partial copy but pos always starts from zero. This is called + * from xstateregs_get() and there we check the CPU has XSAVES. + */ +int copy_xstate_to_kernel(void *kbuf, struct xregs_state *xsave, unsigned int offset_start, unsigned int size_total) +{ +	unsigned int offset, size; +	struct xstate_header header; +	int i; + +	/* +	 * Currently copy_regset_to_user() starts from pos 0: +	 */ +	if (unlikely(offset_start != 0)) +		return -EFAULT; + +	/* +	 * The destination is a ptrace buffer; we put in only user xstates: +	 */ +	memset(&header, 0, sizeof(header)); +	header.xfeatures = xsave->header.xfeatures; +	header.xfeatures &= ~XFEATURE_MASK_SUPERVISOR; + +	/* +	 * Copy xregs_state->header: +	 */ +	offset = offsetof(struct xregs_state, header); +	size = sizeof(header); + +	__copy_xstate_to_kernel(kbuf, &header, offset, size, size_total); + +	for (i = 0; i < XFEATURE_MAX; i++) { +		/* +		 * Copy only in-use xstates: +		 */ +		if ((header.xfeatures >> i) & 1) { +			void *src = __raw_xsave_addr(xsave, 1 << i); + +			offset = xstate_offsets[i]; +			size = xstate_sizes[i]; + +			/* The next component has to fit fully into the output buffer: */ +			if (offset + size > size_total) +				break; + +			__copy_xstate_to_kernel(kbuf, src, offset, size, size_total);  		} + +	} + +	if (xfeatures_mxcsr_quirk(header.xfeatures)) { +		offset = offsetof(struct fxregs_state, mxcsr); +		size = MXCSR_AND_FLAGS_SIZE; +		__copy_xstate_to_kernel(kbuf, &xsave->i387.mxcsr, offset, size, size_total); +	} + +	/* +	 * Fill xsave->i387.sw_reserved value for ptrace frame: +	 */ +	offset = offsetof(struct fxregs_state, sw_reserved); +	size = sizeof(xstate_fx_sw_bytes); + +	__copy_xstate_to_kernel(kbuf, xstate_fx_sw_bytes, offset, size, size_total); + +	return 0; +} + +static inline int +__copy_xstate_to_user(void __user *ubuf, const void *data, unsigned int offset, unsigned int size, unsigned int size_total) +{ +	if (!size) +		return 0; + +	if (offset < size_total) { +		unsigned int copy = min(size, size_total - offset); + +		if (__copy_to_user(ubuf + offset, data, copy)) +			return -EFAULT;  	}  	return 0;  }  /*   * Convert from kernel XSAVES compacted format to standard format and copy - * to a ptrace buffer. It supports partial copy but pos always starts from + * to a user-space buffer. It supports partial copy but pos always starts from   * zero. This is called from xstateregs_get() and there we check the CPU   * has XSAVES.   */ -int copyout_from_xsaves(unsigned int pos, unsigned int count, void *kbuf, -			void __user *ubuf, struct xregs_state *xsave) +int copy_xstate_to_user(void __user *ubuf, struct xregs_state *xsave, unsigned int offset_start, unsigned int size_total)  {  	unsigned int offset, size;  	int ret, i; @@ -961,7 +1076,7 @@ int copyout_from_xsaves(unsigned int pos, unsigned int count, void *kbuf,  	/*  	 * Currently copy_regset_to_user() starts from pos 0:  	 */ -	if (unlikely(pos != 0)) +	if (unlikely(offset_start != 0))  		return -EFAULT;  	/* @@ -977,8 +1092,7 @@ int copyout_from_xsaves(unsigned int pos, unsigned int count, void *kbuf,  	offset = offsetof(struct xregs_state, header);  	size = sizeof(header); -	ret = xstate_copyout(offset, size, kbuf, ubuf, &header, 0, count); - +	ret = __copy_xstate_to_user(ubuf, &header, offset, size, size_total);  	if (ret)  		return ret; @@ -992,25 +1106,30 @@ int copyout_from_xsaves(unsigned int pos, unsigned int count, void *kbuf,  			offset = xstate_offsets[i];  			size = xstate_sizes[i]; -			ret = xstate_copyout(offset, size, kbuf, ubuf, src, 0, count); +			/* The next component has to fit fully into the output buffer: */ +			if (offset + size > size_total) +				break; +			ret = __copy_xstate_to_user(ubuf, src, offset, size, size_total);  			if (ret)  				return ret; - -			if (offset + size >= count) -				break;  		}  	} +	if (xfeatures_mxcsr_quirk(header.xfeatures)) { +		offset = offsetof(struct fxregs_state, mxcsr); +		size = MXCSR_AND_FLAGS_SIZE; +		__copy_xstate_to_user(ubuf, &xsave->i387.mxcsr, offset, size, size_total); +	} +  	/*  	 * Fill xsave->i387.sw_reserved value for ptrace frame:  	 */  	offset = offsetof(struct fxregs_state, sw_reserved);  	size = sizeof(xstate_fx_sw_bytes); -	ret = xstate_copyout(offset, size, kbuf, ubuf, xstate_fx_sw_bytes, 0, count); - +	ret = __copy_xstate_to_user(ubuf, xstate_fx_sw_bytes, offset, size, size_total);  	if (ret)  		return ret; @@ -1018,55 +1137,98 @@ int copyout_from_xsaves(unsigned int pos, unsigned int count, void *kbuf,  }  /* - * Convert from a ptrace standard-format buffer to kernel XSAVES format - * and copy to the target thread. This is called from xstateregs_set() and - * there we check the CPU has XSAVES and a whole standard-sized buffer - * exists. + * Convert from a ptrace standard-format kernel buffer to kernel XSAVES format + * and copy to the target thread. This is called from xstateregs_set().   */ -int copyin_to_xsaves(const void *kbuf, const void __user *ubuf, -		     struct xregs_state *xsave) +int copy_kernel_to_xstate(struct xregs_state *xsave, const void *kbuf)  {  	unsigned int offset, size;  	int i; -	u64 xfeatures; -	u64 allowed_features; +	struct xstate_header hdr;  	offset = offsetof(struct xregs_state, header); -	size = sizeof(xfeatures); +	size = sizeof(hdr); -	if (kbuf) { -		memcpy(&xfeatures, kbuf + offset, size); -	} else { -		if (__copy_from_user(&xfeatures, ubuf + offset, size)) -			return -EFAULT; +	memcpy(&hdr, kbuf + offset, size); + +	if (validate_xstate_header(&hdr)) +		return -EINVAL; + +	for (i = 0; i < XFEATURE_MAX; i++) { +		u64 mask = ((u64)1 << i); + +		if (hdr.xfeatures & mask) { +			void *dst = __raw_xsave_addr(xsave, 1 << i); + +			offset = xstate_offsets[i]; +			size = xstate_sizes[i]; + +			memcpy(dst, kbuf + offset, size); +		} +	} + +	if (xfeatures_mxcsr_quirk(hdr.xfeatures)) { +		offset = offsetof(struct fxregs_state, mxcsr); +		size = MXCSR_AND_FLAGS_SIZE; +		memcpy(&xsave->i387.mxcsr, kbuf + offset, size);  	}  	/* -	 * Reject if the user sets any disabled or supervisor features: +	 * The state that came in from userspace was user-state only. +	 * Mask all the user states out of 'xfeatures': +	 */ +	xsave->header.xfeatures &= XFEATURE_MASK_SUPERVISOR; + +	/* +	 * Add back in the features that came in from userspace:  	 */ -	allowed_features = xfeatures_mask & ~XFEATURE_MASK_SUPERVISOR; +	xsave->header.xfeatures |= hdr.xfeatures; -	if (xfeatures & ~allowed_features) +	return 0; +} + +/* + * Convert from a ptrace or sigreturn standard-format user-space buffer to + * kernel XSAVES format and copy to the target thread. This is called from + * xstateregs_set(), as well as potentially from the sigreturn() and + * rt_sigreturn() system calls. + */ +int copy_user_to_xstate(struct xregs_state *xsave, const void __user *ubuf) +{ +	unsigned int offset, size; +	int i; +	struct xstate_header hdr; + +	offset = offsetof(struct xregs_state, header); +	size = sizeof(hdr); + +	if (__copy_from_user(&hdr, ubuf + offset, size)) +		return -EFAULT; + +	if (validate_xstate_header(&hdr))  		return -EINVAL;  	for (i = 0; i < XFEATURE_MAX; i++) {  		u64 mask = ((u64)1 << i); -		if (xfeatures & mask) { +		if (hdr.xfeatures & mask) {  			void *dst = __raw_xsave_addr(xsave, 1 << i);  			offset = xstate_offsets[i];  			size = xstate_sizes[i]; -			if (kbuf) { -				memcpy(dst, kbuf + offset, size); -			} else { -				if (__copy_from_user(dst, ubuf + offset, size)) -					return -EFAULT; -			} +			if (__copy_from_user(dst, ubuf + offset, size)) +				return -EFAULT;  		}  	} +	if (xfeatures_mxcsr_quirk(hdr.xfeatures)) { +		offset = offsetof(struct fxregs_state, mxcsr); +		size = MXCSR_AND_FLAGS_SIZE; +		if (__copy_from_user(&xsave->i387.mxcsr, ubuf + offset, size)) +			return -EFAULT; +	} +  	/*  	 * The state that came in from userspace was user-state only.  	 * Mask all the user states out of 'xfeatures': @@ -1076,7 +1238,7 @@ int copyin_to_xsaves(const void *kbuf, const void __user *ubuf,  	/*  	 * Add back in the features that came in from userspace:  	 */ -	xsave->header.xfeatures |= xfeatures; +	xsave->header.xfeatures |= hdr.xfeatures;  	return 0;  } |