OSDN Git Service

x86/fpu: Prepare fpu_clone() for dynamically enabled features
authorThomas Gleixner <tglx@linutronix.de>
Thu, 21 Oct 2021 22:55:14 +0000 (15:55 -0700)
committerBorislav Petkov <bp@suse.de>
Tue, 26 Oct 2021 08:18:09 +0000 (10:18 +0200)
The default portion of the parent's FPU state is saved in a child task.
With dynamic features enabled, the non-default portion is not saved in a
child's fpstate because these register states are defined to be
caller-saved. The new task's fpstate is therefore the default buffer.

Fork inherits the permission of the parent.

Also, do not use memcpy() when TIF_NEED_FPU_LOAD is set because it is
invalid when the parent has dynamic features.

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Chang S. Bae <chang.seok.bae@intel.com>
Signed-off-by: Borislav Petkov <bp@suse.de>
Link: https://lkml.kernel.org/r/20211021225527.10184-11-chang.seok.bae@intel.com
arch/x86/include/asm/fpu/sched.h
arch/x86/kernel/fpu/core.c
arch/x86/kernel/process.c

index cdb78d5..99a8820 100644 (file)
@@ -11,7 +11,7 @@
 
 extern void save_fpregs_to_fpstate(struct fpu *fpu);
 extern void fpu__drop(struct fpu *fpu);
-extern int  fpu_clone(struct task_struct *dst);
+extern int  fpu_clone(struct task_struct *dst, unsigned long clone_flags);
 extern void fpu_flush_thread(void);
 
 /*
index 4018083..1ff6b83 100644 (file)
@@ -423,8 +423,20 @@ void fpstate_reset(struct fpu *fpu)
        fpu->perm.__user_state_size     = fpu_user_cfg.default_size;
 }
 
+static inline void fpu_inherit_perms(struct fpu *dst_fpu)
+{
+       if (fpu_state_size_dynamic()) {
+               struct fpu *src_fpu = &current->group_leader->thread.fpu;
+
+               spin_lock_irq(&current->sighand->siglock);
+               /* Fork also inherits the permissions of the parent */
+               dst_fpu->perm = src_fpu->perm;
+               spin_unlock_irq(&current->sighand->siglock);
+       }
+}
+
 /* Clone current's FPU state on fork */
-int fpu_clone(struct task_struct *dst)
+int fpu_clone(struct task_struct *dst, unsigned long clone_flags)
 {
        struct fpu *src_fpu = &current->thread.fpu;
        struct fpu *dst_fpu = &dst->thread.fpu;
@@ -455,17 +467,20 @@ int fpu_clone(struct task_struct *dst)
        }
 
        /*
-        * If the FPU registers are not owned by current just memcpy() the
-        * state.  Otherwise save the FPU registers directly into the
-        * child's FPU context, without any memory-to-memory copying.
+        * Save the default portion of the current FPU state into the
+        * clone. Assume all dynamic features to be defined as caller-
+        * saved, which enables skipping both the expansion of fpstate
+        * and the copying of any dynamic state.
+        *
+        * Do not use memcpy() when TIF_NEED_FPU_LOAD is set because
+        * copying is not valid when current uses non-default states.
         */
        fpregs_lock();
-       if (test_thread_flag(TIF_NEED_FPU_LOAD)) {
-               memcpy(&dst_fpu->fpstate->regs, &src_fpu->fpstate->regs,
-                      dst_fpu->fpstate->size);
-       } else {
-               save_fpregs_to_fpstate(dst_fpu);
-       }
+       if (test_thread_flag(TIF_NEED_FPU_LOAD))
+               fpregs_restore_userregs();
+       save_fpregs_to_fpstate(dst_fpu);
+       if (!(clone_flags & CLONE_THREAD))
+               fpu_inherit_perms(dst_fpu);
        fpregs_unlock();
 
        trace_x86_fpu_copy_src(src_fpu);
index 97fea16..99025e3 100644 (file)
@@ -157,7 +157,7 @@ int copy_thread(unsigned long clone_flags, unsigned long sp, unsigned long arg,
        frame->flags = X86_EFLAGS_FIXED;
 #endif
 
-       fpu_clone(p);
+       fpu_clone(p, clone_flags);
 
        /* Kernel thread ? */
        if (unlikely(p->flags & PF_KTHREAD)) {