| From foo@baz Wed Nov 21 19:20:53 CET 2018 |
| From: David Long <dave.long@linaro.org> |
| Date: Wed, 7 Nov 2018 11:43:58 -0500 |
| Subject: ARM: vfp: use __copy_from_user() when restoring VFP state |
| To: stable@vger.kernel.org, Russell King - ARM Linux <linux@armlinux.org.uk>, Florian Fainelli <f.fainelli@gmail.com>, Tony Lindgren <tony@atomide.com>, Marc Zyngier <marc.zyngier@arm.com>, Mark Rutland <mark.rutland@arm.com> |
| Cc: Greg KH <gregkh@linuxfoundation.org>, Mark Brown <broonie@kernel.org> |
| Message-ID: <20181107164402.9380-21-dave.long@linaro.org> |
| |
| From: Russell King <rmk+kernel@armlinux.org.uk> |
| |
| Commit 42019fc50dfadb219f9e6ddf4c354f3837057d80 upstream. |
| |
| __get_user_error() is used as a fast accessor to make copying structure |
| members in the signal handling path as efficient as possible. However, |
| with software PAN and the recent Spectre variant 1, the efficiency is |
| reduced as these are no longer fast accessors. |
| |
| In the case of software PAN, it has to switch the domain register around |
| each access, and with Spectre variant 1, it would have to repeat the |
| access_ok() check for each access. |
| |
| Use __copy_from_user() rather than __get_user_err() for individual |
| members when restoring VFP state. |
| |
| Acked-by: Mark Rutland <mark.rutland@arm.com> |
| Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> |
| Signed-off-by: David A. Long <dave.long@linaro.org> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| arch/arm/include/asm/thread_info.h | 4 ++-- |
| arch/arm/kernel/signal.c | 17 ++++++++--------- |
| arch/arm/vfp/vfpmodule.c | 17 +++++++---------- |
| 3 files changed, 17 insertions(+), 21 deletions(-) |
| |
| --- a/arch/arm/include/asm/thread_info.h |
| +++ b/arch/arm/include/asm/thread_info.h |
| @@ -126,8 +126,8 @@ struct user_vfp_exc; |
| |
| extern int vfp_preserve_user_clear_hwstate(struct user_vfp __user *, |
| struct user_vfp_exc __user *); |
| -extern int vfp_restore_user_hwstate(struct user_vfp __user *, |
| - struct user_vfp_exc __user *); |
| +extern int vfp_restore_user_hwstate(struct user_vfp *, |
| + struct user_vfp_exc *); |
| #endif |
| |
| /* |
| --- a/arch/arm/kernel/signal.c |
| +++ b/arch/arm/kernel/signal.c |
| @@ -107,21 +107,20 @@ static int preserve_vfp_context(struct v |
| return vfp_preserve_user_clear_hwstate(&frame->ufp, &frame->ufp_exc); |
| } |
| |
| -static int restore_vfp_context(struct vfp_sigframe __user *frame) |
| +static int restore_vfp_context(struct vfp_sigframe __user *auxp) |
| { |
| - unsigned long magic; |
| - unsigned long size; |
| - int err = 0; |
| + struct vfp_sigframe frame; |
| + int err; |
| |
| - __get_user_error(magic, &frame->magic, err); |
| - __get_user_error(size, &frame->size, err); |
| + err = __copy_from_user(&frame, (char __user *) auxp, sizeof(frame)); |
| |
| if (err) |
| - return -EFAULT; |
| - if (magic != VFP_MAGIC || size != VFP_STORAGE_SIZE) |
| + return err; |
| + |
| + if (frame.magic != VFP_MAGIC || frame.size != VFP_STORAGE_SIZE) |
| return -EINVAL; |
| |
| - return vfp_restore_user_hwstate(&frame->ufp, &frame->ufp_exc); |
| + return vfp_restore_user_hwstate(&frame.ufp, &frame.ufp_exc); |
| } |
| |
| #endif |
| --- a/arch/arm/vfp/vfpmodule.c |
| +++ b/arch/arm/vfp/vfpmodule.c |
| @@ -597,13 +597,11 @@ int vfp_preserve_user_clear_hwstate(stru |
| } |
| |
| /* Sanitise and restore the current VFP state from the provided structures. */ |
| -int vfp_restore_user_hwstate(struct user_vfp __user *ufp, |
| - struct user_vfp_exc __user *ufp_exc) |
| +int vfp_restore_user_hwstate(struct user_vfp *ufp, struct user_vfp_exc *ufp_exc) |
| { |
| struct thread_info *thread = current_thread_info(); |
| struct vfp_hard_struct *hwstate = &thread->vfpstate.hard; |
| unsigned long fpexc; |
| - int err = 0; |
| |
| /* Disable VFP to avoid corrupting the new thread state. */ |
| vfp_flush_hwstate(thread); |
| @@ -612,17 +610,16 @@ int vfp_restore_user_hwstate(struct user |
| * Copy the floating point registers. There can be unused |
| * registers see asm/hwcap.h for details. |
| */ |
| - err |= __copy_from_user(&hwstate->fpregs, &ufp->fpregs, |
| - sizeof(hwstate->fpregs)); |
| + memcpy(&hwstate->fpregs, &ufp->fpregs, sizeof(hwstate->fpregs)); |
| /* |
| * Copy the status and control register. |
| */ |
| - __get_user_error(hwstate->fpscr, &ufp->fpscr, err); |
| + hwstate->fpscr = ufp->fpscr; |
| |
| /* |
| * Sanitise and restore the exception registers. |
| */ |
| - __get_user_error(fpexc, &ufp_exc->fpexc, err); |
| + fpexc = ufp_exc->fpexc; |
| |
| /* Ensure the VFP is enabled. */ |
| fpexc |= FPEXC_EN; |
| @@ -631,10 +628,10 @@ int vfp_restore_user_hwstate(struct user |
| fpexc &= ~(FPEXC_EX | FPEXC_FP2V); |
| hwstate->fpexc = fpexc; |
| |
| - __get_user_error(hwstate->fpinst, &ufp_exc->fpinst, err); |
| - __get_user_error(hwstate->fpinst2, &ufp_exc->fpinst2, err); |
| + hwstate->fpinst = ufp_exc->fpinst; |
| + hwstate->fpinst2 = ufp_exc->fpinst2; |
| |
| - return err ? -EFAULT : 0; |
| + return 0; |
| } |
| |
| /* |