[patch 7/8] x86/fpu: Clean up the fpu__clear() variants

From: Thomas Gleixner
Date: Wed Jun 02 2021 - 06:19:58 EST


From: Andy Lutomirski <luto@xxxxxxxxxx>

fpu__clear() currently resets both register state and kernel XSAVE buffer
state. It has two modes: one for all state (supervisor and user) and
another for user state only. fpu__clear_all() uses the "all state"
(user_only=0) mode, while a number of signal paths use the user_only=1
mode.

Make fpu__clear() work only for user state (user_only=1) and remove the
"all state" (user_only=0) code. Rename it to match so it can be used by
the signal paths.

Replace the "all state" (user_only=0) fpu__clear() functionality. Use the
TIF_NEED_FPU_LOAD functionality instead of making any actual hardware
registers changes in this path.

[ Changelog polished by Dave Hansen ]

Signed-off-by: Andy Lutomirski <luto@xxxxxxxxxx>
Signed-off-by: Thomas Gleixner <tglx@xxxxxxxxxxxxx>
---
arch/x86/kernel/fpu/core.c | 62 ++++++++++++++++++++++++++++++---------------
1 file changed, 42 insertions(+), 20 deletions(-)

--- a/arch/x86/kernel/fpu/core.c
+++ b/arch/x86/kernel/fpu/core.c
@@ -354,45 +354,67 @@ static inline void copy_init_fpstate_to_
}

/*
- * Clear the FPU state back to init state.
- *
- * Called by sys_execve(), by the signal handler code and by various
- * error paths.
+ * Reset current's user FPU states to the init states. current's
+ * supervisor states, if any, are not modified by this function. The
+ * caller guarantees that the XSTATE header in memory is intact.
*/
-static void fpu__clear(struct fpu *fpu, bool user_only)
+void fpu__clear_user_states(struct fpu *fpu)
{
WARN_ON_FPU(fpu != &current->thread.fpu);

if (!static_cpu_has(X86_FEATURE_FPU)) {
- fpu__drop(fpu);
- fpu__initialize(fpu);
+ fpu__clear_all(fpu);
return;
}

fpregs_lock();

- if (user_only) {
- if (!fpregs_state_valid(fpu, smp_processor_id()) &&
- xfeatures_mask_supervisor())
- copy_kernel_to_xregs(&fpu->state.xsave,
- xfeatures_mask_supervisor());
- copy_init_fpstate_to_fpregs(xfeatures_mask_user());
- } else {
- copy_init_fpstate_to_fpregs(xfeatures_mask_all);
+ /*
+ * Ensure that current's supervisor states are loaded into
+ * their corresponding registers.
+ */
+ if (xfeatures_mask_supervisor() &&
+ !fpregs_state_valid(fpu, smp_processor_id())) {
+ copy_kernel_to_xregs(&fpu->state.xsave,
+ xfeatures_mask_supervisor());
}

+ /* Reset user states in registers. */
+ copy_init_fpstate_to_fpregs(xfeatures_mask_user());
+
+ /*
+ * Now all FPU registers have their desired values. Inform the
+ * FPU state machine that current's FPU registers are in the
+ * hardware registers.
+ */
fpregs_mark_activate();
+
fpregs_unlock();
}

-void fpu__clear_user_states(struct fpu *fpu)
-{
- fpu__clear(fpu, true);
-}

+/*
+ * Reset current's FPU registers (user and supervisor) to their INIT values.
+ * This is used by execve(); out of an abundance of caution, it completely
+ * wipes and resets the XSTATE buffer in memory.
+ *
+ * Note that XSAVE (unlike XSAVES) expects the XSTATE buffer in memory to
+ * be valid, so there are certain forms of corruption of the XSTATE buffer
+ * in memory that would survive initializing the FPU registers and XSAVEing
+ * them to memory.
+ */
void fpu__clear_all(struct fpu *fpu)
{
- fpu__clear(fpu, false);
+ fpregs_lock();
+ fpu__drop(fpu);
+ /*
+ * This does not change the actual hardware registers; when
+ * fpu__clear_all() returns, TIF_NEED_FPU_LOAD will be set, and a
+ * subsequent exit to user mode will reload the hardware registers
+ * from memory.
+ */
+ fpu__initialize(fpu);
+ fpregs_unlock();
}

/*