Commit 9e798e9a authored by Thomas Gleixner's avatar Thomas Gleixner Committed by Borislav Petkov

x86/fpu: Prepare fpu_clone() for dynamically enabled features

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: default avatarThomas Gleixner <tglx@linutronix.de>
Signed-off-by: default avatarChang S. Bae <chang.seok.bae@intel.com>
Signed-off-by: default avatarBorislav Petkov <bp@suse.de>
Link: https://lkml.kernel.org/r/20211021225527.10184-11-chang.seok.bae@intel.com
parent 53599b4d
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
extern void save_fpregs_to_fpstate(struct fpu *fpu); extern void save_fpregs_to_fpstate(struct fpu *fpu);
extern void fpu__drop(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); extern void fpu_flush_thread(void);
/* /*
......
...@@ -423,8 +423,20 @@ void fpstate_reset(struct fpu *fpu) ...@@ -423,8 +423,20 @@ void fpstate_reset(struct fpu *fpu)
fpu->perm.__user_state_size = fpu_user_cfg.default_size; 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 */ /* 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 *src_fpu = &current->thread.fpu;
struct fpu *dst_fpu = &dst->thread.fpu; struct fpu *dst_fpu = &dst->thread.fpu;
...@@ -455,17 +467,20 @@ int fpu_clone(struct task_struct *dst) ...@@ -455,17 +467,20 @@ int fpu_clone(struct task_struct *dst)
} }
/* /*
* If the FPU registers are not owned by current just memcpy() the * Save the default portion of the current FPU state into the
* state. Otherwise save the FPU registers directly into the * clone. Assume all dynamic features to be defined as caller-
* child's FPU context, without any memory-to-memory copying. * 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(); fpregs_lock();
if (test_thread_flag(TIF_NEED_FPU_LOAD)) { if (test_thread_flag(TIF_NEED_FPU_LOAD))
memcpy(&dst_fpu->fpstate->regs, &src_fpu->fpstate->regs, fpregs_restore_userregs();
dst_fpu->fpstate->size); save_fpregs_to_fpstate(dst_fpu);
} else { if (!(clone_flags & CLONE_THREAD))
save_fpregs_to_fpstate(dst_fpu); fpu_inherit_perms(dst_fpu);
}
fpregs_unlock(); fpregs_unlock();
trace_x86_fpu_copy_src(src_fpu); trace_x86_fpu_copy_src(src_fpu);
......
...@@ -157,7 +157,7 @@ int copy_thread(unsigned long clone_flags, unsigned long sp, unsigned long arg, ...@@ -157,7 +157,7 @@ int copy_thread(unsigned long clone_flags, unsigned long sp, unsigned long arg,
frame->flags = X86_EFLAGS_FIXED; frame->flags = X86_EFLAGS_FIXED;
#endif #endif
fpu_clone(p); fpu_clone(p, clone_flags);
/* Kernel thread ? */ /* Kernel thread ? */
if (unlikely(p->flags & PF_KTHREAD)) { if (unlikely(p->flags & PF_KTHREAD)) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment