From nobody Fri Sep 25 04:07:26 2026 Received: from mail-oo1-f71.google.com (mail-oo1-f71.google.com [209.85.161.71]) (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 9DFDC4A99A3 for ; Wed, 16 Sep 2026 23:23:22 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.161.71 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789601004; cv=none; b=GpXGYFzWlaQkRwftEp7tSiTMnFktmrYHxdOAuVt1j4I7+Lm5G1uUJr3tFy8QThHvLjxMSQcDU3QeSPVEfJRNkUwJkmiji+RR22qii4HlUWpwbw5BjqOn0K4yLBurq5gO6TAIGhVU8IydIoC+1g6vzsxDW5b9lytGLTd/RJAll5Q= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789601004; c=relaxed/simple; bh=lmzy9PmENJFchGq9+VMyZLm7rZhb6PbTGTXhsAicIyE=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=rapP0tRPdBNuUXZRPMXkB6rbdIWv1Ud6x4jI7+qFt52qe3XQhXUBQ2YCY2Lcf2/PaX/J6GC2Dv1ITgqQ64O6EYFNTrn3D2j4zHrmiSJKHTBluD3VlQZtKpucF9tHtYwrnJfP2nQIThhaXC7xu0fZ6HiR9hKF71/+JyOj6QKgwas= 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=BaHJiCVf; arc=none smtp.client-ip=209.85.161.71 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="BaHJiCVf" Received: by mail-oo1-f71.google.com with SMTP id 006d021491bc7-6c7f1caa802so228979eaf.0 for ; Wed, 16 Sep 2026 16:23:22 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1789601001; x=1790205801; 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=MpDStlFMnwPFtqTx5sgyKul3FKBcXTV71LkraBHXvfg=; b=BaHJiCVfyKyFiJUXCxspXGocwp+rGPQSVs2z3LwRV2v/LnySYFurU/Xc83E0s9uraP mKk8OYp18auMSgpfXWbpZjCvO+XDrpB4S7dM326QYa0eZs3GPpXGoQoxSQ8OoM5F/wym u2USV3qVWoy7BkqLvSMqbA/IfG7S8D6d44Gx7/oYBRYZATHfSpaJ5qwahO319QtIp2A8 lTsxKcXuYdeGuP5W2pXK+g555pGAqxqnx0gn1L0WNI+sw9PP6LJjABSGYf+atN6WylbJ mL0frVpt/7DiW5Us7h0yymnWfOWvnO0P7kbIkj8uyqbITmuei/uvOCoRfI1x4DUraPY4 KG0Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1789601001; x=1790205801; 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=MpDStlFMnwPFtqTx5sgyKul3FKBcXTV71LkraBHXvfg=; b=wE5fshhsXcnuZtTlMGy67mg8kHg1fKamITcrVtEPeCugDcycNLihQ/tuuN8FEBLRD4 AC4sjyqq+B28o9SuqdwgYNeJFjzIFzcWCvIiF/C71i8UH71zw1BIXGkPDC/mgPYRny2o 7pBG72WEraNpIYuZXH8i4YCuA0jIWTabcyl9PpzuJk/FfIO42Es+K1QxbrvB/sa6rHtU rICnSGqN36r2xrbgSGkTJmzNUEQE2K3McXuqpOVGlVPMYrTfQaxdXUVq3ZsO4Gd4O7SI Xu5wNeZzYNIXuvv2UG+g49cPtXxo8V6axf1Jj6YNZynJlTP5vPSQJ3lw4B6I93OcOGHy l1YQ== X-Gm-Message-State: AFuF++mxTY/oTLrHYapfsSlKVWQuo7V5/24Hb3VUQ+WwpnDbmjqF+YLF 8tKKgKWHIHTRpq/iayYvVVI+DFUJyJduUbecpskGElRm46cOOrMbLV9eEc0QKYqwmfZ0sR8cCDh V6FhRRQ== X-Received: from oaccg22-n1.prod.google.com ([2002:a05:687c:2296:10b0:46a:ebe4:ef8]) (user=avagin job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6820:1529:b0:6c0:f6be:e524 with SMTP id 006d021491bc7-6c7d53f1ecbmr3839022eaf.69.1789601001293; Wed, 16 Sep 2026 16:23:21 -0700 (PDT) Date: Wed, 16 Sep 2026 23:23:04 +0000 In-Reply-To: <20260916232310.490786-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: <20260916232310.490786-1-avagin@google.com> X-Mailer: git-send-email 2.55.0.1082.g2b9226bbc0-goog Message-ID: <20260916232310.490786-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. Reviewed-by: Alexander Mikhalitsyn Signed-off-by: Andrei Vagin --- Documentation/arch/x86/xstate.rst | 54 ++++++++++++++++++++++++++ arch/x86/include/uapi/asm/sigcontext.h | 15 +++++++ 2 files changed, 69 insertions(+) diff --git a/Documentation/arch/x86/xstate.rst b/Documentation/arch/x86/xst= ate.rst index cec05ac464c1..e2944f744255 100644 --- a/Documentation/arch/x86/xstate.rst +++ b/Documentation/arch/x86/xstate.rst @@ -172,3 +172,57 @@ 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, but not vice-versa. + +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. In 32-bit signal frames, this also + includes the 112-byte legacy FPU state prefix of ``struct _fpstate_32``. +- ``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 ``struct _fpstate_32``, which contains the 112-byte lega= cy +FPU state followed by the 512-byte FXSR state (and any extended xstate). S= ince +there is no standalone UAPI structure defined for just the 112-byte legacy +state, 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..cff01406c0f4 100644 --- a/arch/x86/include/uapi/asm/sigcontext.h +++ b/arch/x86/include/uapi/asm/sigcontext.h @@ -34,6 +34,21 @@ * 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 xsta= te + * context to locate FP_XSTATE_MAGIC2. + * + * In 64-bit signal frames, the fpstate pointer points directly to the xst= ate + * context. In 32-bit signal frames (including 32-bit compat tasks on 64-b= it + * kernels), the fpstate pointer points to struct _fpstate_32, which conta= ins + * the 112-byte legacy FPU state followed by the 512-byte FXSR state (and = any + * extended xstate), 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.1082.g2b9226bbc0-goog From nobody Fri Sep 25 04:07:26 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 83B694DA9D0 for ; Wed, 16 Sep 2026 23:23:23 +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=1789601005; cv=none; b=r6rnLfJE9iwkTt48oCt+r9DkHKifSlqh1VPqmhLpCsdWX5VrwFxLjaiiIkaYXJa735nmH7np/hOYbyzkIzE7GTIOeLH55TQDLVRYRs27QhpS6pQ0HSEYz2dN8ha3YYEd7k5Q+ARQxjqQxAQO1X6CTZYTTSyyTOinMzQMv9jMhew= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789601005; c=relaxed/simple; bh=5s4/G7gA1Kk2yu/L+t0kvpSjVIP7qDQjB2AvDgBAc1s=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=sW7aslMiAQoOPKcZB//bk2/vmyhfixZ+rAyMGWL9twuWHUxMkmjajD/zFDJlDFPnwhPmHj0tcaix5eqZ8+fJPTsxAtTP3jXh3rKxTdCYwvwBp4oA72F2zsoPlYSwVepFRe7edhunqg6xKElN88J9QZNvfw0QPWiK4XwUZIKE4/M= 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=SJa7W/Jt; 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="SJa7W/Jt" Received: by mail-oo1-f72.google.com with SMTP id 006d021491bc7-6c277929a4aso206028eaf.2 for ; Wed, 16 Sep 2026 16:23:23 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1789601002; x=1790205802; 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=2YCI7vYSwkoRbqyFp0eaFKYCz1csTvqKlX2570ejDRg=; b=SJa7W/JtKIRvcr+E4vIL7/4RnKzoiztqt1Ag8cl72xNJwxqCptAEnE/CF4yRVO2LwI RT4ChWxNJKkz5DCCzLaXNu1cFksfY/p9jcKMPA80Go2q2THDDNcBcyd3/h03VPpbP9YB fbifBbS4wb8TwBrp+oAOySLBvfF/CPLl7/q7crn0H5LFWfuqq+h9XwFL8TkEMT+SdY/O PfgHlL0aRl3TbcMI76zOz3btkB6R2bIQ8SF3Ny/bxpX9ccv8ORKtuqjw0w4cahjaIiIF OuKH07Y2kynNK+zDOXXsPDk6Yysu7AYlR49gn2tvgTyJUh1WnfLwWClxFPSmdzcuuJXL gazg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1789601002; x=1790205802; 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=2YCI7vYSwkoRbqyFp0eaFKYCz1csTvqKlX2570ejDRg=; b=BKd+svXfVYIur+tnyHc/eghf7fzv6q9Phk+aP31LjGt37RoEfUD5qGbdk4fr+yYaDR xks3V6f+pjYAEFeVAmrDbJVoDhnkF5RORKM5wWw4WQ0J2YjaMqicuUK+iPb8CPsnXmvz D3vx2hNlYGNEviMjDrcO3fZtJO3qCXI1TBApDxcfgljOl5fNjZYUud79cWcrSti1QqQq rmXN5OfoupbAEGNB6eOzfKbAHWWWiQ0tAIjX3hSIpW4QT/qo7i27bAbVNP/yW1NLW82M s/QgGxjmKrrd7P6u8AbyBK+ofJSXG+AwTgOrsbOqDZ6UuDrU7Yrp7yC5aotAIHv2DY6Z vc6g== X-Gm-Message-State: AFuF++n3nPn+CskKzE6oNdibFgwqR2Wlw3ZbipRhfokWrVJowiduEA0t ruarOswUwpcYOIxk9zMG5pDMMHYqTmQotOwmqr8+qiY3LkK+zVONBRYgYJV8uvtQR9C5GjfOL5R NAPkKQg== X-Received: from iovy1.prod.google.com ([2002:a05:6602:481:b0:9c3:8ed1:397c]) (user=avagin job=prod-delivery.src-stubby-dispatcher) by 2002:a4a:ee0f:0:b0:6b7:8396:f3c5 with SMTP id 006d021491bc7-6c7d3702a19mr4344153eaf.39.1789601002154; Wed, 16 Sep 2026 16:23:22 -0700 (PDT) Date: Wed, 16 Sep 2026 23:23:05 +0000 In-Reply-To: <20260916232310.490786-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: <20260916232310.490786-1-avagin@google.com> X-Mailer: git-send-email 2.55.0.1082.g2b9226bbc0-goog Message-ID: <20260916232310.490786-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.1082.g2b9226bbc0-goog From nobody Fri Sep 25 04:07:26 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 B12E54E430C for ; Wed, 16 Sep 2026 23:23:24 +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=1789601006; cv=none; b=DnDW1tz2ysMtdZJukPfHhnCOT9B2pjtJyTNiIbpdAuCwaUJe8sP00VXTPbXMRKs/6JKv66+pjjZdF7Tbt/iNfsmsCbGOSQkC5XOiixF7A7TDk7g55/uQG4kUkVarKh6ZJE0ERpsNxZ4mQ0dF73SZurUcxtn/g9HqQEuW2UU5j0E= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789601006; c=relaxed/simple; bh=DYk1VYryoqgT8taACmdSymRTefB0VVOx3RJwmin9CBs=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=s9wJuZTI+ilKdSmOCINBeBCCaZSFSqziJLTVt2k8b9bW1+0SeFtvp6ZDBJMWtdsPzPul04lllpzU6tLKZUz+xlxUGYof/N2Tq5CVOdxYQIYHemxljOAmKSpn0a+UkQeo1PAYdBlvHrDusSZxntkk28kRE77xgSVt2wkmZWYkoio= 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=U4ruqLkd; 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="U4ruqLkd" Received: by mail-oo1-f72.google.com with SMTP id 006d021491bc7-6c1b7981b4eso155049eaf.3 for ; Wed, 16 Sep 2026 16:23:24 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1789601003; x=1790205803; 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=7SLsHGqtebxwp2xnD0z2ZKL+UE4NxRki3rz5pBthOQc=; b=U4ruqLkdAAiosAUzmlAs18M2oOfB4HZIIW5B9Dz3pV7a0ICtx0ekbrqXKbzmQt7fA+ suBm5/+811JSDnzxvAC+PzjpLi3IfA8CnV7uhftqk9Dibze3+InFDUyYtnDngmlJ4Y0R yZqJv59O0qLP/RNkWsrp4JkXpzEeYvYy/ZYwe3NXbkGJZG6I8vZM2Oz265VqULc8et1V /tEat6dXEa0F0GfpjcEb5+N9AMqzh+cifOTscMkh22RFved+HeypY7JTCniiFBUJksp+ DBPyTs1ljNkr0fDhkyZSWR0OszLXI+2y6K82H7106wLj7qEmW+zm8nDbMfgGHS8AfJ3Z hj8w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1789601003; x=1790205803; 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=7SLsHGqtebxwp2xnD0z2ZKL+UE4NxRki3rz5pBthOQc=; b=JcDlzdzvKwSeXZim4KG9uCPBRK6H6nqfl336NwFNkhU4cTHlVxOq+PXCn00uIavh1t oQs90El6fOVYrVrLiDIf94xit24hLpel/WyzLLVmSv81dZYdAvnogBbFk4rUVs9QV2hC bq5WbG64GFslZOUYpqjMnKe2MQdqps5beaCIBUY6rrDYoVKcCg/wWFkcszXZsBNEgAzw 0a3jeZX1/1Q9OIPeFwC51cNRBTP3S4JnAJiXKmyJJw9S1sXuxowGsPeksf7g7Rwrx8sd 4MjVzC1sJKPU+nnw6fM/tXw72tEnA8gEIupceoEEfuaWRdA0N/up9ZABFl4JQP3gq19P uDNw== X-Gm-Message-State: AFuF++k1Eio/SCvVzHRypmgSEG0I+vPOu6KPiialbD6mD65d+dC14Mx3 vxDCv5Z53UYUvIOwO8rD0maZ9vYc1m7OU9A2heaPcuN7SQ8kLMbPunardwzmJ8PbuY33tm2781p ZOtCxsA== X-Received: from iobb2.prod.google.com ([2002:a05:6602:2182:b0:9c0:e97:5e02]) (user=avagin job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6820:1f14:b0:6bb:4fd:e33e with SMTP id 006d021491bc7-6c7d5100120mr4456674eaf.68.1789601003065; Wed, 16 Sep 2026 16:23:23 -0700 (PDT) Date: Wed, 16 Sep 2026 23:23:06 +0000 In-Reply-To: <20260916232310.490786-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: <20260916232310.490786-1-avagin@google.com> X-Mailer: git-send-email 2.55.0.1082.g2b9226bbc0-goog Message-ID: <20260916232310.490786-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" 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(). Reviewed-by: Alexander Mikhalitsyn Reviewed-by: Chang S. Bae Signed-off-by: Andrei Vagin --- arch/x86/kernel/fpu/signal.c | 63 ++++++++++++++++++++++-------------- 1 file changed, 39 insertions(+), 24 deletions(-) diff --git a/arch/x86/kernel/fpu/signal.c b/arch/x86/kernel/fpu/signal.c index cd7db6dc819b..db069947bdb8 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,25 @@ 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; } =20 + if (ia32_fxstate) + success =3D restore_from_ia32_fxstate(buf, buf_fx, xrestore_mask, fx_onl= y); + else + 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.1082.g2b9226bbc0-goog From nobody Fri Sep 25 04:07:26 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 6D04B4E432B for ; Wed, 16 Sep 2026 23:23:25 +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=1789601007; cv=none; b=hJqrYrohQnnzBUu0AXKd/j0qL8Y9na+RgbAXdkllJWRSZtBizSM6XKtFvII8ZY3/4dkTlECNyLt3VjBKRbRIrsiCX34oUEJr3m6Q08xCc6Ih7VcroUT9GAYw4xP8g9sTYD1Unsc5am/UzRDQJSfl8US84kWiwTM6dgbsRVRDr3g= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789601007; c=relaxed/simple; bh=ebQ3kkBOnMbGpYQizGxh7N6zJHu9JIKJfF0XRRz/hPs=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=aX855rilJgckCVN2GN7jmT1k6dkI/xtQWF6gdXS4jqA+y4pL4wWPJQoYXZFAx5/f/JSPzdmUa9Ow/6qM8+F2mFMcib/wkRbhD5JPlvoVfYGObXy1usjLXIVOoCWpemizXmN0dE6mVae5r2BAmqnId6ny0LwpK5J+ynid5l4cfgs= 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=e1d0JUAn; 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="e1d0JUAn" Received: by mail-oi1-f198.google.com with SMTP id 5614622812f47-4c734afb8deso498639b6e.1 for ; Wed, 16 Sep 2026 16:23:25 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1789601004; x=1790205804; 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=xSSmIBW07WEV1TvZ4Ek3lr4k1UmQn4uFX87eg6ruy0w=; b=e1d0JUAnzqAXg5OEnFkvGEchvLvWF0Czhq3tEp0W6765w7mOGhrHzgfhqE2zp6EyU2 yDNIfNicu3ANOyaMbJ3NB7iIwLhqM3oWSVuf8l8qfvj3fD6YAsMEql4Ldj96rf91IPEp KFROt5XsM+oQ1Cni1Qa4R1108sOnOu22VZEpQdi84+9PGupfmt1G5hP95QfNFKvos/a9 GUI6pcldOC4/0W15cO0/lfO5iNnqoRcoLnLsRKesGxny5ZqN0Zak31tiXutyDvlEwGMN E1ubx79c/jq00H3cSXLbJ+xkFJxpnXHTn4J/u1+Zt4BJogR7KLsHAC/WfnHYdU/pTyrz boPg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1789601004; x=1790205804; 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=xSSmIBW07WEV1TvZ4Ek3lr4k1UmQn4uFX87eg6ruy0w=; b=THOr2itYYgG2K94B263KzsTitn9SlQEU3WSenwkzE9zV3tgxKpNWfeLh666Te5pgPr I6LiWwPC2EMB8LViCkgxZAIZLdtpkV0rXq4Hv3L+915PGNDlKYJer7TSnDoLHFEGr3ty Q9eETRddBJP4uycCf6gGh5SI7W0V7etrog36PiX+8+FL5iPd12Bk9KGe5ErXS+6icL6/ 8kcvU/fSSBGR10TMXxaP36ltf3oE1ik4GCytrl6GGJiyQROGBsbIEwk+4EOaUAELg+Hc leuybXyt9QaK98MeyGJKkAudBy6NjgneJ9S85eeDn4AYXVxFFeFiipJAsybI50Xv2VCL OVuw== X-Gm-Message-State: AFuF++lwLPFdFqjNCK6iM4BIcj9nrH0ZilL1BmlWqHQ7KsKE1AKS7uxE CwqupMIuEVOb/41uv6Qh2OSV1HBxAUxk0XRY5isJuamb8TezrOngSRPMjSIgfPTR5VNZbDi4U7Q 6YoWuIw== X-Received: from ilol10.prod.google.com ([2002:a92:d8ca:0:b0:509:9557:9554]) (user=avagin job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6808:c136:b0:4bb:ac05:9ebf with SMTP id 5614622812f47-4ca49ecc9b2mr4306460b6e.14.1789601003946; Wed, 16 Sep 2026 16:23:23 -0700 (PDT) Date: Wed, 16 Sep 2026 23:23:07 +0000 In-Reply-To: <20260916232310.490786-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: <20260916232310.490786-1-avagin@google.com> X-Mailer: git-send-email 2.55.0.1082.g2b9226bbc0-goog Message-ID: <20260916232310.490786-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 db069947bdb8..050e58691964 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.1082.g2b9226bbc0-goog From nobody Fri Sep 25 04:07:26 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 85BF54E56CA for ; Wed, 16 Sep 2026 23:23:26 +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=1789601008; cv=none; b=frCHo1yCbvjZJzV+qju4L32uJ9OZFeU1/wy12BOVstpQYUWFkrferw4AOi6IB1IvHh7hlvShhnyxlisieerXKcVt7fpvx4800itkjQpWLt9e+jKVi8dD+QdhcpYoCLBh67c7Q87CXLbJjMhy14+W4kUlpCG+KxZbbG6ImJeurEA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789601008; c=relaxed/simple; bh=FnVCxfJ6AJJ+Urfzm5Th794i/GZyUKsidJkmO59nFqs=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=Xi/bFEipYk+t5nYI3oYlz6IkbgQkCLXRG5sZ7qcomVL1lY+JDimLCQR1bnHCwlfPfszUP3qRMTreKT6CYT00RDRDuU3OrHGdU1OeHFI9e2rWhMBHnt8Jqt9tgIDkBHCejQ2NCrJgkfGPcGR/2lazqUIj3HKJfR9sSytIQmeawKk= 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=MOYuL7pN; 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="MOYuL7pN" Received: by mail-oi1-f198.google.com with SMTP id 5614622812f47-4cbc77da654so120731b6e.2 for ; Wed, 16 Sep 2026 16:23:26 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1789601005; x=1790205805; 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=yzKowJpenJDD2vnwNvQsJP6Hsd/QTiS0cw1NoTxC6s8=; b=MOYuL7pN6s6fyGmQrx2WmUk5U5CLY5+8+wsUqrmNW3PCs/JlEHvjmyOOmH/aW9BnOT 2FB9D4YNElfcvYr90jZBmpgmlx+E/+SVt4j2JWxjWRQctttRPcGFADx26rRl+lRQL1oC A2BmcgeTk/dH/L3SSKhBcsOBI90IHSiadtyoFkY3oVN7nP3tO/F8tcjr8jM7Afl+O0pI Nhws8pFuZv6RqJO88SEmyAk84mgRCogdKGM/+OEBRWLgNUDmm72BOZoekLr08UWMC4RZ Sx9sQe23So4CS0oOs5UyNqKZNGOzAN1bMh6zlawk2RfLDCxvCkjJrKdF9mdxMJSCkCCN ZWYg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1789601005; x=1790205805; 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=yzKowJpenJDD2vnwNvQsJP6Hsd/QTiS0cw1NoTxC6s8=; b=BigWpQ6MTU2L/rxN/i6AJ5z5IrHSlHEorz8fN8G5ufIihM3nuRIxuhrMJBzOAAu00E QuxrBzwaiN4w7sFdPMpkypC5b3+IWH/tAIRUJAox0WI9AQGr1lc5FC3Hl7Yb04sqWVpC +DryK02kzTu1R3JAR+9l1nvd5pmrjzkkYAzeNBgOK60i4pIOYmr2O5ptOBBZ6XA02bxu vP2CebnTHez/vFFSZI9E2PtiiDTuvNt5KrX/YPEpHi+rCIltdUH+Cfs7/4GizMWDwhUL NLLYKL1QlISgi1Dtkhc3pOT59oe2R0L21W2FffYfsfZnRSK9hRSDrc6MMvacNqeLOIK0 acYw== X-Gm-Message-State: AFuF++lchYQ/T/5DpD0GjfTQZHH9orh4XgQ9CuO2N4gG2iQ/lybQP4uu 2rKeVeLpsWec9rbsh3He5dqRDPwz780Aa+DQXEQo1nfY0CThJrxVPH3B4mdN+rPPKAd7BdXhykk tyQ7Ujg== X-Received: from iocrb2-n2.prod.google.com ([2002:a05:6602:a582:20b0:9c3:e4c4:13e1]) (user=avagin job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6808:4fca:b0:4b9:a829:f00e with SMTP id 5614622812f47-4ca4bc74200mr5323830b6e.26.1789601004909; Wed, 16 Sep 2026 16:23:24 -0700 (PDT) Date: Wed, 16 Sep 2026 23:23:08 +0000 In-Reply-To: <20260916232310.490786-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: <20260916232310.490786-1-avagin@google.com> X-Mailer: git-send-email 2.55.0.1082.g2b9226bbc0-goog Message-ID: <20260916232310.490786-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 3e7f5fb5bfaf..362df13a88cf 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 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.1082.g2b9226bbc0-goog From nobody Fri Sep 25 04:07:26 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 220814CA789 for ; Wed, 16 Sep 2026 23:23:27 +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=1789601009; cv=none; b=qxBUpfI9369QRDvOlfY9Gn3KONMkXX4TpQkGUmTnT6jS8yru+tPT7Ib1ReFJLbmLa3A9jRd3F6BCPW+VYOKuCurgQ4krP4QIF6ayNS8rX7eDaOL0HccNmnIwXtHTu8s2kw77awOjD4O0seyGRiEia6oAJdnWIdmFlw9AM9niwlc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789601009; c=relaxed/simple; bh=tOsL8yVQjJBfGBSYp5apPW7reiq1zsapq097WRfiYqU=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=j7vLC/IcsuCRCwL/ktZWMgLvidk8fmqKWuMY3o2LZzjCgrHBsQWXSdFUzyXBhifjLtheleYQawLniEMabwly7b1KrkurEV+yrwQ6fPFOhI6b6YNrnM00vlZiT4ysX7867u3M8BsKsMWA/w2Pm2n1fU3fNZAWPt0cbHMAOjbF+CI= 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=t1oVdHAl; 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="t1oVdHAl" Received: by mail-oi1-f198.google.com with SMTP id 5614622812f47-4b28db6c77aso402290b6e.3 for ; Wed, 16 Sep 2026 16:23:27 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1789601006; x=1790205806; 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=60qM00PG0+A9imsiTqm+yezTKZHIiFhyf2ORUWD9cE4=; b=t1oVdHAlkrsDVPGK7KLFFxNjsZEPHc2/Q4f8H6rmPR4WqyK2Qkzo1eLZcwyEKSyGAp SGALemAFn/lMgGeKaO82DiuyDs1Ter9ko6erWFZOUAIFb391trhajE2Fcc79eYmBtvaC y2CuYB0T9ILhhPJtv/p/I53uSLAX++HuV1a11Wds6VqqLBerWa6OK/DtVwkZjlA5C/Q2 9PnB64jzwOVJM3Yk+v4VamRCUdptTmMvlZm6s99+uV9KWM/fxnNUHVFkm3snlqGazYf0 xnADxoosBuyjW0RyvGZ4RhCu5Bm7HkDRLAP5lWVsDKEvX/Oj8pZJjZ1MA29oDetQ43tD Fmgw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1789601006; x=1790205806; 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=60qM00PG0+A9imsiTqm+yezTKZHIiFhyf2ORUWD9cE4=; b=sihDo86CrVDKTPknZr/T5tMH4CmLmCNBzLFaPmquqIu8d3miS6GMt9x2PO9RUdStvg TQiBg3qjTg0Xq8y9vsrlrFrcoVwjPiN6eqJnvrdVxyi8a1TNSPfBGnZSyInvYhmTyD1y UpGFQZVo+p5TRNO4fVoa33mPiwXcjo8XaqfHdoxDPcvN9D+7iHxDDlhL8DjGBxF8L0vY AOD74g2J6xXC7YipTRGeXU0brvhieekY3QU4bu1NLsFJACiFd+NgXCkchObAMFuLLkSS wyTYv9FCCZvCbtucV2TGq7qEUrOGg0m9d98KGpup//Ad8yYEGdLovuSeG+J0aMNuwpyL iQyw== X-Gm-Message-State: AFuF++l9eRjNBKHRHDuKcZcNMt1Bx/VSw+aaMOANlwJk2S9+6KCAquGe 1zOUMWoe54Zdl4XJ6/QAeje4guRmvwLVGIG3ShiDXuayC593p5xkY++5Z2eu7p296X4X/4bRglG PXGHL7g== X-Received: from ilbbm8.prod.google.com ([2002:a05:6e02:3308:b0:50a:f72b:548d]) (user=avagin job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6820:4c86:b0:6c2:4204:6052 with SMTP id 006d021491bc7-6c7d27e0ec3mr4526532eaf.35.1789601005886; Wed, 16 Sep 2026 16:23:25 -0700 (PDT) Date: Wed, 16 Sep 2026 23:23:09 +0000 In-Reply-To: <20260916232310.490786-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: <20260916232310.490786-1-avagin@google.com> X-Mailer: git-send-email 2.55.0.1082.g2b9226bbc0-goog Message-ID: <20260916232310.490786-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.h | 2 ++ 2 files changed, 31 insertions(+), 9 deletions(-) diff --git a/arch/x86/kernel/fpu/signal.c b/arch/x86/kernel/fpu/signal.c index 050e58691964..1c4d83f3c44b 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,14 +519,16 @@ bool fpu__restore_sig(void __user *buf, int ia32_fram= e) =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) success =3D restore_from_ia32_fxstate(buf, buf_fx, xrestore_mask, fx_onl= y); else - success =3D restore_fpregs_from_user(buf_fx, xrestore_mask, fx_only); + success =3D restore_fpregs_from_user(buf_fx, xrestore_mask, fx_only, siz= e); out: if (unlikely(!success)) fpu__clear_user_states(fpu); 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.1082.g2b9226bbc0-goog From nobody Fri Sep 25 04:07:26 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 7DFA14E1C8D for ; Wed, 16 Sep 2026 23:23:28 +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=1789601010; cv=none; b=bUGgtesWZh3FSaPw5zPFKjcoz/pfgNjgDZVNPG0JY+8zIJjuW4r/dVApChYs/HGmHt2zM61q0Yknw1n9KXWELbuJ3Zf2Nc8E8cnCnMdRmm4ocqlwvGrzvoSGsvuonPOBCB7UsuC//AzjXFWOeR4pRYD0ccIqkByUs8jRUUEWjME= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789601010; c=relaxed/simple; bh=VSDinFDuW0eGkOtmmVpaEPcgXQAROORcx5l5xBaUTJs=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=oYnwVOSKwZXp90HXJQBOSYoSAkNF68cfa2h553/4WsiMpneNPfqCTmgE0NVuoYllmGCHiNlxr8vJtQ4kc8M9TV68yUuypZCF3APN0KaP7QQMGwIllRWpLifvdCrR2IaWzU2HkbRAtEqUMQyZV5w+7oJV59r19XY3ymnvK7KkMM0= 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=ubY1ZMJc; 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="ubY1ZMJc" Received: by mail-oi1-f199.google.com with SMTP id 5614622812f47-4b38ea4c6acso277589b6e.1 for ; Wed, 16 Sep 2026 16:23:28 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1789601007; x=1790205807; 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=Kiv1h3ez+21DDmFIeKd0/A5hdj5Vl12D1mvYw4I116Q=; b=ubY1ZMJcDtBJ6+V0Q5cc0BQIDGBCvWH/GkfDmJuMKRUU418nxoentdPt7snkxLU1bl UFGIFj5RdQvBsxbQ/vWD4Le4rwQ/Ab3XzB6hje0gWMhzW9IWnZxeCL2VELZ1V7jWBj7M i/JSP8q7fyX2ls4NMnB1wA6sAjwNhLgr6OVHcr6Ngb/opFaLjNrllPpuJtPkfGcXVIlF mkM+NFwTjGNmRpWElygh+wuRm+Rh0xHkdRSp+0sjs9DxuXQowNRcQDSgFSl2+0WReR66 NCiQCVcVsjfZ4/gwx/g3OEIjaDFODZiijKKBGc99G8DuSV4FNwgNJKuppkC36XIFnsxx xRdA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1789601007; x=1790205807; 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=Kiv1h3ez+21DDmFIeKd0/A5hdj5Vl12D1mvYw4I116Q=; b=BPM44Iuk5xtfoGzmTlkOOrIJr1gc+rBUn0qU6iQU5Y6ZsPk5sVG69uN62U0YKZAL1d ZQTu/TkM1L62yIfAwX8LMRLA93dfWGmCCF4uYxb+p/f5Ska0xDQMnVHIMRJ+uzlvNTGt JCk5uQpfqoCxpABQseh9WW35wP/vuGmWFZASSKEFyI8f2/1z690e0uPSizirJArPz8RW chNlpogyoMBrSrxYncofqQAaiFz/LGNOIK+LefJh9RQzUkdmibDS1zZrQ5wGSqEtdS2i IKubxUTqkbxuQTBFQ1IJlHZ6ns4Y8sj73RKZWZNw0HOhEgMYBZ7Pc/D+kIP0g7cb/3Cc Kw4g== X-Gm-Message-State: AFuF++nJ4JzrckX7k4cyKIi+rbEu0wRo3J98Apnui5ZfYnT0eEWAadEg deIRVZYsxWF0c6/wwQNICtVY4nNu5eGaGusUnyCq2IqJVixk502Gc9XQwcGH2ycke/6QIApznp2 lpiBGKw== X-Received: from ilow14.prod.google.com ([2002:a92:c88e:0:b0:509:30f8:6591]) (user=avagin job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6808:4f67:b0:4c3:e93e:e331 with SMTP id 5614622812f47-4ca4c49ad67mr5407174b6e.30.1789601006782; Wed, 16 Sep 2026 16:23:26 -0700 (PDT) Date: Wed, 16 Sep 2026 23:23:10 +0000 In-Reply-To: <20260916232310.490786-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: <20260916232310.490786-1-avagin@google.com> X-Mailer: git-send-email 2.55.0.1082.g2b9226bbc0-goog Message-ID: <20260916232310.490786-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 Reviewed-by: Chang S. Bae Signed-off-by: Andrei Vagin --- tools/testing/selftests/x86/Makefile | 5 +- .../selftests/x86/sigframe_fpu_portability.c | 245 ++++++++++++++++++ tools/testing/selftests/x86/xstate.c | 12 - tools/testing/selftests/x86/xstate.h | 20 ++ 4 files changed, 269 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..ec14f3c30093 --- /dev/null +++ b/tools/testing/selftests/x86/sigframe_fpu_portability.c @@ -0,0 +1,245 @@ +// 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.1082.g2b9226bbc0-goog