x86/fpu/signal: Use fpstate for size and features
authorThomas Gleixner <tglx@linutronix.de>
Thu, 14 Oct 2021 22:51:51 +0000 (00:51 +0200)
committerBorislav Petkov <bp@suse.de>
Thu, 21 Oct 2021 12:24:47 +0000 (14:24 +0200)
For dynamically enabled features it's required to get the features which
are enabled for that context when restoring from sigframe.

The same applies for all signal frame size calculations.

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Borislav Petkov <bp@suse.de>
Link: https://lkml.kernel.org/r/87ilxz5iew.ffs@tglx
arch/x86/kernel/fpu/signal.c

index 935818b..f9af174 100644 (file)
@@ -41,7 +41,7 @@ static inline bool check_xstate_in_sigframe(struct fxregs_state __user *fxbuf,
        /* Check for the first magic field and other error scenarios. */
        if (fx_sw->magic1 != FP_XSTATE_MAGIC1 ||
            fx_sw->xstate_size < min_xstate_size ||
-           fx_sw->xstate_size > fpu_user_xstate_size ||
+           fx_sw->xstate_size > current->thread.fpu.fpstate->user_size ||
            fx_sw->xstate_size > fx_sw->extended_size)
                goto setfx;
 
@@ -98,7 +98,8 @@ static inline bool save_fsave_header(struct task_struct *tsk, void __user *buf)
        return true;
 }
 
-static inline bool save_xstate_epilog(void __user *buf, int ia32_frame)
+static inline bool save_xstate_epilog(void __user *buf, int ia32_frame,
+                                     unsigned int usize)
 {
        struct xregs_state __user *x = buf;
        struct _fpx_sw_bytes *sw_bytes;
@@ -113,7 +114,7 @@ static inline bool save_xstate_epilog(void __user *buf, int ia32_frame)
                return !err;
 
        err |= __put_user(FP_XSTATE_MAGIC2,
-                         (__u32 __user *)(buf + fpu_user_xstate_size));
+                         (__u32 __user *)(buf + usize));
 
        /*
         * Read the xfeatures which we copied (directly from the cpu or
@@ -171,6 +172,7 @@ static inline int copy_fpregs_to_sigframe(struct xregs_state __user *buf)
 bool copy_fpstate_to_sigframe(void __user *buf, void __user *buf_fx, int size)
 {
        struct task_struct *tsk = current;
+       struct fpstate *fpstate = tsk->thread.fpu.fpstate;
        int ia32_fxstate = (buf != buf_fx);
        int ret;
 
@@ -215,7 +217,7 @@ retry:
        fpregs_unlock();
 
        if (ret) {
-               if (!__clear_user(buf_fx, fpu_user_xstate_size))
+               if (!__clear_user(buf_fx, fpstate->user_size))
                        goto retry;
                return false;
        }
@@ -224,17 +226,18 @@ retry:
        if ((ia32_fxstate || !use_fxsr()) && !save_fsave_header(tsk, buf))
                return false;
 
-       if (use_fxsr() && !save_xstate_epilog(buf_fx, ia32_fxstate))
+       if (use_fxsr() &&
+           !save_xstate_epilog(buf_fx, ia32_fxstate, fpstate->user_size))
                return false;
 
        return true;
 }
 
-static int __restore_fpregs_from_user(void __user *buf, u64 xrestore,
-                                     bool fx_only)
+static int __restore_fpregs_from_user(void __user *buf, u64 ufeatures,
+                                     u64 xrestore, bool fx_only)
 {
        if (use_xsave()) {
-               u64 init_bv = xfeatures_mask_uabi() & ~xrestore;
+               u64 init_bv = ufeatures & ~xrestore;
                int ret;
 
                if (likely(!fx_only))
@@ -265,7 +268,8 @@ static bool restore_fpregs_from_user(void __user *buf, u64 xrestore,
 retry:
        fpregs_lock();
        pagefault_disable();
-       ret = __restore_fpregs_from_user(buf, xrestore, fx_only);
+       ret = __restore_fpregs_from_user(buf, fpu->fpstate->user_xfeatures,
+                                        xrestore, fx_only);
        pagefault_enable();
 
        if (unlikely(ret)) {
@@ -332,7 +336,7 @@ static bool __fpu_restore_sig(void __user *buf, void __user *buf_fx,
                user_xfeatures = fx_sw_user.xfeatures;
        } else {
                user_xfeatures = XFEATURE_MASK_FPSSE;
-               state_size = fpu->fpstate->size;
+               state_size = fpu->fpstate->user_size;
        }
 
        if (likely(!ia32_fxstate)) {
@@ -425,10 +429,11 @@ static bool __fpu_restore_sig(void __user *buf, void __user *buf_fx,
        return success;
 }
 
-static inline int xstate_sigframe_size(void)
+static inline unsigned int xstate_sigframe_size(struct fpstate *fpstate)
 {
-       return use_xsave() ? fpu_user_xstate_size + FP_XSTATE_MAGIC2_SIZE :
-                       fpu_user_xstate_size;
+       unsigned int size = fpstate->user_size;
+
+       return use_xsave() ? size + FP_XSTATE_MAGIC2_SIZE : size;
 }
 
 /*
@@ -436,17 +441,19 @@ static inline int xstate_sigframe_size(void)
  */
 bool fpu__restore_sig(void __user *buf, int ia32_frame)
 {
-       unsigned int size = xstate_sigframe_size();
        struct fpu *fpu = &current->thread.fpu;
        void __user *buf_fx = buf;
        bool ia32_fxstate = false;
        bool success = false;
+       unsigned int size;
 
        if (unlikely(!buf)) {
                fpu__clear_user_states(fpu);
                return true;
        }
 
+       size = xstate_sigframe_size(fpu->fpstate);
+
        ia32_frame &= (IS_ENABLED(CONFIG_X86_32) ||
                       IS_ENABLED(CONFIG_IA32_EMULATION));
 
@@ -481,7 +488,7 @@ unsigned long
 fpu__alloc_mathframe(unsigned long sp, int ia32_frame,
                     unsigned long *buf_fx, unsigned long *size)
 {
-       unsigned long frame_size = xstate_sigframe_size();
+       unsigned long frame_size = xstate_sigframe_size(current->thread.fpu.fpstate);
 
        *buf_fx = sp = round_down(sp - frame_size, 64);
        if (ia32_frame && use_fxsr()) {
@@ -494,9 +501,12 @@ fpu__alloc_mathframe(unsigned long sp, int ia32_frame,
        return sp;
 }
 
-unsigned long fpu__get_fpstate_size(void)
+unsigned long __init fpu__get_fpstate_size(void)
 {
-       unsigned long ret = xstate_sigframe_size();
+       unsigned long ret = fpu_user_xstate_size;
+
+       if (use_xsave())
+               ret += FP_XSTATE_MAGIC2_SIZE;
 
        /*
         * This space is needed on (most) 32-bit kernels, or when a 32-bit