From nobody Thu Sep 24 13:39:00 2026 Received: from mail-oi1-f197.google.com (mail-oi1-f197.google.com [209.85.167.197]) (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 6525D381E92 for ; Thu, 24 Sep 2026 04:16:08 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.167.197 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790223370; cv=none; b=eP7DxOuyUbhKQgcsPEV4jyqAEvGguU5E/CJJEpa0/ZhWqs0Wl0UMsMsPAI58EjymAw2XKxA7ePdIUSx630x+Y65kVRic6+kibwp8zRqO2SdV72dFAJdrvtDn47YaPReN13q5gZWVA5bwn60Hg4l4yUW+yYioYQ7cH92TFgTuZU4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790223370; c=relaxed/simple; bh=DF9ttw9gsIyZmdVs7+pxevIxFIZSuyIkqbVJ2evMJ0w=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=jSxjxKsPp5uWfzx7KyTjCCwbjZ5iJXgOBUVAxjsNzu4WPt3hDTBd2UbUL0AcoHVH77RepBHsLG2Fqu2VQw+UxXPQ2q1JEWcMY0F8dloI7avx7SRMPWANchaHQT3k0X53THKxLJ/QsUEezBK9czfIiEBv3sGLNYiFtW0FOHZBcgE= 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=klJkymFB; arc=none smtp.client-ip=209.85.167.197 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="klJkymFB" Received: by mail-oi1-f197.google.com with SMTP id 5614622812f47-4b2f2b8d88fso2669483b6e.2 for ; Wed, 23 Sep 2026 21:16:08 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1790223367; x=1790828167; 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=wEI/P7TyGSCt3KjWgkyHKt9XAW456tf2RY2L8btU5B0=; b=klJkymFBrW/BWYMfhIOX2TEopHynPnFJYOvG9JD4SJdkLyxD1T4JV0KvMz/mxSgAK1 eUGnnmh7DHzRXp+tPyWUHIW4XNejLVenv6hGve7Lz9pRoZUilzU+GgkC9gp6VRZhnMfE 6GjCH8SrqCkWOPe836h5g3NpFwxuzU4zXsyCXVHfhKW6iltoVVFQBGt1dIgyYSC1l/Dh VZqiYFL563XZ4OKfnNUvRep9/yP7vcapjzgGPYd7YCvbMA9i1Z482Mwlpr7gqGzNOKs5 oSxmc61Qs7DrU0IK/scYPK+vL87+/AJn0Hy3GQfwhsqvC72RUupLQ6y4iSzPY+EEAciv s2qA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1790223367; x=1790828167; 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=wEI/P7TyGSCt3KjWgkyHKt9XAW456tf2RY2L8btU5B0=; b=xywtcCSqLahDGm5d6hv5kNq6qOadloubNxiaEfOVS8qtxvJSlflhE/+0rbQHZhhiL8 jUCgQDbHPu7wD6P47mHg0ZTPOrxju6MLsSJdf2VBDAaeoH4iUTNk5s8tdG+Wj6jLheXE iarrqxbZWUmGDEW+Ub1ITLdcjYd1ywbLJhFIx+XciwnaUZS+TFDP59BQb6eVSVHg0Pqv NzCf0WCIQg9hcwSdOWfBYibpCPW2dkFSpcBIE4L3WwhQ3YqAAWYhZhCxRnBcTUywZI11 ydJFV1eWxmQ4Y0z3fSddpp4TXWIwm7yYku+zLr30KkUZ2mDDBuBEbfhA+0F0yqjI+wwo znbQ== X-Gm-Message-State: AFuF++lRKa3WTA6p262aZFUhjWWG383KfSq+RSmkI+TuNmQWd/5pp22U 3hiY5lxtICnSvXF6nIAa9xGES0oVgxmUM7ZoBkaOZfT3NdzcMF6vlqR0jF3K6suc1G6X0h726hM TTa2GTg== X-Received: from ilef15-n2.prod.google.com ([2002:a05:6e02:618f:20b0:503:668b:b863]) (user=avagin job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6808:1793:b0:4cb:f21c:a796 with SMTP id 5614622812f47-4d72982ef61mr1415785b6e.30.1790223366735; Wed, 23 Sep 2026 21:16:06 -0700 (PDT) Date: Thu, 24 Sep 2026 04:15:57 +0000 In-Reply-To: <20260924041604.3209659-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: <20260924041604.3209659-1-avagin@google.com> X-Mailer: git-send-email 2.56.0.rc1.310.g51773c2048-goog Message-ID: <20260924041604.3209659-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.56.0.rc1.310.g51773c2048-goog From nobody Thu Sep 24 13:39:00 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 5520037E304 for ; Thu, 24 Sep 2026 04:16:09 +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=1790223371; cv=none; b=KE2VhlGVjMxrazg9Ijy1V9p5+wTfIX6H+n3JVPTLKFbjSRFIJQilPDtVf3dDTNC3q8qsga9002mVeTiFWID1jR64YeP+OGRR0Af9kywLnFSWZ5nWzXngwD/2acEn0chPvwa7UqlKqCh3cG7nS1wj8H9Ud+/NOF8gTTREhzacrPM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790223371; c=relaxed/simple; bh=aImIUW6y9yKf8Bc7UWxcZcK4+SQfkQEiUJbPLmaDGGk=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=umhvZ9GJZBqPwJ1IKGggTF9GDNHm+dsq99I/ZhvUfWnN10hgwMweeQ+d0ZBlRlpcA5W40ibD0Ltc62WHx6jHbV+y/is1RlocgZzjI2VdwdVg6N0oLMhm9rtmkXRP3F920wUJrUtv/09y5ZzNCtVMN5abetTtwjH9cnO1vcIL2gs= 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=H5ql08NK; 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="H5ql08NK" Received: by mail-oi1-f198.google.com with SMTP id 5614622812f47-4cbc77da654so2416223b6e.2 for ; Wed, 23 Sep 2026 21:16:09 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1790223368; x=1790828168; 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=AjuLd/aZUXW8lBEMkItWaOZzC7szGVpoIVkdc9ox1Io=; b=H5ql08NKByP5F6Pq98DLNwgOiA+At5o8Ku/HGNsD2bibKIHYglXwWG2Eq36Bj5x7lS 9HluJpBT2uy4yPgP4cHBWTvs7iXJ7aWNMnHTCnIfv4x78ACHSTlzG51K/O/jywB/OIOU KemV95Va24XpKipezRdcq2VHjKxSOIpPF6VWH44ZgzM60XMPB7iefiUlPfjYJ61P5JIU 543tMw8KS43DEsKzUGeRaTLkh+M5DXok0KAB95cqmEhc7jxv2awVUAtwvN8Wfw0QTqZw lAs5lmyh5rntNs9KCMzrq3DSzOqhGslAju698xaVu2OoVDBny6ntzCwkPLrmfHM6Y5+f 1CkQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1790223368; x=1790828168; 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=AjuLd/aZUXW8lBEMkItWaOZzC7szGVpoIVkdc9ox1Io=; b=ZI8siPODu/viStEcmFyDe8dZTDp2KYxBdECis/qykwdKexqC9zm7UJru06uxHNmKzt MRKkP21jnQU/TMHbj0ax5kEV4lNVnlaRxO1qX0or0bWGTW2rZq6vs+AWLPzJMjw4ryqG C0ifzOWLAhuaZrImTn4nzPKZcSQzpO71IfQGfukclod3uXeTaQJ5OaMBExAOmTsOlkOq o13YHdQAlBQlsbP16qfSUI4HTLm0FZChqmYF5CFaYD3PwcskM5QkJ6wWKUpeYg0Qj426 z9PsK6bTrjBZmnGaolKt4SEpefogE4w5DCkf4Lmbyk8nz78GfXT31Y2BCXsYjDcVgdvq n+wA== X-Gm-Message-State: AFuF++ncvdkLCCKXEB+sMB0HtuDR6/L2+6bjlTQ5Wt+8TB4T1Y8rDPpb o784ntQpuE9VQQu14hhHA86T+8RFGwUbVok3VELRc+QIO4FG5vbpd+HK/rtWWBweIFSN7Dtbz4f VcPImAw== X-Received: from jabfq3.prod.google.com ([2002:a05:6638:6503:b0:5f3:4cc3:520e]) (user=avagin job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6808:4494:b0:4c3:440f:d7e with SMTP id 5614622812f47-4d72c042e68mr1355578b6e.13.1790223367759; Wed, 23 Sep 2026 21:16:07 -0700 (PDT) Date: Thu, 24 Sep 2026 04:15:58 +0000 In-Reply-To: <20260924041604.3209659-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: <20260924041604.3209659-1-avagin@google.com> X-Mailer: git-send-email 2.56.0.rc1.310.g51773c2048-goog Message-ID: <20260924041604.3209659-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.56.0.rc1.310.g51773c2048-goog From nobody Thu Sep 24 13:39:00 2026 Received: from mail-ot1-f72.google.com (mail-ot1-f72.google.com [209.85.210.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 D5E5F309EE9 for ; Thu, 24 Sep 2026 04:16:10 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.210.72 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790223373; cv=none; b=aoxZIGTcdBoSkHAntjtjoaq2s0tmvFVry5HZvpELB+nUBV3703XwOPa8xavNP/MUBmbpghj02aPqzgVoEE5k9k1hlUA9NTYiUnkOkESwUssnMnuPq+dS6cf/qynq9MpL36ePJ50CjLO4LPWw4wbz8LM5F8n95wvtmj9Yszpi45M= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790223373; c=relaxed/simple; bh=zpTsHsdhvX0iPTlbayTEc1FmShanIfURQhpU//AP8tI=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=gy7eQCGb5/C9qT5uSeQYnZh4iSVH3/8EEuL0ev4+DxvzVqwVVk6j3JjNMiWl3Icm14TmCt0fbsikbx08QaLK8J9B144ydy2F4d/uY7oRvzvA1lsZi7mZbYdDRg7EcqAom8QO1izvjYGShMhMRPTc0ehmkAFqPtz2UosVeMMsyls= 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=glsg9Y19; arc=none smtp.client-ip=209.85.210.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="glsg9Y19" Received: by mail-ot1-f72.google.com with SMTP id 46e09a7af769-7f4e8568ccdso2955844a34.3 for ; Wed, 23 Sep 2026 21:16:10 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1790223369; x=1790828169; 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=xcw9Mgm3kBNox5yRvORKHD3/cac/jIReOJZK/oYGkSs=; b=glsg9Y195zjeGCb8DMlENcEhBDvZPWwjh3d4V2Vr5Ts4s/87yFFw92UGTYHkCJc4GO pCZVJCHiP0Kd1rM4Jj9JzJtmpf363NZsUM3LKxl64PLNplnL7HFDkRLfHpOpPO241rLU CsxAA5OxoDEykw1Oigvzyb6jn8Pq+68Qqu5SAXnZVCFF4gLLll+V0uITwwnXRFAeku8n dcJkOmHfzXzi1ugHpj5CyQyqkvT+AdT13Ewf6pKeedtprEti2L0WeW30QN7k39sPqfSR GdRKIi5qh3+/KTxUaXBB2lExezaJ7ZKBo1VfbU1OEx4jgSFG82J7C8s5dQa4GmuSofaV 57LA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1790223369; x=1790828169; 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=xcw9Mgm3kBNox5yRvORKHD3/cac/jIReOJZK/oYGkSs=; b=0Ax7+hvS0icQxUU4hNTLbxlGfml7tWQJwZxMmmCDVx3bmExVVIkSgDwvbqVESDklqo 5p2jcQSQ3us+sOEipUM73QtVcilCbv2O6sQhIB11Omx4Pqbz9vity/9qZiKnnUhZfLte IBxyf3DSmeBvmnNVPH9FRTXa4Xcx07HWs1XTZrHAJBQCItDr34RpcOw/no7NHENZcGkr YokKhdi0zzapBMkCeIRX+ULY+Iiuv9JGPw9/g06stTWEhSjh8MvOiM3Aq+S4TM3oUExv 5LoZFaR01bcGamGQybS6JbI/uvPwygdUEhozdM9X6VXzDryQtraHJT0ikmM8EqVEYBDy 6AHw== X-Gm-Message-State: AFuF++nOHG9avQEyxIRWYsqP5ExGN6kxbhG3b7+9GLc4k6lMMgZGsN7R 5yYc9c2I+Zzd0/45zn8CE0bUZw0AHvIAVxjbkSdYs86X22nC6nTjqt1DmZke/QcFee0Qdx8mera do1UPag== X-Received: from ioxv7.prod.google.com ([2002:a05:6602:587:b0:9c3:8639:712f]) (user=avagin job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6808:238e:b0:4d6:9153:2965 with SMTP id 5614622812f47-4d72b63ec49mr1377005b6e.54.1790223368785; Wed, 23 Sep 2026 21:16:08 -0700 (PDT) Date: Thu, 24 Sep 2026 04:15:59 +0000 In-Reply-To: <20260924041604.3209659-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: <20260924041604.3209659-1-avagin@google.com> X-Mailer: git-send-email 2.56.0.rc1.310.g51773c2048-goog Message-ID: <20260924041604.3209659-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 | 56 +++++++++++++++++++++--------------- 1 file changed, 33 insertions(+), 23 deletions(-) diff --git a/arch/x86/kernel/fpu/signal.c b/arch/x86/kernel/fpu/signal.c index cd7db6dc819b..b9dcd0cd0e41 100644 --- a/arch/x86/kernel/fpu/signal.c +++ b/arch/x86/kernel/fpu/signal.c @@ -325,32 +325,26 @@ 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) +/* + * 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; - } + bool success; =20 - if (likely(!ia32_fxstate)) { - /* Restore the FPU registers directly from user memory. */ - return restore_fpregs_from_user(buf_fx, xrestore_mask, fx_only); - } + if (!IS_ENABLED(CONFIG_X86_32) && !IS_ENABLED(CONFIG_IA32_EMULATION)) + return false; =20 /* * Copy the legacy state because the FP portion of the FX frame has @@ -450,10 +444,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 +477,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.56.0.rc1.310.g51773c2048-goog From nobody Thu Sep 24 13:39:00 2026 Received: from mail-ot1-f72.google.com (mail-ot1-f72.google.com [209.85.210.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 361A7404BE9 for ; Thu, 24 Sep 2026 04:16:11 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.210.72 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790223375; cv=none; b=gBst3mPp20ooRXdBRmNbsvrkNHiyIeWiLty7pbYd65HGzEOKNB7EgyHXhATXmPL8wprKMVoFDVa9IdyzWotSz7RMgyZcxe7KQFXBLVw9MmiuyOjbRH1zU35PTmyKGSVe7tM0Snx2NaxEYx+D/EuIXTnOTJt8QKa/qHgjxhBFHvM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790223375; c=relaxed/simple; bh=4mNQWbChJAgFDbg2bTs/RQS9DczFtsrEoJsv+d9jZf8=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=H6vqtCP/5/W1xq/mMk9cfVYVQEiefHVFZUEKiOKN4SUnMXSPgDEM4XO9Slg+/BxWsDkkSMr5vqFUd7zER+az/xxApBWX65FxQWHZtsgknbBjjaAh5M5ofFn/qowuiefqN2m6+rR6/3kATwzn8b8gelHXVGgw5AQu7QXMUoIXaDY= 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=n1GB0Nav; arc=none smtp.client-ip=209.85.210.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="n1GB0Nav" Received: by mail-ot1-f72.google.com with SMTP id 46e09a7af769-7f4e8568ccdso2955854a34.3 for ; Wed, 23 Sep 2026 21:16:11 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1790223370; x=1790828170; 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=pLR8sISmsxHCx8KzZxnkzJcWho2PP8br9Rps/xb8c2M=; b=n1GB0NavtU9Cuq3LRd0Ah8vP3pwhbBuyQpVBAd/8XdcxlRNHL0iflVzGbiFcL1oT0m XYGyQIiWaE+0tG4CzhXq3uU/DTs/TlykTSPp4LVvnDcsIGHXaN1hAOIbxR3trAsmgbJ3 72cYGp6+FNTKsQ3QR0X1AAwcyyxOtB0y7TVo8ucanY93JXeLDjJjwPZkIQ0ac/zsYT6V 9B8FMN0cYJH3sUFqFnOC9nQ6Y5psXx6LROgSuQ2Wyw/INwGU77zoAKxbdR34g2i9XHLZ HlCYIVCB8pPP7E3WmiPSM97ZA3guMpcwA0kgvbDdI0MZ/IcY5PdLJBcOBV9VyQ5RH1aB Rn5Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1790223370; x=1790828170; 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=pLR8sISmsxHCx8KzZxnkzJcWho2PP8br9Rps/xb8c2M=; b=VARs2PRHrfcSmy7MLZeCcHLAosa49RxURqSmBFn5dbxEydsiZRUtdtnKYAMj6TjmnR BhjDRN9lo3F6fRlYqzmNhbfsa5pORlB+U5TEn11HxGVagMH6Lvp3Bo90VKz2DHjPJGg3 3d/8qZ8zsG45jW+wMQaVh2c+ug1I+m/wG/6Jy/l5NBELZ4SeMevj01NsWiC4aFJRTX4n oeyyyej7fN6Sy6aol+JVScUm7CWr8d32LCmGlj16WVIOmWm0Bh7l3riF0XfVKZOrpsYi ysT4i1aF4WzGbmNLLxrhzp293jvtDDGZwsmxt0ZkecI17QsNpWRBu+IhXO3Y/kmgzhOS IFLg== X-Gm-Message-State: AFuF++l+pwj++RvqINw9UFu4f/LZRvBHPMgiecH74tsz5I6fs3O9b+vN xnY+o7+dTOCqgalqyP2GFgCwW0Ht1g8zgKDVtfEjX09BuFG/NQZD9IL/hexO2B7kF2LiFahu2bS UipBxHg== X-Received: from jabfl2.prod.google.com ([2002:a05:6638:63c2:b0:5f4:158f:39f2]) (user=avagin job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6808:16a1:b0:4d6:85d4:8499 with SMTP id 5614622812f47-4d728922d24mr1384307b6e.7.1790223369945; Wed, 23 Sep 2026 21:16:09 -0700 (PDT) Date: Thu, 24 Sep 2026 04:16:00 +0000 In-Reply-To: <20260924041604.3209659-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: <20260924041604.3209659-1-avagin@google.com> X-Mailer: git-send-email 2.56.0.rc1.310.g51773c2048-goog Message-ID: <20260924041604.3209659-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 b9dcd0cd0e41..d56819fe491e 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.56.0.rc1.310.g51773c2048-goog From nobody Thu Sep 24 13:39:00 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 43303403E97 for ; Thu, 24 Sep 2026 04:16:14 +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=1790223378; cv=none; b=iVn835doUHGKOVsFXg8a4S4VDOyR1TsR2vFXhMPI+dnSoeJ1FDIkqf01nyg0YaoogNOjbVat9ktYTGXAoO7klw7a1zJeuRF0GhNeq8U7ZGn3rhb76yaEuQ2ddoz9dnUxBr9y6nQOLSuLF5JpzGArkhnL1j3DCi756tnD4kfbZMc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790223378; c=relaxed/simple; bh=GW++IQkN3swJonF+Ux3M4S61EH1JXfeg3xTVbZ1j010=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=ph+33zhTvyVj1kGMdhU1wiATtALW9+MBb0B1lE2LgPKB8u8ECmFbIIdqrY47AjR5tAIHFUuqqq8uCebMn7A88GUoic4B+exERZvrwJRobbIkDymP6dsMYzDPHFRluk9Whn2dJ+JoXkvOBBtOKHonHUDB8oWlJkUfLrCtL+JIrUc= 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=NN9EqCVL; 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="NN9EqCVL" Received: by mail-oi1-f199.google.com with SMTP id 5614622812f47-4c307941971so2581356b6e.1 for ; Wed, 23 Sep 2026 21:16:13 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1790223371; x=1790828171; 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=FLDrAGg7xWWuICM8+UcK0lvwffhpOIQZdOjs40Fvj3s=; b=NN9EqCVLzfjeLgGrieIf2R0Z2ThHNMUFZ/EjArJGFMBHFUOW5eWXDLYh0eskE35bhR VVOfGkiBOFlf9NTSaW6U57naKQ4zaWK0N6mfQTPMYMsxzjZaUSHFAmG69SKa6U7rWllj HzZcLZizY8D669OuFGe3V92pcnKS/Sn4FC7pi7ElYqEinqPAOOLvzzv8VYDfk1gYS4T8 FpKPMEvyMOGjJ6UPdrtxc6zVv/dubMhXfx2Tk3PHXIdZPoJ+17nGxBiwzlD7ccQT7G0c QCaLYDsvPFu+zU/27ebEqD1WtRplJx9GF5w1I//8Nt5fAyd3HCMXhXn2Hin2oqXKY5Zk zDmA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1790223371; x=1790828171; 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=FLDrAGg7xWWuICM8+UcK0lvwffhpOIQZdOjs40Fvj3s=; b=JnZa9cFscK8AexQEdoblZ15bumzGP+8c6ECQrSLaUSvweIGP8tho8AeQW8WH0Pb4DA 3SXVp0jZLCuBPoiR8xs462nYcShaRjtgyoSKx+HUSBW/XGZZS3Aa9JmGDlQTybbu8VBU WFsLEXfh+fKH/rI09HkWW1J5QntZHjFpPLyHuaphlz+bIKyx7egogUV7luliqOZiyRIL yckq7RVMAZFigy5fOo/Uiolf1vqbywK3S92MHDds2IbGGvt9OE6e4pZvydCviytjR/+1 vF6jjmzx1neguWlfGnPm7V1gSyMninyGeutcXZpxTgmmSAkwzQs5Zg9fDgrS5vP8X2zx fIRQ== X-Gm-Message-State: AFuF++lcaBnQ92eSx4q6dTB3uILQkx3zNVqeLkz7/refhcW67tlXAFbC Xsr7SpRVA4AVVXx40lTTRtrSZOfn+kHkFjgPJqanjV9wOfcCYwoQauDIeTpYWqzXuEu1t8ttFqM iDEDXCg== X-Received: from iofh3.prod.google.com ([2002:a05:6602:6fc3:b0:9c4:a4f8:640]) (user=avagin job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6808:250b:b0:4c4:a240:9c3a with SMTP id 5614622812f47-4d72c0429famr1389700b6e.4.1790223371033; Wed, 23 Sep 2026 21:16:11 -0700 (PDT) Date: Thu, 24 Sep 2026 04:16:01 +0000 In-Reply-To: <20260924041604.3209659-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: <20260924041604.3209659-1-avagin@google.com> X-Mailer: git-send-email 2.56.0.rc1.310.g51773c2048-goog Message-ID: <20260924041604.3209659-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.56.0.rc1.310.g51773c2048-goog From nobody Thu Sep 24 13:39:00 2026 Received: from mail-oi1-f197.google.com (mail-oi1-f197.google.com [209.85.167.197]) (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 AF6F7318ED2 for ; Thu, 24 Sep 2026 04:16:15 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.167.197 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790223381; cv=none; b=Rp1/Vo8W8GoUa8jPOiN5qFHoeNnEYyC3eQJkMifZ9hJgBJskRYPXV1gmiJkdzMuE7rl3VqNYPXQRYcari8+MZ9CwZgHRLYBv7+T1KoE1w0V8HPj5VNZZ4CfMn3kKsRfFUj23dEaD/jYwhYM8Teu2zi5pf4C8x2IEP/G4ZkDZIvA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790223381; c=relaxed/simple; bh=pVlFrXyRPMoilQAeEOQdUfMs6W76Ng/9nHuuJx2B/go=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=J+4ZQUIXBKnojtDu5NoEjDGnJz47Ea4MlV6wjQ+ypqJ2LRimEmYlvR1XzfzaHaVncPU5Y4fmQopEARDEzgCR1qQFOAzg5zmYuojGX7CESi+BX2y2jwRtVJjsAsotadocyMs27oYWoROvE8yPMcJ3zsflpzK1tOCVIcU2I+XuKfw= 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=tuHc9G8t; arc=none smtp.client-ip=209.85.167.197 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="tuHc9G8t" Received: by mail-oi1-f197.google.com with SMTP id 5614622812f47-4c7f887ac6bso2481096b6e.3 for ; Wed, 23 Sep 2026 21:16:14 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1790223372; x=1790828172; 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=Olld7wn72Rs8HlMT4HtQFLvrZ9j1PJvn2XP9d4jJL8A=; b=tuHc9G8tQdxaEqoTL3rFkZE33IUzHGnEebd6pTh6Sb549jYDG7m6gK1C8qHqFEjJHW FUjLjq/umiedJu0xemWAqv5QfGaHqp0xlR7Yarm6jMM40g53YhOGqW3Ru4ulZpRxRXCQ Ok9XDKTQBpswjFsYdtJTXGtdYX2HGiuO7S7zRokbAjbOm05hy2zOIB1tKBZwR+fKZ4nM dRcp+qnnJ828EK+3P6GGqKgrrOSeqi0ecPCSUN/ifdzlx1Lo6Zkun3lhRCNBkDd1OOTB g56aM3UECs5vD2enxNo6pepowwm3BnQazDiSVuIkiRJCThj6BlqWDRs1bxFoZoaNDORh 980Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1790223372; x=1790828172; 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=Olld7wn72Rs8HlMT4HtQFLvrZ9j1PJvn2XP9d4jJL8A=; b=gkl2ezLc1BahqC5WDT+Z41F2K6JatAK5xjiFQLS4MhQe0LUlcbeqVLdUGdvcjBxDnM BZYOzaoWGqk8nvd6I2kHfivrfP/L5zkHe7OEi0HivOx08FYrI0D22ld1dCWWKh5BESSW D5v762+HzGq3JGwI8ZOLFDdXZfC85c8SRXO5aLsb2rMKsG4OBXDyWaYCAk6GvW6KSOJq +gzRU4jVM0iSnWu2+ONo5rfToGhmyKysQ3MTNoQrbb/7fHujVUhzv2hQ57cuQXBHaau2 7PzkQJtno/pFupjhuqnSsWHsM1S/Ojnr0g5HZi2r791Lg4IApxKfFI7oxHyPwQCTIkam oWrg== X-Gm-Message-State: AFuF++lU/juzNcSzDIf0vQ/IZCHcsGjuFcd6JWdtSvbQFii9GMKp2sKW DlrvDKaLLwG4zQ9Y1X5Dy/ZntrKi/Jm+ZXPQ6bV5cg3QssuPoNP4gmwpM1QuD2v583vq0W/O71m kfQxi1A== X-Received: from ilbdv16-n2.prod.google.com ([2002:a05:6e02:4010:20b0:50d:488e:cf6f]) (user=avagin job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6808:1705:b0:4cb:f21b:6f83 with SMTP id 5614622812f47-4d72ea690a5mr1229457b6e.22.1790223372048; Wed, 23 Sep 2026 21:16:12 -0700 (PDT) Date: Thu, 24 Sep 2026 04:16:02 +0000 In-Reply-To: <20260924041604.3209659-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: <20260924041604.3209659-1-avagin@google.com> X-Mailer: git-send-email 2.56.0.rc1.310.g51773c2048-goog Message-ID: <20260924041604.3209659-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 d56819fe491e..594ba36dec73 100644 --- a/arch/x86/kernel/fpu/signal.c +++ b/arch/x86/kernel/fpu/signal.c @@ -27,9 +27,10 @@ static inline bool check_xstate_in_sigframe(struct fxregs_state __user *bu= f_fx, struct _fpx_sw_bytes *fx_sw) { + struct fpstate *fpstate =3D x86_task_fpu(current)->fpstate; int min_xstate_size =3D sizeof(struct fxregs_state) + sizeof(struct xstate_header); - void __user *fpstate =3D buf_fx; + 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; } @@ -496,14 +514,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.56.0.rc1.310.g51773c2048-goog From nobody Thu Sep 24 13:39:00 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 1E000411A07 for ; Thu, 24 Sep 2026 04:16:16 +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=1790223382; cv=none; b=qE52WI0X1cHHCtZWed3xlr4WzOnO4iIoOXbHeUze1S/hWI1SxMptHTfFj8skSLV7V/7e+BSYr3HwbSHhoR+AjhoUtauFAw8gm40aKSJ4Hq4dH+YsN1aiPcr2BlppCH4SS4FdE0gdjfe2KRaoznIP84P5Hx50jiCyWh2MuaB0Q6w= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790223382; c=relaxed/simple; bh=8mdhDtcp+ktkBhsOXw21LIPphcxOMhbdoB2hbCly+IA=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=Ak2GDxAS6THApkrOJNL/V8rGLKEA8nmBaYYqF8XbwQlpQURGNYTw0DeghtpbaJREufDDRe7+qQh0iFPhZ2l0bWkFfHKcBuOC5yGY0dtSL92WgV5sEDig3hVgz3a6ERvcH/EtAIHjW84KkTyLxrO3NRpPNZyd1MClWzUODhKFdWg= 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=AtdMNr2M; 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="AtdMNr2M" Received: by mail-oi1-f199.google.com with SMTP id 5614622812f47-4cbf700ddccso1631042b6e.0 for ; Wed, 23 Sep 2026 21:16:16 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1790223373; x=1790828173; 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=7omE5mskqIzejwV4pi0nJZ66+RS5YowQWq8HiVWV1Cs=; b=AtdMNr2M51agOsyVM1izKqfCTO0L+ePTSScHhXPPCTf8MASK8feAqlQwsPrihVmSRW ONeU/Y7Y3puqF0zxjyOE4U+j6Cr/B7JaTv5i3x39TGhJiVjWVnPCLADhKnA1xQ22DYnP fbIpg6EqKUZ3K6jsZbXPxwhuYe/Z0y6swW7D83bUJ3STDj3PzebB/TK61G3MFGnqJCCM h66eNy4j8Oml1qMHtAxhnh3ZAaS0usOtu63NjUtO7z/31sIvX6rucxZb1N621ikQoDNb iXq2VYsUjLWvWMgZI2FXc1HuG9+N6WVhcjeOEw/dXjfFcqHwI5dkRWHCias6t4feBHxp tM8w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1790223373; x=1790828173; 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=7omE5mskqIzejwV4pi0nJZ66+RS5YowQWq8HiVWV1Cs=; b=laD8Sq/v8YNlibfmEZscIehYE9mtIwKiKEEusVa4spNIZnVspgK2WMjh5cGv2H6bdK Z6zEGRpmzFXm5zuBjvuRZ9PVceGTZcQtgEtE6hjTBOiF4SK6+As0le8jSgBfhe63zMMY HiEl1ZU5+8AlHFC/E+0xc7FUEq619oLa971ZsnxkTjrUu0RYSAOs4R80lsA4uz9ilBpa nDZDf55g4+YAa4HBDuHgDbjjd2Lv6lhLdGcN9SaTX5WwzPApG3B34iHHMttEQM+ksAR6 OgL39V+qm7rED3qkjHLanZPLJtPmVeVQzv6IlAAuWrcH0T2spWUyrlpjXKtrifQeHW4H 9Azg== X-Gm-Message-State: AFuF++nYG8JJQGzHOwxOp/ropk4Kcyfwis3gzK9h9xtE8Nd2J9meoQPQ aCIF9uAd/8zFp6posWJFl7mYQvHUyLLuhRvsB/ucFE5JoryG3iJ1rxcmx0Tb5TH3kPpILbEVdSi hkpxUOA== X-Received: from ilsb15.prod.google.com ([2002:a05:6e02:48f:b0:50d:9814:cccb]) (user=avagin job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6808:5395:b0:4b9:e5fa:8a11 with SMTP id 5614622812f47-4d728f2aabbmr1350299b6e.22.1790223373129; Wed, 23 Sep 2026 21:16:13 -0700 (PDT) Date: Thu, 24 Sep 2026 04:16:03 +0000 In-Reply-To: <20260924041604.3209659-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: <20260924041604.3209659-1-avagin@google.com> X-Mailer: git-send-email 2.56.0.rc1.310.g51773c2048-goog Message-ID: <20260924041604.3209659-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 | 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 d478b13cc8d5..7565d2cf6a3c 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..cbf022fea800 --- /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) +{ + uint32_t eax, ebx, ecx, edx; + struct xstate_info xstate; + + /* 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) +{ + uint64_t xfeatures, *ymmh_p; + struct xsave_buffer *xbuf; + struct _fpx_sw_bytes *sw; + ucontext_t *uc =3D ucp; + void *fp; + + fp =3D uc->uc_mcontext.fpregs; + 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.56.0.rc1.310.g51773c2048-goog