[tip: x86/fpu] x86/fpu: Extract restore_from_ia32_fxstate() and clean up fpu__restore_sig()
From: tip-bot2 for Andrei Vagin
Date: Fri Sep 25 2026 - 21:13:10 EST
The following commit has been merged into the x86/fpu branch of tip:
Commit-ID: 6916dec7a02fe0f50d6dc80e5f5c55f11cebd874
Gitweb: https://git.kernel.org/tip/6916dec7a02fe0f50d6dc80e5f5c55f11cebd874
Author: Andrei Vagin <avagin@xxxxxxxxxx>
AuthorDate: Fri, 25 Sep 2026 16:24:49
Committer: Borislav Petkov (AMD) <bp@xxxxxxxxx>
CommitterDate: Fri, 25 Sep 2026 12:14:57 -07:00
x86/fpu: Extract restore_from_ia32_fxstate() and clean up fpu__restore_sig()
When restoring an FPU signal frame for a 32-bit or IA32-compat task on
FXSR-enabled systems, a legacy 32-bit FP frame is present alongside the
FX/XSAVE frame. Because the legacy FP frame duplicates the FP state
portion of the FX/XSAVE frame, for backward compatibility it is treated
as the source of truth, and its state is folded into the FX/XSAVE state
before restoring the registers.
Currently, most of __fpu_restore_sig() is dedicated to handling this
32-bit legacy/compat fpstate, while the native direct restore path lives
in restore_fpregs_from_user(). Having the compat handling intermixed
with the main signal restoration flow makes it tricky to quickly see
what code is doing what.
Extract the 32-bit legacy/compat FPU restore handling into a separate
helper function, restore_from_ia32_fxstate(), and inline the remainder
of __fpu_restore_sig() directly into fpu__restore_sig().
Signed-off-by: Andrei Vagin <avagin@xxxxxxxxxx>
Signed-off-by: Borislav Petkov (AMD) <bp@xxxxxxxxx>
Reviewed-by: Alexander Mikhalitsyn <alexander@xxxxxxxxxxxxx>
Reviewed-by: Chang S. Bae <chang.seok.bae@xxxxxxxxx>
Link: https://patch.msgid.link/20260925162454.1403405-4-avagin@xxxxxxxxxx
---
arch/x86/kernel/fpu/signal.c | 56 ++++++++++++++++++++---------------
1 file changed, 33 insertions(+), 23 deletions(-)
diff --git a/arch/x86/kernel/fpu/signal.c b/arch/x86/kernel/fpu/signal.c
index cd7db6d..b9dcd0c 100644
--- a/arch/x86/kernel/fpu/signal.c
+++ b/arch/x86/kernel/fpu/signal.c
@@ -325,32 +325,26 @@ retry:
return true;
}
-static bool __fpu_restore_sig(void __user *buf_f, void __user *buf_fx,
- bool ia32_fxstate)
+/*
+ * Restore FPU state from a signal frame when a legacy 32-bit FP frame
+ * (buf_f) is present.
+ *
+ * The legacy FP frame duplicates the FP state portion of the FX/XSAVE
+ * frame (buf_fx). For backward compatibility, the legacy FP frame is
+ * treated as the source of truth, and its state is folded into the
+ * FX/XSAVE state before restoring the registers.
+ */
+static bool restore_from_ia32_fxstate(void __user *buf_f, void __user *buf_fx,
+ u64 xrestore_mask, bool fx_only)
{
struct task_struct *tsk = current;
struct fpu *fpu = x86_task_fpu(tsk);
struct user_i387_ia32_struct env;
- bool success, fx_only = false;
union fpregs_state *fpregs;
- u64 xrestore_mask = 0;
-
- if (use_xsave()) {
- struct _fpx_sw_bytes fx_sw_user;
-
- if (!check_xstate_in_sigframe(buf_fx, &fx_sw_user))
- return false;
-
- fx_only = !fx_sw_user.magic1;
- xrestore_mask = fx_sw_user.xfeatures;
- } else {
- xrestore_mask = XFEATURE_MASK_FPSSE;
- }
+ bool success;
- if (likely(!ia32_fxstate)) {
- /* Restore the FPU registers directly from user memory. */
- return restore_fpregs_from_user(buf_fx, xrestore_mask, fx_only);
- }
+ if (!IS_ENABLED(CONFIG_X86_32) && !IS_ENABLED(CONFIG_IA32_EMULATION))
+ return false;
/*
* Copy the legacy state because the FP portion of the FX frame has
@@ -450,10 +444,11 @@ static inline unsigned int xstate_sigframe_size(struct fpstate *fpstate)
bool fpu__restore_sig(void __user *buf, int ia32_frame)
{
struct fpu *fpu = x86_task_fpu(current);
- void __user *buf_fx = buf;
+ bool success = false, fx_only = false;
bool ia32_fxstate = false;
- bool success = false;
+ void __user *buf_fx = buf;
unsigned int size;
+ u64 xrestore_mask;
if (unlikely(!buf)) {
fpu__clear_user_states(fpu);
@@ -482,10 +477,25 @@ bool fpu__restore_sig(void __user *buf, int ia32_frame)
success = !fpregs_soft_set(current, NULL, 0,
sizeof(struct user_i387_ia32_struct),
NULL, buf);
+ goto out;
+ }
+
+ if (use_xsave()) {
+ struct _fpx_sw_bytes fx_sw_user;
+
+ if (!check_xstate_in_sigframe(buf_fx, &fx_sw_user))
+ goto out;
+
+ fx_only = !fx_sw_user.magic1;
+ xrestore_mask = fx_sw_user.xfeatures;
} else {
- success = __fpu_restore_sig(buf, buf_fx, ia32_fxstate);
+ xrestore_mask = XFEATURE_MASK_FPSSE;
}
+ if (ia32_fxstate)
+ success = restore_from_ia32_fxstate(buf, buf_fx, xrestore_mask, fx_only);
+ else
+ success = restore_fpregs_from_user(buf_fx, xrestore_mask, fx_only);
out:
if (unlikely(!success))
fpu__clear_user_states(fpu);