From nobody Fri Sep 25 22:19:12 2026 Received: from mail-oo1-f70.google.com (mail-oo1-f70.google.com [209.85.161.70]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9A19D204C31 for ; Tue, 8 Sep 2026 04:34:32 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.161.70 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788842074; cv=none; b=IaCxcfQ2JOi42olVrqDUgXENsFKUP2gG8TCG1D8eGxp55NJOvlm0d8H+P/tQ6C/o+39jJxCOgcK9knUlrFgySZs8tkya1hZDnu5ghopFsBK3INi2w60TDktXusy8WbTEc3+SGD50d077xOyF6jP/tZQr4wOPDy4NWSC9aSgQk2I= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788842074; c=relaxed/simple; bh=820WAcn/OhQr9WjFQhybPFw8BQBq2i5U1o+fO7Nb+VY=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=m+6n0F1B+ZymdhcIY607VjtQNm3mfdSA1mBjBUKhM4BTfR9lWHsCh9c1wCDWFMW3nwM4eAL7Nv72PeUDpbTI6WxiZ4sAb9fnxYbGP4oGCP+gwU7MVUtmng6r4fVSdpcpl51iEFwyC9Y+xkERf0BmsLYc2dSSNy+kELPap1Atius= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--avagin.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=rj5GEUkd; arc=none smtp.client-ip=209.85.161.70 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--avagin.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="rj5GEUkd" Received: by mail-oo1-f70.google.com with SMTP id 006d021491bc7-6b352d9062fso4940634eaf.3 for ; Mon, 07 Sep 2026 21:34:32 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1788842071; x=1789446871; darn=vger.kernel.org; h=content-type:cc:to:from:subject:message-id:references:mime-version :in-reply-to:date:from:to:cc:subject:date:message-id:reply-to :content-type; bh=K7eSdg298vcCEnyIcdSVrV4v+DIWuyNtu9DRp3wZxhc=; b=rj5GEUkdSRbKCn+fCB9/BIQ7JP90Dmn39WMQJd9vWpTAAaITv5ZPuLvwxFkKHnXGMJ cOsU7znUt1fEV2OAJ/oIA7SlWC8PbQQKchbnn8x3JEqLqOasp6GUAJPxBP1StTsq2jYT RfQOdpQswRaicuguoxbkPV2GYz/Ql07YgnTdWWBZOnDdH/8MtxD6Y8tyGIfJsDRdMAKC nLK9nvto25JYC4KtALY4fHDIOzneG4jQ8i8NHJ8Dapfs589gXFfz61jYyFpiHizzt5gT c9hADS+hfAW8OnTvlB7Zuq/b1ZUqH9wGraqPDgq2j6KH2Fs5v8/C/FDexth+PYUKeM79 MJZQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1788842071; x=1789446871; h=content-type:cc:to:from:subject:message-id:references:mime-version :in-reply-to:date:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to:content-type; bh=K7eSdg298vcCEnyIcdSVrV4v+DIWuyNtu9DRp3wZxhc=; b=il+f8A0BcX5wUUs7vrnnOc49irvQA4a+IApfq6Vq5+pAFZ9rYxbnODzZOltlpo2J9d sPdpfcm7C8FWV25aRLyJYKIJQmmn8ovDMqjj1Dnvm+YJGllgCZ83JHgEyh5FaYYhX3M3 iHxgfH6LgD6xtxzyW40jaZu7ycTLXQFyGZ5n7Jp6YhATjd9K0a7qxO4AkFO19IWACuI8 nGoGBXkDm9HSMh0Z+hYs1ySn66fSWtomPU6A8U3Usa56cw/YSXgLrWvyz7wPbDeN9dL/ CcNm7cZJZF8rbRzVix901EROi/iJSaOU7FIL9V42SSHRMKVQmgIAOBxNn5DPsgSx8cr6 Xqpg== X-Gm-Message-State: AFuF++kAeh/EKEtcGtfIx64L5o0LRgnzSz8BYhIj2Ov8xe5h+/byw1EE ZwhS8YUxKnPm6o1J2nTkFJOwtDtn4F3MvvLu77KDAn+k3jSxSCKCqPW/cdgllmc4n4LesMWMjZ+ G5oSi7g== X-Received: from ilbbn26.prod.google.com ([2002:a05:6e02:339a:b0:50b:4ac:93e6]) (user=avagin job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6820:2d08:b0:6b1:a812:9871 with SMTP id 006d021491bc7-6b6fbbdb3cbmr16624710eaf.18.1788842071107; Mon, 07 Sep 2026 21:34:31 -0700 (PDT) Date: Tue, 8 Sep 2026 04:34:21 +0000 In-Reply-To: <20260908043427.1842515-1-avagin@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260908043427.1842515-1-avagin@google.com> X-Mailer: git-send-email 2.55.0.979.g7e5102b832-goog Message-ID: <20260908043427.1842515-2-avagin@google.com> Subject: [PATCH 1/7] x86/fpu: Document signal frame layout and portability From: Andrei Vagin To: Thomas Gleixner , Ingo Molnar , Borislav Petkov , "Chang S. Bae" Cc: linux-kernel@vger.kernel.org, criu@lists.linux.dev, Dave Hansen , x86@kernel.org, Andrei Vagin , Alexander Mikhalitsyn , "H. Peter Anvin" Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" The x86 signal frame is designed to be self-describing, with the 'xstate_size' field in the software-reserved bytes indicating the actual size of the context. This design is required for portability, allowing a signal frame created on a system with a specific set of xstate features to be restored on a machine with a different (larger) set of features. Document the signal frame software reserved bytes (struct _fpx_sw_bytes) and portability constraints in Documentation/arch/x86/xstate.rst, and add a summary and cross-reference in . Reviewed-by: Alexander Mikhalitsyn Signed-off-by: Andrei Vagin --- Documentation/arch/x86/xstate.rst | 55 ++++++++++++++++++++++++++ arch/x86/include/uapi/asm/sigcontext.h | 14 +++++++ 2 files changed, 69 insertions(+) diff --git a/Documentation/arch/x86/xstate.rst b/Documentation/arch/x86/xst= ate.rst index cec05ac464c1..92e5ff7dd6a2 100644 --- a/Documentation/arch/x86/xstate.rst +++ b/Documentation/arch/x86/xstate.rst @@ -172,3 +172,58 @@ are extended to control the guest permission: =20 Note that some VMMs may have already established a set of supported state components. These options are not presumed to support any particular VMM. + +Signal Frame Layout and Portability +----------------------------------- + +The signal frame is designed to be self-describing and portable. This is +especially important for checkpoint/restore tools like CRIU, which may res= tore +a process on a different host than where it was checkpointed. A signal fra= me +created on a machine with fewer CPU features can be successfully restored = on a +machine with more CPU features. + +Signal Frame Software Reserved Bytes +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +On CPUs supporting XSAVE, bytes 464..511 in the 512-byte FXSAVE/FXRSTOR fr= ame +are reserved for software use and contain ``struct _fpx_sw_bytes`` (define= d in +````):: + + struct _fpx_sw_bytes { + __u32 magic1; + __u32 extended_size; + __u64 xfeatures; + __u32 xstate_size; + __u32 padding[7]; + }; + +- ``magic1``: Set to ``FP_XSTATE_MAGIC1`` (``0x46505853U``) if an extended + xstate context is present; 0 for a legacy frame. +- ``extended_size``: The total size allocated on the stack for the frame, + measured from the ``fpstate`` pointer (including the trailing + ``FP_XSTATE_MAGIC2`` marker and any alignment padding). In 32-bit signal + frames, this also includes the 112-byte legacy ``struct _fpstate_32`` + prefix. +- ``xfeatures``: The mask of xstate features saved in the frame. +- ``xstate_size``: The actual size of the xstate context for the enabled + features (including the 512-byte FXSAVE area and the 64-byte XSAVE heade= r). + +The kernel uses ``xstate_size`` in conjunction with the pointer to the xst= ate +context to locate the ``FP_XSTATE_MAGIC2`` (``0x46505845U``) marker right = after +the xstate context (at ``xstate_context + xstate_size``). In 64-bit signal= frames, +the ``fpstate`` pointer points directly to the xstate context. In 32-bit s= ignal +frames (including 32-bit compat tasks on 64-bit kernels), the ``fpstate`` +pointer points to a legacy 112-byte FPU environment (``struct _fpstate_32`= `) +that precedes the xstate context, so the xstate context starts at +``fpstate + 112`` (and ``extended_size`` spans the entire allocation from +``fpstate``). + +Portability Constraints +^^^^^^^^^^^^^^^^^^^^^^^ + +Signal frame portability is constrained by the architectural XSAVE layout. +Restoration is supported only if the destination host supports all features +present in the frame and uses matching component offsets and sizes for the= m. +While layout compatibility is generally maintained across CPUs from the sa= me +vendor, differences can occur across vendors or if the XSAVE space of a +deprecated feature (e.g. MPX) is repurposed for a newer feature (e.g. APX). diff --git a/arch/x86/include/uapi/asm/sigcontext.h b/arch/x86/include/uapi= /asm/sigcontext.h index d0d9b331d3a1..d85226c67e19 100644 --- a/arch/x86/include/uapi/asm/sigcontext.h +++ b/arch/x86/include/uapi/asm/sigcontext.h @@ -34,6 +34,20 @@ * fpstate+extended_size-FP_XSTATE_MAGIC2_SIZE address) is set to * FP_XSTATE_MAGIC2 so that you can sanity check your size calculations.) * + * The xstate_size field indicates the actual size of the xstate context + * (including the 512-byte FXSAVE area and the 64-byte XSAVE header + * struct _header). This size is used in conjunction with the pointer to + * the xstate context to locate FP_XSTATE_MAGIC2. In 64-bit signal frames, + * the fpstate pointer points directly to the xstate context. In 32-bit + * signal frames (including 32-bit compat tasks on 64-bit kernels), the + * fpstate pointer points to a legacy 112-byte FPU environment + * (struct _fpstate_32) that precedes the xstate context, so the xstate + * context starts at fpstate + 112. + * + * This makes the signal frame self-describing and portable across machines + * with different xstate features. See Documentation/arch/x86/xstate.rst + * for details on signal frame portability and its architectural constrain= ts. + * * This extended area typically grows with newer CPUs that have larger and * larger XSAVE areas. */ --=20 2.55.0.979.g7e5102b832-goog From nobody Fri Sep 25 22:19:12 2026 Received: from mail-oi1-f200.google.com (mail-oi1-f200.google.com [209.85.167.200]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 724353655F5 for ; Tue, 8 Sep 2026 04:34:33 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.167.200 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788842075; cv=none; b=M/kiqrxkEBRyNtm1MYxfF81tA0TDvIN8X9f1F00FD/9uCfWIdmlCQ4Z2z+to1I3PYJ/WmnJs3JMvkhcki1/lhLA5fghrBxtiTDNBCscLz7+/2ALcLrKUI5E+bLljzn2VRDLf680zBKZQMWk26dmEG81xy2XYKEeoQ55omzsmpI8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788842075; c=relaxed/simple; bh=3arWyUpBuTdLuoCRIJ0BXLWc3JQVfLUUbplcM237xWY=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=WtMLbLOjmaHAsvfsswlLSxozipd+LZ9EFihlyYYn7jzltKWv/XKdVDm/aSu/HaULmWpZ8UrWDL0e9YwG/5PdnNB7TK5Rb4xPuD/GtWTpMHUiKocyFvXa0A49ppNOytsZNrwwiRgt42rHxMyv96EfDVnsSmmIs1vT85OdX8JTW94= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--avagin.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=sCs239BJ; arc=none smtp.client-ip=209.85.167.200 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--avagin.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="sCs239BJ" Received: by mail-oi1-f200.google.com with SMTP id 5614622812f47-49d4cb3ca23so4914995b6e.3 for ; Mon, 07 Sep 2026 21:34:33 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1788842072; x=1789446872; darn=vger.kernel.org; h=content-type:cc:to:from:subject:message-id:references:mime-version :in-reply-to:date:from:to:cc:subject:date:message-id:reply-to :content-type; bh=UyzaR2lGME41fPihsd4/ZzoGyGNbpc2wpKrxSvHxLu0=; b=sCs239BJIgBi/P/1Iu4LUEOeyuZwIt3ro3NVOV9yE97RPNzvwRYItGXYdSZulOZIDs 7L0+6TVebBNru+58hAzV0eTi0GxU0tbpRaK8hXyRfQ3uLNRKqn3wTQ7ync5Y8F4Pfku8 bu3gjpxb0bFDqBU+tgZbPiY8lD/AU2WdnbX7KKBG5ESzDGAHHTOPUp3zCj/1R/BwYHAE +UnR8ROoxxQ+n4fLyxPBzFa/faJP4AmT/JlZYwhYpeBYe+ZaC2Z28er1dOjrnmNsRVop ZdFhJ41gCQ7z7SOfxMwsYpyjEq3aLA5W80k1BU3Z7y/QeyRG6CdzIn0akehITPb61W+3 +3RQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1788842072; x=1789446872; h=content-type:cc:to:from:subject:message-id:references:mime-version :in-reply-to:date:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to:content-type; bh=UyzaR2lGME41fPihsd4/ZzoGyGNbpc2wpKrxSvHxLu0=; b=Wk9ScF7lVYxbxmGlEGvYIuvgptZN0a8yz2wu5uhXAcTl8R84nrNPPazhbZthF8EnXL 8MawrCskwCIEfJtaamxaDRydUcGkQGOTSbbT+WBrjLOqAn/AA74FjuMYk1T6SaXUcWEy mOXjzleAGYffm8opZje/Gf9hvC5ll2K3QDhgWQH1l2eJaWqQIqWcy2FClmFatfi4Wy/1 zdDiWsKMF6CRn+mepJPo1qSVR8PsfoFIhEZELU2DoycXR/ugKVRfPXRLkQBh2+NfsXWE 9M58aTKwNA9hmczeFWzW0d+Fr7zFMHbXRk3rd4jNOgvVWeCe5M/gtkM+vhVPl5hy0dgQ 7TeQ== X-Gm-Message-State: AFuF++lpQuR265F7zPXKdWxmCNPWRRjPCnKn2D5XvrWkHFS5OKyi0/rc jIKcoBPIvV8CR3LCv4At4MPB/AzOV3C328Bw8o7eDXCQtY3wStdbjO8AH10WfjZhqD5GS7yO/Gi eAjz6Fg== X-Received: from jabt15-n2.prod.google.com ([2002:a05:6638:b0cf:20b0:5f1:35ea:3ebb]) (user=avagin job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6820:f09:b0:6aa:d97f:453b with SMTP id 006d021491bc7-6b6fd9c68ebmr23912709eaf.28.1788842072108; Mon, 07 Sep 2026 21:34:32 -0700 (PDT) Date: Tue, 8 Sep 2026 04:34:22 +0000 In-Reply-To: <20260908043427.1842515-1-avagin@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260908043427.1842515-1-avagin@google.com> X-Mailer: git-send-email 2.55.0.979.g7e5102b832-goog Message-ID: <20260908043427.1842515-3-avagin@google.com> Subject: [PATCH 2/7] x86/fpu: Clean up and rename variables in signal frame handling From: Andrei Vagin To: Thomas Gleixner , Ingo Molnar , Borislav Petkov , "Chang S. Bae" Cc: linux-kernel@vger.kernel.org, criu@lists.linux.dev, Dave Hansen , x86@kernel.org, Andrei Vagin , Alexander Mikhalitsyn , "H. Peter Anvin" , Ingo Molnar Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Clean up signal frame handling code by renaming several variables for clarity and consistency, and moving masking logic closer to its usage. - Rename 'fxbuf' to 'buf_fx' in check_xstate_in_sigframe() for consistency. - Rename label 'setfx' to 'err_setfx' in check_xstate_in_sigframe() to indicate it is an error path. - In __restore_fpregs_from_user(), rename 'ufeatures' to 'task_xfeatures' and 'xrestore' to 'xrestore_mask'. - Move the masking logic 'xrestore_mask &=3D task_xfeatures' from restore_fpregs_from_user() into __restore_fpregs_from_user(). - Rename 'xrestore' to 'xrestore_mask' in restore_fpregs_from_user() to match the name in __restore_fpregs_from_user() and __fpu_restore_sig(). - In __fpu_restore_sig(), rename 'buf' to 'buf_f' to distinguish it from 'buf_fx', and 'user_xfeatures' to 'xrestore_mask'. No functional changes. Suggested-by: Ingo Molnar Reviewed-by: Alexander Mikhalitsyn Signed-off-by: Andrei Vagin --- arch/x86/kernel/fpu/signal.c | 41 ++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/arch/x86/kernel/fpu/signal.c b/arch/x86/kernel/fpu/signal.c index 33e1284bf3e4..cd7db6dc819b 100644 --- a/arch/x86/kernel/fpu/signal.c +++ b/arch/x86/kernel/fpu/signal.c @@ -24,15 +24,15 @@ * Check for the presence of extended state information in the * user fpstate pointer in the sigcontext. */ -static inline bool check_xstate_in_sigframe(struct fxregs_state __user *fx= buf, +static inline bool check_xstate_in_sigframe(struct fxregs_state __user *bu= f_fx, struct _fpx_sw_bytes *fx_sw) { int min_xstate_size =3D sizeof(struct fxregs_state) + sizeof(struct xstate_header); - void __user *fpstate =3D fxbuf; + void __user *fpstate =3D buf_fx; unsigned int magic2; =20 - if (__copy_from_user(fx_sw, &fxbuf->sw_reserved[0], sizeof(*fx_sw))) + if (__copy_from_user(fx_sw, &buf_fx->sw_reserved[0], sizeof(*fx_sw))) return false; =20 /* Check for the first magic field and other error scenarios. */ @@ -40,7 +40,7 @@ static inline bool check_xstate_in_sigframe(struct fxregs= _state __user *fxbuf, fx_sw->xstate_size < min_xstate_size || fx_sw->xstate_size > x86_task_fpu(current)->fpstate->user_size || fx_sw->xstate_size > fx_sw->extended_size) - goto setfx; + goto err_setfx; =20 /* * Check for the presence of second magic word at the end of memory @@ -53,7 +53,7 @@ static inline bool check_xstate_in_sigframe(struct fxregs= _state __user *fxbuf, =20 if (likely(magic2 =3D=3D FP_XSTATE_MAGIC2)) return true; -setfx: +err_setfx: trace_x86_fpu_xstate_check_failed(x86_task_fpu(current)); =20 /* Set the parameters for fx only state */ @@ -240,15 +240,18 @@ bool copy_fpstate_to_sigframe(void __user *buf, void = __user *buf_fx, int size, u return true; } =20 -static int __restore_fpregs_from_user(void __user *buf, u64 ufeatures, - u64 xrestore, bool fx_only) +static int __restore_fpregs_from_user(void __user *buf, u64 task_xfeatures, + u64 xrestore_mask, bool fx_only) { if (use_xsave()) { - u64 init_bv =3D ufeatures & ~xrestore; + u64 init_bv; int ret; =20 + /* Restore enabled features only. */ + xrestore_mask &=3D task_xfeatures; + init_bv =3D task_xfeatures & ~xrestore_mask; if (likely(!fx_only)) - ret =3D xrstor_from_user_sigframe(buf, xrestore); + ret =3D xrstor_from_user_sigframe(buf, xrestore_mask); else ret =3D fxrstor_from_user_sigframe(buf); =20 @@ -266,20 +269,18 @@ static int __restore_fpregs_from_user(void __user *bu= f, u64 ufeatures, * Attempt to restore the FPU registers directly from user memory. * Pagefaults are handled and any errors returned are fatal. */ -static bool restore_fpregs_from_user(void __user *buf, u64 xrestore, bool = fx_only) +static bool restore_fpregs_from_user(void __user *buf, u64 xrestore_mask, = bool fx_only) { struct fpu *fpu =3D x86_task_fpu(current); int ret; =20 - /* Restore enabled features only. */ - xrestore &=3D fpu->fpstate->user_xfeatures; retry: fpregs_lock(); /* Ensure that XFD is up to date */ xfd_update_state(fpu->fpstate); pagefault_disable(); ret =3D __restore_fpregs_from_user(buf, fpu->fpstate->user_xfeatures, - xrestore, fx_only); + xrestore_mask, fx_only); pagefault_enable(); =20 if (unlikely(ret)) { @@ -324,7 +325,7 @@ static bool restore_fpregs_from_user(void __user *buf, = u64 xrestore, bool fx_onl return true; } =20 -static bool __fpu_restore_sig(void __user *buf, void __user *buf_fx, +static bool __fpu_restore_sig(void __user *buf_f, void __user *buf_fx, bool ia32_fxstate) { struct task_struct *tsk =3D current; @@ -332,7 +333,7 @@ static bool __fpu_restore_sig(void __user *buf, void __= user *buf_fx, struct user_i387_ia32_struct env; bool success, fx_only =3D false; union fpregs_state *fpregs; - u64 user_xfeatures =3D 0; + u64 xrestore_mask =3D 0; =20 if (use_xsave()) { struct _fpx_sw_bytes fx_sw_user; @@ -341,14 +342,14 @@ static bool __fpu_restore_sig(void __user *buf, void = __user *buf_fx, return false; =20 fx_only =3D !fx_sw_user.magic1; - user_xfeatures =3D fx_sw_user.xfeatures; + xrestore_mask =3D fx_sw_user.xfeatures; } else { - user_xfeatures =3D XFEATURE_MASK_FPSSE; + xrestore_mask =3D XFEATURE_MASK_FPSSE; } =20 if (likely(!ia32_fxstate)) { /* Restore the FPU registers directly from user memory. */ - return restore_fpregs_from_user(buf_fx, user_xfeatures, fx_only); + return restore_fpregs_from_user(buf_fx, xrestore_mask, fx_only); } =20 /* @@ -356,7 +357,7 @@ static bool __fpu_restore_sig(void __user *buf, void __= user *buf_fx, * to be ignored for histerical raisins. The legacy state is folded * in once the larger state has been copied. */ - if (__copy_from_user(&env, buf, sizeof(env))) + if (__copy_from_user(&env, buf_f, sizeof(env))) return false; =20 /* @@ -420,7 +421,7 @@ static bool __fpu_restore_sig(void __user *buf, void __= user *buf_fx, * * Preserve supervisor states! */ - u64 mask =3D user_xfeatures | xfeatures_mask_supervisor(); + u64 mask =3D xrestore_mask | xfeatures_mask_supervisor(); =20 fpregs->xsave.header.xfeatures &=3D mask; success =3D !os_xrstor_safe(fpu->fpstate, --=20 2.55.0.979.g7e5102b832-goog From nobody Fri Sep 25 22:19:12 2026 Received: from mail-oo1-f69.google.com (mail-oo1-f69.google.com [209.85.161.69]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 8A07B364926 for ; Tue, 8 Sep 2026 04:34:34 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.161.69 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788842076; cv=none; b=sUoRlivayOHMgRcjBHY4StQYSM5q1AU7R0+uCgZKhvA11BPpT0susi/lEYr3v3JRaRZvor/pFPb3UbUSDE+4P7uIy7C9BZmhB9MsMhVuyCrf0iC7J/LTXZtGWGbXF5yOTwozvbz5BtkBCZLyjOIK8KV8GyjRXpVyOE+sRgV+im8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788842076; c=relaxed/simple; bh=OT8/M/0/yhqxETfMx73ZRyufQ+Q63Q+7ezLnv92HfdU=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=ikZiOBs1LN0iRlqlmnT6Auqul202LP3vyXAgj+Zg9/EYK5YUVs3twbB2w08SVBvm5WeQiRjvqLCsZcPRAOnBUXPNALlXLy0gV8nIHxzlECuS0QvQPunlYud6eumWIwf0DKB9OHxQi0dZ4eJR3rNwnp+gtzbaye4vF6E3wiErV70= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--avagin.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=Mb0H9f9/; arc=none smtp.client-ip=209.85.161.69 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--avagin.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="Mb0H9f9/" Received: by mail-oo1-f69.google.com with SMTP id 006d021491bc7-6bc3374cb0cso283890eaf.1 for ; Mon, 07 Sep 2026 21:34:34 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1788842073; x=1789446873; darn=vger.kernel.org; h=content-type:cc:to:from:subject:message-id:references:mime-version :in-reply-to:date:from:to:cc:subject:date:message-id:reply-to :content-type; bh=cCyDq/GM83Q8gTiNm48MdWVZRtnNjAWy4/ex2uDp03k=; b=Mb0H9f9/NK9fVxa89CR+nY3ARFei3lpPy/TOgh5mkcY/avEF1hOE1/28jL91QoctOs pYGqUOtXRq+eslXafZWF/4hk8X2xbGfRIcUv0J3YU2bCacP5d4DvxgfvuQ6IBUUPPNP/ CxQ6GiWXdSkT/li7Jert86Ja+y+8Q1zIjcvAp1CbzHXIsVuk5E0+qxXf77iiCYfIF2jM qsIo9z61r2fjnAIUY1TUsrEE1jXaYMSPC/QKINKPRj2gw80ijsobytuepkZRnerDRlrT trujnkZip3iAfzLNIOP0hH7snrRnXfWRrU+6DZHTWkffk3MNC9N7vrAUbhnB8uo2DWxb QzAQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1788842073; x=1789446873; h=content-type:cc:to:from:subject:message-id:references:mime-version :in-reply-to:date:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to:content-type; bh=cCyDq/GM83Q8gTiNm48MdWVZRtnNjAWy4/ex2uDp03k=; b=PUt9QsSMKvQ46r3jjZSxYuX0XXQ715fVcJWFZk1OfRoIsShFUcnLqsnFS7SJX520hs OesPyhQBGjJDEltW4qBnkWsoD5NNjBZ7JN4hyQeqe2AJsdg0E7jzMgalmaEV5Y80rPLq cnQZ7/MCYKsGOSRvvWcv+ITn+EJ/ieU7toVft/hpjbR+OH3az1Ygl9WywzZ3fC+11Vk3 KL5Rjq9QQ93KLc6U7RGtyznO9CrZyTUMFIR0ksRpmKGRM/wKoSpDhNn/UcWAEdB+btpm g869yxR5brQVV3ZHQQAxX00xuy/K7pdryFLzazjrd86pniUfJ+OFq8QOHnB92N1Hj8hO fODw== X-Gm-Message-State: AFuF++kImFxU/gcHKjvKRktKZjFycFVmt//0byle9sY1VaNKX6tYxHUK s2WOiPPVBq2z7t6wkfzuLl6ZH2fEgZkshNuDnXWUFOhENkqPp7Gef6Ma4B+hDps6mo2tUslkYJA K4W7ONg== X-Received: from ilbeb18.prod.google.com ([2002:a05:6e02:4612:b0:509:86e0:7b73]) (user=avagin job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6820:221e:b0:6b0:40d9:8ac6 with SMTP id 006d021491bc7-6b6fb8d2aa7mr16910166eaf.9.1788842073017; Mon, 07 Sep 2026 21:34:33 -0700 (PDT) Date: Tue, 8 Sep 2026 04:34:23 +0000 In-Reply-To: <20260908043427.1842515-1-avagin@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260908043427.1842515-1-avagin@google.com> X-Mailer: git-send-email 2.55.0.979.g7e5102b832-goog Message-ID: <20260908043427.1842515-4-avagin@google.com> Subject: [PATCH 3/7] x86/fpu: Extract restore_from_ia32_fxstate() and clean up fpu__restore_sig() From: Andrei Vagin To: Thomas Gleixner , Ingo Molnar , Borislav Petkov , "Chang S. Bae" Cc: linux-kernel@vger.kernel.org, criu@lists.linux.dev, Dave Hansen , x86@kernel.org, Andrei Vagin , Alexander Mikhalitsyn , "H. Peter Anvin" Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Improve readability of the signal frame restoration code. Previously, most of __fpu_restore_sig() was dedicated to handling the 32-bit compat fpstate, while the native direct path lived in restore_fpregs_from_user(). Having the compat handling intermixed with the main flow made it tricky to quickly see what code was 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(). The legacy 32-bit FP frame duplicates the FP state portion of the FX/XSAVE frame. 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. Reviewed-by: Alexander Mikhalitsyn Signed-off-by: Andrei Vagin --- arch/x86/kernel/fpu/signal.c | 67 +++++++++++++++++++++++------------- 1 file changed, 43 insertions(+), 24 deletions(-) diff --git a/arch/x86/kernel/fpu/signal.c b/arch/x86/kernel/fpu/signal.c index cd7db6dc819b..60063a7a44f8 100644 --- a/arch/x86/kernel/fpu/signal.c +++ b/arch/x86/kernel/fpu/signal.c @@ -325,32 +325,24 @@ static bool restore_fpregs_from_user(void __user *buf= , u64 xrestore_mask, bool f return true; } =20 -static bool __fpu_restore_sig(void __user *buf_f, void __user *buf_fx, - bool ia32_fxstate) +#if defined(CONFIG_X86_32) || defined(CONFIG_IA32_EMULATION) +/* + * 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 =3D current; struct fpu *fpu =3D x86_task_fpu(tsk); struct user_i387_ia32_struct env; - bool success, fx_only =3D false; union fpregs_state *fpregs; - u64 xrestore_mask =3D 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 =3D !fx_sw_user.magic1; - xrestore_mask =3D fx_sw_user.xfeatures; - } else { - xrestore_mask =3D XFEATURE_MASK_FPSSE; - } - - if (likely(!ia32_fxstate)) { - /* Restore the FPU registers directly from user memory. */ - return restore_fpregs_from_user(buf_fx, xrestore_mask, fx_only); - } + bool success; =20 /* * Copy the legacy state because the FP portion of the FX frame has @@ -436,6 +428,13 @@ static bool __fpu_restore_sig(void __user *buf_f, void= __user *buf_fx, fpregs_unlock(); return success; } +#else +static inline bool restore_from_ia32_fxstate(void __user *buf_f, void __us= er *buf_fx, + u64 xrestore_mask, bool fx_only) +{ + return false; +} +#endif =20 static inline unsigned int xstate_sigframe_size(struct fpstate *fpstate) { @@ -450,10 +449,11 @@ static inline unsigned int xstate_sigframe_size(struc= t fpstate *fpstate) bool fpu__restore_sig(void __user *buf, int ia32_frame) { struct fpu *fpu =3D x86_task_fpu(current); - void __user *buf_fx =3D buf; + bool success =3D false, fx_only =3D false; bool ia32_fxstate =3D false; - bool success =3D false; + void __user *buf_fx =3D buf; unsigned int size; + u64 xrestore_mask; =20 if (unlikely(!buf)) { fpu__clear_user_states(fpu); @@ -482,10 +482,29 @@ bool fpu__restore_sig(void __user *buf, int ia32_fram= e) success =3D !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 =3D !fx_sw_user.magic1; + xrestore_mask =3D fx_sw_user.xfeatures; } else { - success =3D __fpu_restore_sig(buf, buf_fx, ia32_fxstate); + xrestore_mask =3D XFEATURE_MASK_FPSSE; + } + + if (ia32_fxstate) { + success =3D restore_from_ia32_fxstate(buf, buf_fx, + xrestore_mask, fx_only); + goto out; } =20 + /* Restore the FPU registers directly from user memory. */ + success =3D restore_fpregs_from_user(buf_fx, xrestore_mask, fx_only); out: if (unlikely(!success)) fpu__clear_user_states(fpu); --=20 2.55.0.979.g7e5102b832-goog From nobody Fri Sep 25 22:19:12 2026 Received: from mail-oi1-f198.google.com (mail-oi1-f198.google.com [209.85.167.198]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 3E97F36F41C for ; Tue, 8 Sep 2026 04:34:35 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.167.198 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788842077; cv=none; b=IUOSRM6TELd/tgxvXD3sq9/nUUhtm2Xm6+eyckX8jtOtGoGjRtU783EWgm/Re+VsPljV9QpFd5o69nx+xF9mt3FbqzOYZEYyzqTUz+LDEjQNIHkkzEJYur+Fsx2G2SGtJH9edXq6f1YSW5aT32bh7ql8GGlBlecHG45dpV+75U4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788842077; c=relaxed/simple; bh=9GI5Z4OXm6r2NqJVRrt9DakVj5uxrvUci+yMH6Y5JR0=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=u+QRUMTo0kFpL2nwIKBWFqAL7A+YrOMiK5Wt8+82lDSLws09fCEIWp/cHFUDazL7UJEEztv9epczYNzlRrKsOSUPnIb4SyVgYj17KQX8LeXaNlj6wHh+N9Gqn6XdoLAvxMnxxDfv5AuTSlR1HkW3f+rWN/SuyTxfz78bCKnBQYs= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--avagin.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=pDa8KIQs; arc=none smtp.client-ip=209.85.167.198 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--avagin.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="pDa8KIQs" Received: by mail-oi1-f198.google.com with SMTP id 5614622812f47-4b3962f71e7so4777123b6e.3 for ; Mon, 07 Sep 2026 21:34:35 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1788842074; x=1789446874; darn=vger.kernel.org; h=content-type:cc:to:from:subject:message-id:references:mime-version :in-reply-to:date:from:to:cc:subject:date:message-id:reply-to :content-type; bh=E+3pXqdr3UPO/oCiF3HW/6/WAxnlQ8Fn84d6+8TUwB0=; b=pDa8KIQsI0At0G1wTC1w997r3SSbc0NmAwaZ4BT5NXlCj2UiIfMMOZohn9UOFcAaoA krqlDmGCPzx/ywCNSR6WUdVCO9C7WLkDqTZ79oaJHDHrFp4o+Pdh3oSpj07e2e3aDBQ1 rxXLQ6TU2QmfitCx9P15demf5Ck61gS6B+Lg6FlrSEFLLmrsw4mqmS6B4iopTEknkAXA 5L5k8ohABXWyl9sXneMUDECpZ4z1YK95z+LRHtB8P4rhcUlRq0ASMD/J5fS1FxWcL3SZ zTiC1vLvwGaKWLZtVZTXkb8YwO0YdaEn4vIswhcWWupyHcCDquaq3lLXVnJuLiVovYN5 DvSQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1788842074; x=1789446874; h=content-type:cc:to:from:subject:message-id:references:mime-version :in-reply-to:date:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to:content-type; bh=E+3pXqdr3UPO/oCiF3HW/6/WAxnlQ8Fn84d6+8TUwB0=; b=faKfqx7UvUmQwceelGBZrxO87E1cO/+xQtj0DWleGTEQHTpYOTjlJm/J6hxRYxfHuc vBcGHwLkvOpuW/fwK09OTRXIDWZtH+WCZDwMwPB0Hq6MnFLcDhIvGBls+3RQN08JhoYP gOVa7Vn9T6Fbe1BW+/nrny/pxcHAykpLBTgDNJ7GpE/eL9OTG94TJjo18TcXUk7EE7wG 7r6mvb0b8v0OfNIN7c5W+19dqy6rW2Q1T4CyJ1cZ9wK22e3rgzB4d0lyb0KXUeui/rva D1g8TZ04odWM/262pYOiqrqT71kzEWUR2J5/gLvgJ+0ySsTjATWkLmUHpVFkXjwnaIxh d0Rg== X-Gm-Message-State: AFuF++kxaeBlOkem9HlP6CjRRexMgfu2DFd8h+ooiFgSBewa6d0Y8yMd /KuJzSPj/FiSa9GvLKENV4NlCZJkBdok5c9XlxloJONzAvz74N1I4BQaT/hDZO1CwL0n6gAPUB+ cuyitPw== X-Received: from ilbbe4.prod.google.com ([2002:a05:6e02:3044:b0:509:a53c:7df3]) (user=avagin job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6820:1c9d:b0:6b7:46fa:1695 with SMTP id 006d021491bc7-6b746fa1a33mr14056827eaf.42.1788842073901; Mon, 07 Sep 2026 21:34:33 -0700 (PDT) Date: Tue, 8 Sep 2026 04:34:24 +0000 In-Reply-To: <20260908043427.1842515-1-avagin@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260908043427.1842515-1-avagin@google.com> X-Mailer: git-send-email 2.55.0.979.g7e5102b832-goog Message-ID: <20260908043427.1842515-5-avagin@google.com> Subject: [PATCH 4/7] x86/fpu: Document reasoning of FX-only fallback From: Andrei Vagin To: Thomas Gleixner , Ingo Molnar , Borislav Petkov , "Chang S. Bae" Cc: linux-kernel@vger.kernel.org, criu@lists.linux.dev, Dave Hansen , x86@kernel.org, Andrei Vagin , Alexander Mikhalitsyn , "H. Peter Anvin" Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add a comment to check_xstate_in_sigframe() to explain the reasoning behind falling back to the FX-only state when signal frame metadata is inconsistent. The fallback is intended to preserve backward compatibility with legacy user-space processes that are not aware of XSAVE states and might only fill or copy the legacy FP state. However, this fallback should be avoided whenever possible. If a process was actively using extended features, falling back to the FX-only state silently resets those extended registers to their initial state, which can lead to silent user-space state corruption. Reviewed-by: Alexander Mikhalitsyn Reviewed-by: Chang S. Bae Signed-off-by: Andrei Vagin --- arch/x86/kernel/fpu/signal.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/arch/x86/kernel/fpu/signal.c b/arch/x86/kernel/fpu/signal.c index 60063a7a44f8..d686d5f7f3d0 100644 --- a/arch/x86/kernel/fpu/signal.c +++ b/arch/x86/kernel/fpu/signal.c @@ -54,6 +54,14 @@ static inline bool check_xstate_in_sigframe(struct fxreg= s_state __user *buf_fx, if (likely(magic2 =3D=3D FP_XSTATE_MAGIC2)) return true; err_setfx: + /* + * The fallback to FX-only state is used to preserve backward + * compatibility with user-space processes that are not aware of xsave + * states. + * + * In all other cases, returning false (to trigger SIGSEGV) is + * preferred to avoid silent user-space state corruption. + */ trace_x86_fpu_xstate_check_failed(x86_task_fpu(current)); =20 /* Set the parameters for fx only state */ --=20 2.55.0.979.g7e5102b832-goog From nobody Fri Sep 25 22:19:12 2026 Received: from mail-oi1-f199.google.com (mail-oi1-f199.google.com [209.85.167.199]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 3AEC636F906 for ; Tue, 8 Sep 2026 04:34:36 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.167.199 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788842078; cv=none; b=olaGSsazPZ39oxIOi9kN+G3FEuN23uVvjDBMDFX0OIzgLhVKa++KgsUjFTeDC5nHU/tR4/vkLAlj9eyBA6V6hVtoJDJXjU+jxA2aSs+Zm9vx3moPIxQ1Bh2ASFCZo2zcSVGr1ezJMGc75UqO2Z2TSYjHgpm/FL1s+CNX955PotA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788842078; c=relaxed/simple; bh=Yzbz5KsjH5VLNThiyWRSB/Nuu62H2W9/vrT//XuTC8w=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=J4JNt5yoA+WBwA0r9jLXLqTwmSlzSFasiBJLiFIDPwt9/wwPRPAUP1Vwy4JQtFoMvwf0mXdz9t73Ie/INeDIMeShaBjYBldP98kNkn+ZvVzK8N7VACznuw+lrNrEdS2jWMs00z0tv10ej2Jp4Yamth/LgB06/CJdZA8H3jbX8nY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--avagin.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=I0XERpU7; arc=none smtp.client-ip=209.85.167.199 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--avagin.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="I0XERpU7" Received: by mail-oi1-f199.google.com with SMTP id 5614622812f47-4b1bc2e44e0so4965503b6e.0 for ; Mon, 07 Sep 2026 21:34:36 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1788842075; x=1789446875; darn=vger.kernel.org; h=content-type:cc:to:from:subject:message-id:references:mime-version :in-reply-to:date:from:to:cc:subject:date:message-id:reply-to :content-type; bh=5TNNxc4M/PskCbQkTRvLFwOXh+gojv+fbauKLe1Eeeg=; b=I0XERpU7qxDu8Tu+/ahJ8Bp9YUUgPzrSaebN8pYJkC3Gws4DUnQZOuT7QD+nzOWooa job0UfZLToohvIUxs5ooA2WNNLz1u4zr0OvUOBBDl2yqN5Raluiw/fBbaFnGmVGmke3x ZJvjGKSPr332TrriF8bumLgOOsZM8yyuWoGVOF59ow9CdfoQGcMb7f4K65CSqYSvJnuN Z61DP0v5vBn2tOn3eUE5Zo7n2u7Du1a3pK8/tgZGhwkHwC4/rJZajanXkrKfjpI4O7lW LUEUHjuCurGOkDwl/+rj/N7eZF8V/yhC5WT3XdOzHtw+6bUwDUbcUrSXRHDMYRoqGq9d TQ/w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1788842075; x=1789446875; h=content-type:cc:to:from:subject:message-id:references:mime-version :in-reply-to:date:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to:content-type; bh=5TNNxc4M/PskCbQkTRvLFwOXh+gojv+fbauKLe1Eeeg=; b=rW5uqoyNx9I4dWzArVRmitxYSmdxWpOV9PtzkBwaX0jZWU9R6ZqZ/PRtEd9jv8Cr15 9aKDjDA+oiNg3Qm/MkK7csgbOhE8oswlMtntL8u4me/CSmgx4OQsJ+k5JSwtEGhBsS4k 2Cv7TrnkoihVCV7xiTBtXLedGS4+Q/TcL/MoZyX75EjregIg5pxxef7KT6yE5n0cRC65 bIr37U2spsn8ecslbOjFm6vxVtCvWcarMhbc1jArC0o4JO0CvUjlyPttkzrouv2T92t1 h3ttMVETNzbSZOrTUfMPM0MLRyLOlcH4rSPM1gJS6DGTtH6AtWWUvOYU2QH0QMaoqK80 qWpg== X-Gm-Message-State: AFuF++lF6oYnfnTjOS1hRodtCQE3BchHxqBgtKN1HTayGBvmb33qUrMk ODZ/AHo3r5V1AjUM7rckjhbvEzbqR+V0ya+kMqu3zYxpd3WAfZoN4q54/SABlOw9Bg130jGSklq 4JtPacA== X-Received: from iobih9.prod.google.com ([2002:a05:6602:6b09:b0:9a8:51c0:4784]) (user=avagin job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6820:a28c:20b0:6ba:2927:3661 with SMTP id 006d021491bc7-6ba29273f2fmr7920635eaf.18.1788842074915; Mon, 07 Sep 2026 21:34:34 -0700 (PDT) Date: Tue, 8 Sep 2026 04:34:25 +0000 In-Reply-To: <20260908043427.1842515-1-avagin@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260908043427.1842515-1-avagin@google.com> X-Mailer: git-send-email 2.55.0.979.g7e5102b832-goog Message-ID: <20260908043427.1842515-6-avagin@google.com> Subject: [PATCH 5/7] x86/fpu: Fix potential underflow in xstate_calculate_size() From: Andrei Vagin To: Thomas Gleixner , Ingo Molnar , Borislav Petkov , "Chang S. Bae" Cc: linux-kernel@vger.kernel.org, criu@lists.linux.dev, Dave Hansen , x86@kernel.org, Andrei Vagin , Alexander Mikhalitsyn , "H. Peter Anvin" Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" xstate_calculate_size() calculates the size required for a given set of xfeatures. It determines the topmost feature by finding the most significant bit in xfeatures using fls64(xfeatures) - 1. If xfeatures is 0, fls64(0) returns 0, and topmost becomes -1. Previously, topmost was unsigned int, so -1 underflowed to UINT_MAX. This caused the subsequent check `topmost <=3D XFEATURE_SSE` to fail, and the code proceeded to access xstate arrays using topmost (UINT_MAX) as an index, leading to an out-of-bounds access. Fix this by checking if xfeatures only contains legacy features (FP/SSE) or is empty (!(xfeatures & ~XFEATURE_MASK_FPSSE)) before calculating topmost. Fixes: d6d6d50f1e80 ("x86/fpu/xstate: Consolidate size calculations") Reviewed-by: Alexander Mikhalitsyn Reviewed-by: Chang S. Bae Signed-off-by: Andrei Vagin --- arch/x86/kernel/fpu/xstate.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/arch/x86/kernel/fpu/xstate.c b/arch/x86/kernel/fpu/xstate.c index a7b6524a9dea..fe0a29f599d2 100644 --- a/arch/x86/kernel/fpu/xstate.c +++ b/arch/x86/kernel/fpu/xstate.c @@ -589,12 +589,13 @@ static bool __init check_xstate_against_struct(int nr) =20 static unsigned int xstate_calculate_size(u64 xfeatures, bool compacted) { - unsigned int topmost =3D fls64(xfeatures) - 1; - unsigned int offset, i; + unsigned int topmost, offset, i; =20 - if (topmost <=3D XFEATURE_SSE) + if (!(xfeatures & ~XFEATURE_MASK_FPSSE)) return sizeof(struct xregs_state); =20 + topmost =3D fls64(xfeatures) - 1; + if (compacted) { offset =3D xfeature_get_offset(xfeatures, topmost); } else { --=20 2.55.0.979.g7e5102b832-goog From nobody Fri Sep 25 22:19:12 2026 Received: from mail-oa1-f70.google.com (mail-oa1-f70.google.com [209.85.160.70]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 4ABFD3769F4 for ; Tue, 8 Sep 2026 04:34:37 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.160.70 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788842083; cv=none; b=qoSEtCOAaZ8uMGXQyGPWMClZ2zivWITTdqQuQIjBWpQ3EYFNdnRMjjySMtchAta/Ep9VoggPl5YDtbkIAbmDayY/nhth5F6hDMHn/JqiorZgAjcqb+dXkUQTqmYn9MIrjs17/Bq5LQPnAIBHKUurU7yHKuQL1+lINbCp1JLS8hc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788842083; c=relaxed/simple; bh=GpJ13KDRv1KtlmyYw9gNXVJEXhFgdRO5bHwPuyUvWYE=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=gOh6zWaP4Q4wrkIExu2WbYZ2KTvqZ+MGIHDkS3lQTmH0sXzBDId2FXebt5rFiTPApsuOHUJaDOYOoTJc3Vejx7En12CFNVNAh3Kb2mHoH8Cm6sssebU7mIyIcxYvNyEvlks7HalUwKM2vGCpsRkduAsUnEL84Hqw7lD1Nni1kFI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--avagin.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=gfNQz837; arc=none smtp.client-ip=209.85.160.70 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--avagin.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="gfNQz837" Received: by mail-oa1-f70.google.com with SMTP id 586e51a60fabf-4519ef1babeso2778627fac.2 for ; Mon, 07 Sep 2026 21:34:37 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1788842076; x=1789446876; darn=vger.kernel.org; h=content-type:cc:to:from:subject:message-id:references:mime-version :in-reply-to:date:from:to:cc:subject:date:message-id:reply-to :content-type; bh=my0QXM+ikv9/PpCYzgn4TQ3ElcvYhNy4NwT6XgTRAzE=; b=gfNQz837a7l80YZeMwWSB+qSejnyEpCY+y93xb17HWuAogBBMo3Ua1UNaOVjhdf04l E4UupdXf3+5l5ON6mczu6iO0K7MktOWE8ADwIfTOnjcrneXD24Yyrq4kYA7zOWmUx86X dU0Sm8sywO4yL7O61w9W7NcCAP6I2IVceQSzxAwqW6VGxPb3YnPcyLbBtcfo6gu4MrKb +wMouzfju7W9GAnLG0PbSfOQXutwki+MduelNeLWE8DX7oJpw10w38O693HDYDGgBYcO 6Ug2XzUndZv5NNNX/EkcxqWM9rntCpBsjgNStYh3Cxa0k3e8wLN2stvYDczguK4aidZI Z7xQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1788842076; x=1789446876; h=content-type:cc:to:from:subject:message-id:references:mime-version :in-reply-to:date:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to:content-type; bh=my0QXM+ikv9/PpCYzgn4TQ3ElcvYhNy4NwT6XgTRAzE=; b=dGoQyVKF40ZJSUbzk+tPvbVzVZh3tGIHfiXsNOG4dELLhAou1tOnunMgNHWT+hQQrq VWGOpqSHmUEOEJPZz3GzahXiloizlOR3Am6eDIEcyRncRaDXqGihnKvGtHfh6GUM0B81 YCeYONUjKi4cpixaobg4cCv/9KQB6r0yqK8cj3BSFAFb2mGcRx5ujRM8zar47p1OzN4a 59gy63TZCuQnimC1lABc8nzgydbBUmtnoSymQAU55XROFOBHi3vemC2gUaQgl9wBUEP3 s/c0DGIRAnh8D3nUWTriQtQHaWcftLkoscmR0rVcf0JLPKqY7kkqFLiAE2cstcxph9mN Vk9A== X-Gm-Message-State: AFuF++lMl9ry8vN2QHKMPpUd6UJ0ae+ivqSmDmf2WzDy9Hs8077uvVed Mf1Ue+g+8jE8YZ73L6um/TXJjnFhWv5VAmf97JpzixGpLNHAvb0QwA0spwQJwWFHd6vyn94Pdrm 3gaKP9g== X-Received: from ilex16.prod.google.com ([2002:a05:6e02:6290:b0:50b:2126:7461]) (user=avagin job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6820:211:b0:6aa:ed21:6c95 with SMTP id 006d021491bc7-6b6f7e6daa7mr14676398eaf.0.1788842075735; Mon, 07 Sep 2026 21:34:35 -0700 (PDT) Date: Tue, 8 Sep 2026 04:34:26 +0000 In-Reply-To: <20260908043427.1842515-1-avagin@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260908043427.1842515-1-avagin@google.com> X-Mailer: git-send-email 2.55.0.979.g7e5102b832-goog Message-ID: <20260908043427.1842515-7-avagin@google.com> Subject: [PATCH 6/7] x86/fpu: Pre-fault only required size of xstate buffer From: Andrei Vagin To: Thomas Gleixner , Ingo Molnar , Borislav Petkov , "Chang S. Bae" Cc: linux-kernel@vger.kernel.org, criu@lists.linux.dev, Dave Hansen , x86@kernel.org, Andrei Vagin , Alexander Mikhalitsyn , "H. Peter Anvin" Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" The kernel previously used the default task FPU state size (user_size) to fault in the user buffer when restoring FPU registers from a signal frame. This can lead to attempting to fault in memory past the end of the actual frame if the frame was smaller than the default size. Introduce consistency checks to calculate the actual required size for the features enabled in the xfeatures mask, ensure that the provided xstate_size is sufficient, and shrink it to the actual required size. Use this validated size to fault in the user buffer. Keep the strict check that the provided xstate_size does not exceed the default user_size for now. Reviewed-by: Alexander Mikhalitsyn Reviewed-by: Chang S. Bae Signed-off-by: Andrei Vagin --- arch/x86/kernel/fpu/signal.c | 38 +++++++++++++++++++++++++++--------- arch/x86/kernel/fpu/xstate.c | 2 +- arch/x86/kernel/fpu/xstate.h | 2 ++ 3 files changed, 32 insertions(+), 10 deletions(-) diff --git a/arch/x86/kernel/fpu/signal.c b/arch/x86/kernel/fpu/signal.c index d686d5f7f3d0..452ebef88511 100644 --- a/arch/x86/kernel/fpu/signal.c +++ b/arch/x86/kernel/fpu/signal.c @@ -29,7 +29,8 @@ static inline bool check_xstate_in_sigframe(struct fxregs= _state __user *buf_fx, { int min_xstate_size =3D sizeof(struct fxregs_state) + sizeof(struct xstate_header); - void __user *fpstate =3D buf_fx; + struct fpstate *fpstate =3D x86_task_fpu(current)->fpstate; + void __user *buf =3D buf_fx; unsigned int magic2; =20 if (__copy_from_user(fx_sw, &buf_fx->sw_reserved[0], sizeof(*fx_sw))) @@ -38,8 +39,8 @@ static inline bool check_xstate_in_sigframe(struct fxregs= _state __user *buf_fx, /* Check for the first magic field and other error scenarios. */ if (fx_sw->magic1 !=3D FP_XSTATE_MAGIC1 || fx_sw->xstate_size < min_xstate_size || - fx_sw->xstate_size > x86_task_fpu(current)->fpstate->user_size || - fx_sw->xstate_size > fx_sw->extended_size) + fx_sw->xstate_size > fpstate->user_size || + fx_sw->extended_size < fx_sw->xstate_size + FP_XSTATE_MAGIC2_SIZE) goto err_setfx; =20 /* @@ -48,11 +49,27 @@ static inline bool check_xstate_in_sigframe(struct fxre= gs_state __user *buf_fx, * fpstate layout with out copying the extended state information * in the memory layout. */ - if (__get_user(magic2, (__u32 __user *)(fpstate + fx_sw->xstate_size))) + if (__get_user(magic2, (__u32 __user *)(buf + fx_sw->xstate_size))) return false; + if (unlikely(magic2 !=3D FP_XSTATE_MAGIC2)) + goto err_setfx; =20 - if (likely(magic2 =3D=3D FP_XSTATE_MAGIC2)) - return true; + if (fx_sw->xstate_size !=3D fpstate->user_size || + fx_sw->xfeatures !=3D fpstate->user_xfeatures) { + unsigned int xsize; + u64 xfeatures; + + /* Calculate size of enabled features only. */ + xfeatures =3D fx_sw->xfeatures & fpstate->user_xfeatures; + + xsize =3D xstate_calculate_size(xfeatures, false); + if (fx_sw->xstate_size < xsize) + return false; + + fx_sw->xstate_size =3D xsize; + } + + return true; err_setfx: /* * The fallback to FX-only state is used to preserve backward @@ -277,7 +294,8 @@ static int __restore_fpregs_from_user(void __user *buf,= u64 task_xfeatures, * Attempt to restore the FPU registers directly from user memory. * Pagefaults are handled and any errors returned are fatal. */ -static bool restore_fpregs_from_user(void __user *buf, u64 xrestore_mask, = bool fx_only) +static bool restore_fpregs_from_user(void __user *buf, u64 xrestore_mask, + bool fx_only, size_t xstate_size) { struct fpu *fpu =3D x86_task_fpu(current); int ret; @@ -311,7 +329,7 @@ static bool restore_fpregs_from_user(void __user *buf, = u64 xrestore_mask, bool f if (ret !=3D X86_TRAP_PF) return false; =20 - if (!fault_in_readable(buf, fpu->fpstate->user_size)) + if (!fault_in_readable(buf, xstate_size)) goto retry; return false; } @@ -501,8 +519,10 @@ bool fpu__restore_sig(void __user *buf, int ia32_frame) =20 fx_only =3D !fx_sw_user.magic1; xrestore_mask =3D fx_sw_user.xfeatures; + size =3D fx_sw_user.xstate_size; } else { xrestore_mask =3D XFEATURE_MASK_FPSSE; + size =3D fpu->fpstate->user_size; } =20 if (ia32_fxstate) { @@ -512,7 +532,7 @@ bool fpu__restore_sig(void __user *buf, int ia32_frame) } =20 /* Restore the FPU registers directly from user memory. */ - success =3D restore_fpregs_from_user(buf_fx, xrestore_mask, fx_only); + success =3D restore_fpregs_from_user(buf_fx, xrestore_mask, fx_only, size= ); out: if (unlikely(!success)) fpu__clear_user_states(fpu); diff --git a/arch/x86/kernel/fpu/xstate.c b/arch/x86/kernel/fpu/xstate.c index fe0a29f599d2..4fe148338382 100644 --- a/arch/x86/kernel/fpu/xstate.c +++ b/arch/x86/kernel/fpu/xstate.c @@ -587,7 +587,7 @@ static bool __init check_xstate_against_struct(int nr) return true; } =20 -static unsigned int xstate_calculate_size(u64 xfeatures, bool compacted) +unsigned int xstate_calculate_size(u64 xfeatures, bool compacted) { unsigned int topmost, offset, i; =20 diff --git a/arch/x86/kernel/fpu/xstate.h b/arch/x86/kernel/fpu/xstate.h index 38a2862f09d3..c73cf2444de6 100644 --- a/arch/x86/kernel/fpu/xstate.h +++ b/arch/x86/kernel/fpu/xstate.h @@ -55,6 +55,8 @@ extern int copy_sigframe_from_user_to_xstate(struct task_= struct *tsk, const void extern void fpu__init_cpu_xstate(void); extern void fpu__init_system_xstate(unsigned int legacy_size); =20 +extern unsigned int xstate_calculate_size(u64 xfeatures, bool compacted); + extern void __user *get_xsave_addr_user(struct xregs_state __user *xsave, = int xfeature_nr); =20 static inline u64 xfeatures_mask_supervisor(void) --=20 2.55.0.979.g7e5102b832-goog From nobody Fri Sep 25 22:19:12 2026 Received: from mail-oo1-f72.google.com (mail-oo1-f72.google.com [209.85.161.72]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 4D390204C31 for ; Tue, 8 Sep 2026 04:34:42 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.161.72 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788842084; cv=none; b=kJ69cL4IJuFvl5l5jbYZR79hqbAg8FypTXfhTcOCSZc+0RxtSMz5W1Oax33vDZYQy1DPID2FuIUX1Tp6f6/fUWkdW9uilw4Bu8IotWwPUvJeap7lQZbjSvm/7KFErpPsTSgGDBLqBSFgzWv8UmlTftX7zpGZ3HjOC6Y7uVfoIIM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788842084; c=relaxed/simple; bh=zYqFe0NRBz1fFvs4TdBDdowJAswYqgLWUgvYLXwpNC0=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=lqQRmOa+c2xFf25NumlkSAdFiJ9LHeSkV+lf7INASIq1jOVGnBSih7cP1fBtVHJX8fhWJ7DnN3QDQb5Up8ptm+SAeqUM0+nWT/GOz46raUZ2q4JF981Mfpqhpr7uZvRXkbTA84/t1YJ5on1fqVOfmddYj08dlfHd7Uv48bu7X5w= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--avagin.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=OOz5lS9x; arc=none smtp.client-ip=209.85.161.72 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--avagin.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="OOz5lS9x" Received: by mail-oo1-f72.google.com with SMTP id 006d021491bc7-6b1abf73d1fso2120220eaf.3 for ; Mon, 07 Sep 2026 21:34:42 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1788842077; x=1789446877; darn=vger.kernel.org; h=content-type:cc:to:from:subject:message-id:references:mime-version :in-reply-to:date:from:to:cc:subject:date:message-id:reply-to :content-type; bh=XY3hQ0xzaAWy4M5qAqcF3UMax0jVDeGWrrn7UI3joc0=; b=OOz5lS9x1z64s7Ixl2d1IUMiHWbVjacMq0zp0uWYdzXZ6vqxfC300X/dodFI7WwcVZ 6VyuWJfSVIeVJ4IXyjXts2XR/sLR57sZh0/PNO4+7nAlCAqVo5eUskCiZDN3YXyx9LHl gRoA2/7+yAyY8tn/gLHpBiWnK/gjpVFpi/sZDRuh7FvymHrzG1oVxDlOwldtr1QIEgWN EXl8/nndVmh3BHQzMXrzDNDtPGc+fwez1s2U5QlRmA32GsdpNS4FtCA3xchxTceK0x1p +PfBQy+SMQGkEH3bi9iIBZyDzGTlv+7hVbFStV2gyRHvXvMFZFUgK4OHcoCUloGWwAi4 RbNw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1788842077; x=1789446877; h=content-type:cc:to:from:subject:message-id:references:mime-version :in-reply-to:date:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to:content-type; bh=XY3hQ0xzaAWy4M5qAqcF3UMax0jVDeGWrrn7UI3joc0=; b=FFiOWdhyf5Umh4MmEyd+huiKoKslcMhmjwLzulQ0Lx/4jep9OTNS2oEkTWaAuO7Faa xM2BQwaSI3rcYAzUD2Md8pSmKULtwfAvcKqwlYHn637bqAKlx0EMinB6RnO9QzdJUqBF AaRuijvLJNuI1uuNnrLYg1+917RU9C6GhWYzDZW8ext32dGy65aW8CalF3r914gHHuW+ MbdnBbP4j7AMeOnKecLwUQFIa6f+BBLlHpj+Bp+ktsqWTdoaTtSuij5JTYkaFA8F+R3T pZlRQtqbEa5cTaiXdubF5OWeah2oKP+sf/Dgz+1izyn8W7MEFkJGZOfkUXFjYd96AZfq YEWw== X-Gm-Message-State: AFuF++mMILH3WgwsyHHDdxvFJQzywEEZQq0eyNa3FGPTdLpMPee9/iFl UFDamnPfGznx+CnkRZ8xsTkYvyT0kn63ce/NYanNAlrhoGWIrVkQwpd43c6Istr4uBsyIIt5jfn 8XdlSQg== X-Received: from ilsd9.prod.google.com ([2002:a05:6e02:509:b0:505:fb7c:de7d]) (user=avagin job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6820:4dee:b0:6b7:8396:f3d5 with SMTP id 006d021491bc7-6b78396f81amr12890637eaf.55.1788842076754; Mon, 07 Sep 2026 21:34:36 -0700 (PDT) Date: Tue, 8 Sep 2026 04:34:27 +0000 In-Reply-To: <20260908043427.1842515-1-avagin@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260908043427.1842515-1-avagin@google.com> X-Mailer: git-send-email 2.55.0.979.g7e5102b832-goog Message-ID: <20260908043427.1842515-8-avagin@google.com> Subject: [PATCH 7/7] selftests/x86: Add tests for signal frame FPU portability From: Andrei Vagin To: Thomas Gleixner , Ingo Molnar , Borislav Petkov , "Chang S. Bae" Cc: linux-kernel@vger.kernel.org, criu@lists.linux.dev, Dave Hansen , x86@kernel.org, Andrei Vagin , Alexander Mikhalitsyn , "H. Peter Anvin" Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add a new selftest tools/testing/selftests/x86/sigframe_fpu_portability.c to verify signal frame portability and consistency when the xstate size is shrunk: - test_valid_shrunk_xstate_size: Verifies that the kernel correctly restores the xstate context from a signal frame where xstate_size has been manually shrunk to only cover active features, as long as the FP_XSTATE_MAGIC2 marker is correctly placed. This simulates migrating a process created on a host with fewer xstate features to a host with more features. - test_invalid_shrunk_xstate_size: Verifies that the kernel rejects (via SIGSEGV) a signal frame where xstate_size is smaller than required by the enabled features in the xfeatures mask. Reviewed-by: Alexander Mikhalitsyn Signed-off-by: Andrei Vagin --- tools/testing/selftests/x86/Makefile | 5 +- .../selftests/x86/sigframe_fpu_portability.c | 246 ++++++++++++++++++ tools/testing/selftests/x86/xstate.c | 12 - tools/testing/selftests/x86/xstate.h | 20 ++ 4 files changed, 270 insertions(+), 13 deletions(-) create mode 100644 tools/testing/selftests/x86/sigframe_fpu_portability.c diff --git a/tools/testing/selftests/x86/Makefile b/tools/testing/selftests= /x86/Makefile index 434065215d12..72071deda978 100644 --- a/tools/testing/selftests/x86/Makefile +++ b/tools/testing/selftests/x86/Makefile @@ -19,7 +19,8 @@ TARGETS_C_32BIT_ONLY :=3D entry_from_vm86 test_syscall_vd= so unwind_vdso \ test_FCMOV test_FCOMI test_FISTTP \ vdso_restorer TARGETS_C_64BIT_ONLY :=3D fsgsbase sysret_rip syscall_numbering \ - corrupt_xstate_header amx lam test_shadow_stack avx apx + corrupt_xstate_header amx lam test_shadow_stack avx apx \ + sigframe_fpu_portability # Some selftests require 32bit support enabled also on 64bit systems TARGETS_C_32BIT_NEEDED :=3D ldt_gdt ptrace_syscall =20 @@ -138,3 +139,5 @@ $(OUTPUT)/avx_64: CFLAGS +=3D -mno-avx -mno-avx512f $(OUTPUT)/amx_64: EXTRA_FILES +=3D xstate.c $(OUTPUT)/avx_64: EXTRA_FILES +=3D xstate.c $(OUTPUT)/apx_64: EXTRA_FILES +=3D xstate.c + +$(OUTPUT)/sigframe_fpu_portability_64: CFLAGS +=3D -mno-avx -mno-avx512f diff --git a/tools/testing/selftests/x86/sigframe_fpu_portability.c b/tools= /testing/selftests/x86/sigframe_fpu_portability.c new file mode 100644 index 000000000000..5ddab44a0369 --- /dev/null +++ b/tools/testing/selftests/x86/sigframe_fpu_portability.c @@ -0,0 +1,246 @@ +// SPDX-License-Identifier: GPL-2.0-only +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "helpers.h" +#include "xstate.h" + +#ifndef FP_XSTATE_MAGIC2_SIZE +#define FP_XSTATE_MAGIC2_SIZE sizeof(FP_XSTATE_MAGIC2) +#endif + +/* + * This test verifies the FPU portability and consistency of the signal fr= ame. + * + * - test_valid_shrunk_xstate_size: + * Verifies that the kernel restores state from a frame with xstate_size + * shrunk to only include active features. + * + * - test_invalid_shrunk_xstate_size: + * Verifies that the kernel rejects a frame if xstate_size is too small = for + * the features enabled in xfeatures. + */ + +#define SIGFRAME_XSTATE_HDR_OFFSET 512 +#define XSTATE_SSE_ONLY_SIZE (SIGFRAME_XSTATE_HDR_OFFSET + XSAVE_HDR_SIZE) +#define XFEATURE_MASK_FPSSE ((1 << XFEATURE_FP) | (1 << XFEATURE_SSE)) + +static uint32_t ymm_offset; +static uint32_t xstate_size_ymm; +static pid_t self_pid; + +/* Use a raw syscall instead of raise() to avoid clobbering FPU registers.= */ +static inline void raw_raise(int sig) +{ + register long rax asm("rax") =3D SYS_kill; + register long rdi asm("rdi") =3D self_pid; + register long rsi asm("rsi") =3D sig; + + asm volatile ("syscall" + : "+r" (rax) + : "r" (rdi), "r" (rsi) + : "rcx", "r11", "memory"); +} + +/* + * Avoid using printf() in signal handlers as it is not + * async-signal-safe. + */ +#define SIGNAL_BUF_LEN 1024 +static char sig_err_buf[SIGNAL_BUF_LEN]; + +static void sig_print(const char *msg) +{ + int left =3D SIGNAL_BUF_LEN - strlen(sig_err_buf) - 1; + + strncat(sig_err_buf, msg, left); +} + + +static void check_avx_support(void) +{ + struct xstate_info xstate; + uint32_t eax, ebx, ecx, edx; + + /* Check CPUID.01H:ECX.OSXSAVE[bit 27] before calling xgetbv to avoid #UD= */ + __cpuid(1, eax, ebx, ecx, edx); + if (!(ecx & (1 << 27))) + ksft_exit_skip("OSXSAVE not enabled by OS\n"); + + /* Check XCR0[2] (YMM) is enabled by OS */ + if (!(xgetbv(0) & (1 << XFEATURE_YMM))) + ksft_exit_skip("AVX (YMM) not enabled in XCR0\n"); + + xstate =3D get_xstate_info(XFEATURE_YMM); + if (!xstate.size) + ksft_exit_skip("AVX not supported by hardware\n"); + + ymm_offset =3D xstate.xbuf_offset; + xstate_size_ymm =3D xstate.xbuf_offset + xstate.size; +} + +#define TEST_YMMH_VAL (0x5656565656565656UL) + +__attribute__((target("avx"))) +static void read_ymm0(uint64_t *v) +{ + asm volatile ("vmovdqu %%ymm0, %0" : "=3Dm" (*(char (*)[32])v)); +} + +__attribute__((target("avx"))) +static void write_ymm0(uint64_t *v) +{ + asm volatile ("vmovdqu %0, %%ymm0" : : "m" (*(char (*)[32])v)); +} + +static void __handle_shrunk_xstate_size(int sig, siginfo_t *si, void *ucp,= bool valid_size) +{ + ucontext_t *uc =3D ucp; + void *fp =3D uc->uc_mcontext.fpregs; + struct _fpx_sw_bytes *sw; + struct xsave_buffer *xbuf; + uint64_t xfeatures, *ymmh_p; + + if (!fp) { + sig_print("fpregs is NULL\n"); + return; + } + + sw =3D get_fpx_sw_bytes(fp); + if (sw->magic1 !=3D FP_XSTATE_MAGIC1) { + sig_print("magic1 is not valid\n"); + return; + } + + xbuf =3D (struct xsave_buffer *)fp; + + /* + * Both test cases shrink the frame to contain only AVX (FP + SSE + YMM). + * If valid_size is true, set xstate_size to match the enabled features. + * If valid_size is false, set xstate_size too small (SSE only), which + * the kernel must reject. + */ + if (valid_size) + sw->xstate_size =3D xstate_size_ymm; + else + sw->xstate_size =3D XSTATE_SSE_ONLY_SIZE; + + xfeatures =3D get_xstatebv(xbuf); + xfeatures &=3D XFEATURE_MASK_FPSSE | (1 << XFEATURE_YMM); + set_xstatebv(xbuf, xfeatures); + set_fpx_sw_bytes_features(fp, xfeatures); + + *(uint32_t *)(fp + sw->xstate_size) =3D FP_XSTATE_MAGIC2; + + if (valid_size) { + ymmh_p =3D (uint64_t *)(fp + ymm_offset); + ymmh_p[0] =3D TEST_YMMH_VAL; + ymmh_p[1] =3D TEST_YMMH_VAL + 1; + } + + /* clear everything after MAGIC2. */ + if (sw->xstate_size + FP_XSTATE_MAGIC2_SIZE < sw->extended_size) + memset(fp + sw->xstate_size + FP_XSTATE_MAGIC2_SIZE, 0, + sw->extended_size - sw->xstate_size - FP_XSTATE_MAGIC2_SIZE); +} + +static void handle_valid_shrunk_xstate_size(int sig, siginfo_t *si, void *= ucp) +{ + __handle_shrunk_xstate_size(sig, si, ucp, true); +} + +static void handle_invalid_shrunk_xstate_size(int sig, siginfo_t *si, void= *ucp) +{ + __handle_shrunk_xstate_size(sig, si, ucp, false); +} + +static void test_valid_shrunk_xstate_size(void) +{ + uint64_t v[4] =3D {0, 0, 0, 0}; + + sig_err_buf[0] =3D 0; + sethandler(SIGUSR1, handle_valid_shrunk_xstate_size, 0); + + v[0] =3D 0x1111111111111111ULL; + v[1] =3D 0x2222222222222222ULL; + v[2] =3D 0x3333333333333333ULL; + v[3] =3D 0x4444444444444444ULL; + write_ymm0(v); + + raw_raise(SIGUSR1); + v[0] =3D v[1] =3D v[2] =3D v[3] =3D 0; + read_ymm0(v); + + if (sig_err_buf[0]) + ksft_test_result_fail("%s\n", sig_err_buf); + else if (v[2] =3D=3D TEST_YMMH_VAL && v[3] =3D=3D (TEST_YMMH_VAL + 1)) + ksft_test_result_pass("YMM state restored correctly from shrunk frame\n"= ); + else + ksft_test_result_fail( + "Got upper bits: 0x%lx 0x%lx (expected %lx %lx)\n", + v[2], v[3], TEST_YMMH_VAL, TEST_YMMH_VAL + 1); + + clearhandler(SIGUSR1); +} + +static sigjmp_buf segv_jmpbuf; + +static void handle_segv(int sig, siginfo_t *si, void *ucp) +{ + siglongjmp(segv_jmpbuf, 1); +} + +static void test_invalid_shrunk_xstate_size(void) +{ + uint64_t v[4] =3D {0, 0, 0, 0}; + + sig_err_buf[0] =3D 0; + sethandler(SIGUSR1, handle_invalid_shrunk_xstate_size, 0); + sethandler(SIGSEGV, handle_segv, 0); + + if (sigsetjmp(segv_jmpbuf, 1) =3D=3D 0) { + v[0] =3D 0x1111111111111111ULL; + v[1] =3D 0x2222222222222222ULL; + v[2] =3D 0x3333333333333333ULL; + v[3] =3D 0x4444444444444444ULL; + write_ymm0(v); + + raw_raise(SIGUSR1); + sig_print("Inconsistent size was NOT rejected\n"); + } + + clearhandler(SIGUSR1); + clearhandler(SIGSEGV); + + if (sig_err_buf[0]) + ksft_test_result_fail("%s\n", sig_err_buf); + else + ksft_test_result_pass("Inconsistent size correctly rejected\n"); +} + +int main(void) +{ + ksft_print_header(); + ksft_set_plan(2); + + self_pid =3D getpid(); + + check_avx_support(); + + test_valid_shrunk_xstate_size(); + test_invalid_shrunk_xstate_size(); + + ksft_finished(); + return 0; +} diff --git a/tools/testing/selftests/x86/xstate.c b/tools/testing/selftests= /x86/xstate.c index 97fe4bd8bc77..0ab577157cd7 100644 --- a/tools/testing/selftests/x86/xstate.c +++ b/tools/testing/selftests/x86/xstate.c @@ -34,18 +34,6 @@ (1 << XFEATURE_XTILEDATA) | \ (1 << XFEATURE_APX)) =20 -static inline uint64_t xgetbv(uint32_t index) -{ - uint32_t eax, edx; - - asm volatile("xgetbv" : "=3Da" (eax), "=3Dd" (edx) : "c" (index)); - return eax + ((uint64_t)edx << 32); -} - -static inline uint64_t get_xstatebv(struct xsave_buffer *xbuf) -{ - return *(uint64_t *)(&xbuf->header); -} =20 static struct xstate_info xstate; =20 diff --git a/tools/testing/selftests/x86/xstate.h b/tools/testing/selftests= /x86/xstate.h index 6ee816e7625a..eedf0cab7ccb 100644 --- a/tools/testing/selftests/x86/xstate.h +++ b/tools/testing/selftests/x86/xstate.h @@ -3,6 +3,8 @@ #define __SELFTESTS_X86_XSTATE_H =20 #include +#include +#include =20 #include "kselftest.h" =20 @@ -94,6 +96,14 @@ static inline void xrstor(struct xsave_buffer *xbuf, uin= t64_t rfbm) : : "D" (xbuf), "a" (rfbm_lo), "d" (rfbm_hi)); } =20 +static inline uint64_t xgetbv(uint32_t index) +{ + uint32_t eax, edx; + + asm volatile("xgetbv" : "=3Da" (eax), "=3Dd" (edx) : "c" (index)); + return eax + ((uint64_t)edx << 32); +} + #define CPUID_LEAF_XSTATE 0xd #define CPUID_SUBLEAF_XSTATE_USER 0x0 =20 @@ -160,6 +170,11 @@ static inline void set_xstatebv(struct xsave_buffer *x= buf, uint64_t bv) *(uint64_t *)(&xbuf->header) =3D bv; } =20 +static inline uint64_t get_xstatebv(struct xsave_buffer *xbuf) +{ + return *(uint64_t *)(&xbuf->header); +} + /* See 'struct _fpx_sw_bytes' at sigcontext.h */ #define SW_BYTES_OFFSET 464 /* N.B. The struct's field name varies so read from the offset. */ @@ -175,6 +190,11 @@ static inline uint64_t get_fpx_sw_bytes_features(void = *buffer) return *(uint64_t *)(buffer + SW_BYTES_BV_OFFSET); } =20 +static inline void set_fpx_sw_bytes_features(void *buffer, uint64_t featur= es) +{ + *(uint64_t *)(buffer + SW_BYTES_BV_OFFSET) =3D features; +} + static inline void set_rand_data(struct xstate_info *xstate, struct xsave_= buffer *xbuf) { int *ptr =3D (int *)&xbuf->bytes[xstate->xbuf_offset]; --=20 2.55.0.979.g7e5102b832-goog