From nobody Fri Sep 25 04:07:53 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 B31D74D2EC8 for ; Thu, 24 Sep 2026 21:01:27 +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=1790283690; cv=none; b=JWg5nUt9vBhBv5cWceRJTaJNjkMoW4a3nMy8ISNJE+SGdLtZxBYp6wibBAK9ISpLhWzJsrxFyG9fNbzZI5HmJaN2DZrvuVPDqSHY+taYg2MNMYOphgziHION7Xbvr0uAxd/47eoU+6GgnmpKR7RNb/96rCccNiKoDgk+Gib0yrE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790283690; c=relaxed/simple; bh=MFHgjRUFrURWMuBfH/7m+QQEwb6KSPJcfIBqBGQPZlI=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=CpmAcgYO+Fb05bBUsdqpKf3hEquoWrOKN0ut+6R7O2QCPJWW8zBq/RnFsXQxsEbZZSoOSjGqJKo4+t/oMeGSkMAxt8dkIollDO/+bPoVu666GrzSp5ubMDEFfkRJAtlPLjM+D0AsgEflySigDasdssLFTANLWgcSGR+rPfrlcJ8= 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=tshDfNy8; 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="tshDfNy8" Received: by mail-oi1-f197.google.com with SMTP id 5614622812f47-4b1bc2e44e0so555274b6e.0 for ; Thu, 24 Sep 2026 14:01:27 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1790283685; x=1790888485; 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=VWY43kpRw2o5ypwDPUlrvSnRFUL6IMLWNwvSOplpeh0=; b=tshDfNy86jb8yQm0M40GSzOtqb90MtfQSaymlsnmohMVY7i5CMyeUar36BvZDzlkqp gpiHRmpiNExiu0vXfrLuBCdd62k1k/WOSMz2Cm27e59e0YyVZZSwwwEghiCFDxEchiud puCvTf6pKBjdbmK6/vOTsBdBcj5eigg2FzObqDsbYpW46QkxAEwjPTGWG0yS82gaM+Hn sRbbayEHhOVyBsvdW6U8NP11aMEW/2MA4XgGutmWe7RWN3q1KRry2YS66aKvutL6pGnP vzOLpVfDGLs+WH8q5Somz4HAYeTAwViHAeHuc+8+JysvKH6OoNn4u/JT2WWcKlX8mZ6o Ke4g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1790283685; x=1790888485; 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=VWY43kpRw2o5ypwDPUlrvSnRFUL6IMLWNwvSOplpeh0=; b=CuxrnGV4AsO7mRoa39m8u+q87cUtZrc0eQQ3ILEWtis1HCy66uKZndHQ8cdxepzK2o s1khJtF12Dj86iAWwj0oqg+j7UxIc99HRst437ezPKM0c+OTJxFVqF8v8GATDqMienf0 Dn+7Rn2oj0KGK7lWCn7C420r3xSJC7GYSVldgL2UuePBa6+UHtcVnf/CU18vjEJo0a5n 9mLpteHgDE0VBDRJbc92tsEORxDv8cNe8Gn1Ttkcuk+07nK1Wmhgu+eOjCDCOKpIhbOl kgeSM2qohccd1J7gffpZEPD3hDzmiGTMpCF7qRU+MpZlW7Gf97QUs0RdndCHKyQJZ5dT vjQQ== X-Gm-Message-State: AFuF++n9wL9Qvq0sMY9EEOqv/bFD7VLlH+IhJS2SCMCWb9o/AnHquhoa 7oIm96n6uqTZzXKOQUQ+M5co0c4wrTO1oVLfUYSUsl1xEi1Wapgf1KGjvhihLfN1HXTqeb13YKN 3XVoWVg== X-Received: from iodl4.prod.google.com ([2002:a05:6602:69c4:b0:9c4:bdf1:40e5]) (user=avagin job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6808:6a98:b0:4b5:5bfb:f25b with SMTP id 5614622812f47-4d72e8622fdmr4395052b6e.20.1790283685167; Thu, 24 Sep 2026 14:01:25 -0700 (PDT) Date: Thu, 24 Sep 2026 21:01:15 +0000 In-Reply-To: <20260924210121.87032-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: <20260924210121.87032-1-avagin@google.com> X-Mailer: git-send-email 2.56.0.rc1.315.gc6ed9934b7-goog Message-ID: <20260924210121.87032-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.315.gc6ed9934b7-goog From nobody Fri Sep 25 04:07:53 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 472284BFE85 for ; Thu, 24 Sep 2026 21:01:28 +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=1790283692; cv=none; b=JE6DYPWldJHi95JktQwHc3Bhb2bMjj5d20d+Pf5gpAN+UB7mOoAk7xxJQxNcsVd4XcWckUqS5DzHO9Xgr+ihcqA8Y5kKkrF4EwaMhzb3ddpgX76oSF5kBiKCXJYoDEv9+i5kXHvc8bZf3aeCnNAM9Si7cTxbThN3Sbwc0vnAFME= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790283692; c=relaxed/simple; bh=zWUFtQ0OFpf3Izpq9TTF+b80Ep7btpGUWULX6UL8HCg=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=uKi2qjsjzAyvySDpfycYJbjUddsLBHk4kgG4iha8aBrZ2o/bPLTCt/oyPGMJ6ube7ywbSh/qn+gls8GzQWrONy62JKEAPdjmw9g7kHRcV8JUlefbslvbnyuNkZOo/lX03e1OHZRkDg4e6W+sBt0E9+StW92c3GQiW5RNmrTmWVE= 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=qv/AhzID; 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="qv/AhzID" Received: by mail-oi1-f198.google.com with SMTP id 5614622812f47-4ab4dd46ddeso342535b6e.3 for ; Thu, 24 Sep 2026 14:01:28 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1790283687; x=1790888487; 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=kuB32MkZPc+XuzqWI7qs9zzr2PkaAGHh7pBlaVBsovk=; b=qv/AhzID+HGl+Fx+DQ0ZcRM5XZ4MVrtvj+m0CzM2aytQHTKVS04BeFL8ZsZ5q4+t61 axLx/VVFHXDqyut6wdorzzV8AnS03M4nxo6UIp46irC+Y1wt9lxaiQBua+2ps4qr39w7 cEsTbk+Co4IgCDKtMLz4tO+anEj+LGxn2/zq6Sfl1VcSS1zKrIhsZVVH8IuDDaLofvx4 qq6aNP6g8p918zx2PXp9D0G1f30sn5eXA/7UlwoipK7fty4eb3BR7mgQOgAFX+1WP47A hUq3AkDuyXNzoRW2lRtPhir9tMmDuZtjt8vuN1Fm9V4XzV3la6dYKaHymU5cJpohaAkl /R1Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1790283687; x=1790888487; 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=kuB32MkZPc+XuzqWI7qs9zzr2PkaAGHh7pBlaVBsovk=; b=A4HdBvRDJ4TVXV66e9uHiAjItQR2u8TJ98XPvkRVj9FvMwVXkLiFOIy5Qts1GFPVeF KsOLvB6Nwl9TzwD+uIvqjd0O7yJBersI/u/hYX4OSFk9sd/JC5blsjABHEUjCBppSC0k Fbti7Dh/+WB4SutxtQsKaToxyzMu4WSVrqSYR2eZdn8dBKGep5mQwnPdb2EmobZ8Jc0U 8DKvvkJ/bXvFFQWLA/japGK2iNWmk6FGXIFiOTrgulbvoSG0m12tYc+dOaO/Ka/T2b5o Vvda84XHCtmb0oQLQSFSd0+HqVjLCI0Nt5Tx8KCEj5kA1C/lbjBk7lBcWzBntvayvV8/ lfBQ== X-Gm-Message-State: AFuF++nAdC+H9siYyJIFQeZjwlg9+S7dfBd4fKX8gcPfJ5x6dw+oK1c2 Bo/CfgOAFAVM6ZnVdcCCZMWnp65AEluk5lMBSu5+rFI0zkeWOQQ+gfeKy1uIgRnsDfRoZOfFqET avu/ivg== X-Received: from iobbj14.prod.google.com ([2002:a05:6602:35ce:b0:9b6:e72f:c47b]) (user=avagin job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6808:1990:b0:4b2:8e1c:5920 with SMTP id 5614622812f47-4dc57e29203mr41742b6e.9.1790283686460; Thu, 24 Sep 2026 14:01:26 -0700 (PDT) Date: Thu, 24 Sep 2026 21:01:16 +0000 In-Reply-To: <20260924210121.87032-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: <20260924210121.87032-1-avagin@google.com> X-Mailer: git-send-email 2.56.0.rc1.315.gc6ed9934b7-goog Message-ID: <20260924210121.87032-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.315.gc6ed9934b7-goog From nobody Fri Sep 25 04:07:53 2026 Received: from mail-oi1-f200.google.com (mail-oi1-f200.google.com [209.85.167.200]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B1F304CC604 for ; Thu, 24 Sep 2026 21:01:30 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.167.200 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790283693; cv=none; b=mz95piCyJiJCJKAllP1SUF2Hd0fh24lHDlY4h7Ofm5O33p2FZV0MtnRu+8lk1ckvgg5qvTUJo8z1IhV28VDGfeOvAx60PRfBNpL6MGbCg4RQqvGI8ewtkDKtXMLtv0G6hTXfLNnVNiQycxzzbe0Uri9x/4Knn75hEXMY+zHBYHs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790283693; c=relaxed/simple; bh=4RpkI8KfXMOOmHjyMtN5zyDz4IiUQsJHKpCFwvHuwUI=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=um8nVSUaleY7OFxLU1Y4o/UvoHTy0+Gbw7UXL5rUX5TKkvgcSApANZTQXTre5KsO3dCr5BESLu4UcKyBzmf323CcVi6u/O+ipCmxyH4PX6KMIWiNor9FrZYeQl9G9WaXUogrS6Y82pBNvS7npwbOj4nFfYhSfQZROj7+Tg5qztg= 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=T90HGomE; arc=none smtp.client-ip=209.85.167.200 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--avagin.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="T90HGomE" Received: by mail-oi1-f200.google.com with SMTP id 5614622812f47-4cbf700ddccso383473b6e.0 for ; Thu, 24 Sep 2026 14:01:30 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1790283688; x=1790888488; 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=naZNUXTORfsHf0bPiWPQFHA5ELE5DP9iIGnMiQwp+lA=; b=T90HGomERP+29ScP2Q6aDcIy8CM/CSDu5B3apccO2MBKmW/IOfiQHBP9HgF9ESkD/G uFkkH9WtHF4SMaCzf6StQ4qUfQjXA5D0bfo9pfns/ersr5z2Q/j3eatZjjtVZQqSShNH tTgSabKnkY7axUSlRcbwVMafwqKzgqZINrdk9fWEHtcOxyCGSubQgNn9Q4BO22byFfwL zuloUGn1Y78c77+zN/vrJrg/dr7A9+OWsqVtiqpsA7b21pDNzhjtUZkZ856+Zo635pdJ E4qp1+K8+Qouz4eg8Gjh7lSdQQiGmmODhXupDOtmxD/Cr6TSwB3ELpxN2LGo+qljUKuf 3HMQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1790283688; x=1790888488; 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=naZNUXTORfsHf0bPiWPQFHA5ELE5DP9iIGnMiQwp+lA=; b=cTiqclMepuxv48vlDKjifc8cfsIIMMDPHZVC0V6ioqAmYKNyiOgYHmtrJUL1/o7DJx C+unl5Qdj8YhTu48Sgz9P3cJU2Mqe7jd13f3MFmDUgeQdV1MWO0oi+Y3ZT6IDkqXWysq q9aMpntZd2/bqaFX11u9NaG2JXf/WKWNqKElwQ45yja2URi8kDDIMBuPmbmVKktlqahD +Wy6kk/eAMpeF7numQkwzAy1ePfH9LS3k+/2+QwoY5vqAPn32RaduyQV6jhD9EpWApex n4xfpvNQM5s8gCHp2KCbbTLZdhODA/PVrEM1D0RwAdkBCIDbDI3yZM47mbjG9/ftbE3l neoA== X-Gm-Message-State: AFuF++nKwYDEhxAmFH9MgMwI7OPnZUKPXeHqyxIMUA6qCQOROx1L/Xh4 V5vL1+s2gMOj3PlL5iSjMVf/tO00mN7qlMK1YN3vJpWxO6cAY5wyeBTKtP3XwHA3p12r2ak58AX mIFkauA== X-Received: from iobhi22.prod.google.com ([2002:a05:6602:1d56:b0:9ac:9ca2:2fe0]) (user=avagin job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6808:2518:b0:496:34a:d7e3 with SMTP id 5614622812f47-4dc5a2bbdb0mr32861b6e.17.1790283687699; Thu, 24 Sep 2026 14:01:27 -0700 (PDT) Date: Thu, 24 Sep 2026 21:01:17 +0000 In-Reply-To: <20260924210121.87032-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: <20260924210121.87032-1-avagin@google.com> X-Mailer: git-send-email 2.56.0.rc1.315.gc6ed9934b7-goog Message-ID: <20260924210121.87032-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.315.gc6ed9934b7-goog From nobody Fri Sep 25 04:07:53 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 746CF48CD63 for ; Thu, 24 Sep 2026 21:01:31 +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=1790283693; cv=none; b=giTHGBheUavADQoR7tkk84aZaVDxa6aEzqNxk+DGs/2T5unSylqnLw7Oudki9xEuVMFcH2FgUmn9XuBcRDD0uqIGZskurpVWelJqm+ApGH2S0NrMUuwkbjubj2kNOBUaj+z27acUiJfsmnj073A08qkAnYrohCvaB8v2H99FPpM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790283693; c=relaxed/simple; bh=mj3SdbpcuVXLFrrUIf3KbnkVRVxd21IkNwP+DxYDJxk=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=QhMrE4Q9zmECznZ9bzgVJAcF+gGMyoN4zqvGZa293kVYj4xtuc2R2TFlOCF0YY0CFvW/dPu3d9m56ix+4xKX9cVm02OQk4laADi3yXFr1UVQlBusZYBDNMQbUSVp1dpXiO9KU3nIYdkkgkshUFHFq10QAoU0tz6ajMoe9l7eU1A= 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=eDy8n+D8; 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="eDy8n+D8" Received: by mail-oi1-f199.google.com with SMTP id 5614622812f47-4b2d1eb484aso514890b6e.2 for ; Thu, 24 Sep 2026 14:01:30 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1790283689; x=1790888489; 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=MtHl8hYmoYmzTRn6TIDSWRUdJUOUFOpNuKrI1Eig3qY=; b=eDy8n+D8jXnzMAz6BAu3kWf0EDjoxk5cOp8zItEIsq+1RxvyF+ZqsSqReSPnnNg8Mb xaVUgFYnsca8vBOWbNcP5J0Xpnskza/lRY0iCh4poJ15mfEKxoiY04LtE+UHQ1I3V1k+ qWDwrZxevlWlKAV6eunVadxvaU7dCgOICU2LdcUEOmHOFKJPtMl1Jlro7IzRd3E5CMa2 K4u9jYcE7MzKvJVF1mOk6F/nvVKM9VWM97I9W6+2XGp5r95jfBFbtVFyXKm2aq2jbW7V CqT0KLYEO+CELmKVsa8etlnkA7DBdWlB21i0fuZ/S04s8l+l5Szyz87uRG5z/x3+ravq Q1KA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1790283689; x=1790888489; 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=MtHl8hYmoYmzTRn6TIDSWRUdJUOUFOpNuKrI1Eig3qY=; b=HjowTSrytCBFWQ5S7EaW1pfJ3BrShQnqYi0erpk8VztmQE5FERia/SSm3n/u9DYY50 1rgKUCTFMVRoqYikdBrzssZHd8IR/nLoLcBXMbSl0qU+hJ7Cv4sX4gigDpIAb0KhhRfe GsTasf0moNWmqGiD3KdVBAroK9hR3pRgC8B+E842tQTfoKNPUU1etah/99htJUcMr+yC HFa1u2ekt8BWGelBOix6YxQdnQNXvLsUcybiqMJz85dK6rz/QIP+mwKqDq3ZHlCikZun jlbeTsIQjnChkfluCTyrTKQum3Q3qKlRbF1NPdZYuXM4hRjGFHSTsL7/jUr5HMjFk32f 16zw== X-Gm-Message-State: AFuF++kR91DRkOf4EUXMBiVD80w+j3vfsoRK0msBTTvxN/81zyUZ/3I2 WziNjexjwra6v4yLrDWjiD8wWDDKYjeOFcVE/OGfdH5fVmVVYBpxWpiY8LNznW5N7yP39ugNEVP e3VpUGA== X-Received: from jaar2.prod.google.com ([2002:a05:6638:c082:b0:5f1:d3d4:268d]) (user=avagin job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6808:3090:b0:4b9:e65b:8c39 with SMTP id 5614622812f47-4d72aa5ad53mr4530494b6e.39.1790283688845; Thu, 24 Sep 2026 14:01:28 -0700 (PDT) Date: Thu, 24 Sep 2026 21:01:18 +0000 In-Reply-To: <20260924210121.87032-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: <20260924210121.87032-1-avagin@google.com> X-Mailer: git-send-email 2.56.0.rc1.315.gc6ed9934b7-goog Message-ID: <20260924210121.87032-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.315.gc6ed9934b7-goog From nobody Fri Sep 25 04:07:53 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 621D54DA9A6 for ; Thu, 24 Sep 2026 21:01:32 +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=1790283694; cv=none; b=ZhoFGh+s5sY+X3P4aj5fSV18iKUGzcMBVYIzjuj+6kgXrorFmoFITfba50uUjvjbcjzWhHeq2q+wNyqeIfOUr2RbpXEGxARYfY4jq50SkPYvmeM1E8UXRdMTwdnwYLgN1czo8iDEU3KpJM9sS5jJ7JYLeRCcEm2HRsGKI8H3GtY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790283694; c=relaxed/simple; bh=eWdNXYkzpEGoLypYsfkXKKnEba4CgKEPV8FCoFIGb5o=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=fhZjHGjB9ndsHisHtZqJStUy4SCJ6GoTa1FpXhwp9PDXxdQI9e2xzVOzARrb7HvuO4g4VWHaLYhZZR7sJH4PL3raWtSZWpiGcwez3G0OTzi6CXV5vl037WjUuKU6lCNw9Nuw+wd5AOhj/0WffwsPt56VrlHzh2mmruNg87htJqQ= 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=ncMPk2z5; 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="ncMPk2z5" Received: by mail-oi1-f198.google.com with SMTP id 5614622812f47-4db3db8687fso350155b6e.0 for ; Thu, 24 Sep 2026 14:01:32 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1790283690; x=1790888490; 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=dwQiHsyrLWUzsxzNSVYlkwXiHkF2i4EcK7GZMOAbO5Y=; b=ncMPk2z5Tode78c6tfWQYJLqBiIliNfKsfN676GUv1WtQ5abKsygzx1Gw1OPLGMO0r 3z4N3e5Q3a12fLk2NNGMJACOdu+mtTrOZ5FGCjnPX7F1U12VaKzJrcUtfxVyuLgBCUSO caVCLWkgR12ircKHw3WAi6493vREg4cggoo++jGKzfmnXoojRaq3tnNI3TdGH1ggi0BI r6XpgqQ9LkmG+jqKFfI5TFYakdUR5ZMs65528wQwjI2JH/4LSR9zkEok1NkaF6dji6Dt +I60RjnU9ZlDUaHLHBNp0jXQErwJj3He/TbWza8a21PAqYvgLlqE3bxByxQ1x5aLtz5v FjfA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1790283690; x=1790888490; 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=dwQiHsyrLWUzsxzNSVYlkwXiHkF2i4EcK7GZMOAbO5Y=; b=tLIuirs+5YQYX+ooubZWVKJlZ8/d8a84A0Ugw11+YTUhEsye/n1OqFHjNzPrVZCjEw BX3vfAvPQ2qQb0cfSG2gZMtf+4C0mm3F1WRc1XjyNowBmMdk9AFV/+VKmUHa10FZtyJP /9TczvQ7NzoCDjV2qMgYpWKInjeYDBWLv0IigwjSQGdnMagDYY/d1E4Qg5I8xl7f/wQb pAH5xg+HzrsQqPM2mIbt8wkha8eqaPuPKyfI6GKXhcX7ZSoJ4P6oDB82/QRnpzwP2BXh QdNSr4Vz8OnWSW1RI9t3fFmzQqaRFr6J+dM0Id4GhGhF9ucvO099Gy+wbJ7fhX2TPNYu Spkg== X-Gm-Message-State: AFuF++nTbKVoiR2bhLenh/kwOhE2uVeNARZgJgTolmJgcd9KJLESp0f9 0o5NRfphLSI4onssgFbmBeM8z1DmlYRY+V/xKftxW1C0FzkqnuDvBQE6aoHbRHiS6xC+zfA7s1h tLjO7/A== X-Received: from iobdo19.prod.google.com ([2002:a05:6602:4613:b0:9c3:8c1f:2a18]) (user=avagin job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6808:10c7:b0:4d6:48c4:a819 with SMTP id 5614622812f47-4d72cb41616mr3405681b6e.29.1790283689932; Thu, 24 Sep 2026 14:01:29 -0700 (PDT) Date: Thu, 24 Sep 2026 21:01:19 +0000 In-Reply-To: <20260924210121.87032-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: <20260924210121.87032-1-avagin@google.com> X-Mailer: git-send-email 2.56.0.rc1.315.gc6ed9934b7-goog Message-ID: <20260924210121.87032-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.315.gc6ed9934b7-goog From nobody Fri Sep 25 04:07:53 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 34BEC4D8DA0 for ; Thu, 24 Sep 2026 21:01:33 +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=1790283695; cv=none; b=qES5pyJ1fsW+4LKMdzZ7zRglYRfs7lkQmN6Dk11HrX16/ZQRgBBYgghDjent7Kj6mm7FJPUk2GU2FfVqVx6EjaIkZUga32PS/EvOfSBxgviXhCw2k/a7Ux+zWRiCGEkrVpfLSIpyQWYSquXHH9SUjK9HsEYtVK1x9MlCvMTdnRE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790283695; c=relaxed/simple; bh=tM1fEi/PQAroXNwyfupEEjRLXuAl9ax4bNrf5+sYBPE=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=hl7iWEur7+xhCZRWjO5DU8PS5BAXuSOkW8Ygvs3I9NZFGzsa0sqzTxNNVA2fvBXTGfmDKvBgnj+HI8mZhH++okH7uPAEFOgragC6KW9DoojbFiKAx7OtWpllA4CxcK8Rz+9ZQNW0326Lytske7kiNFDPKj588+q0Sm9GvYbWZ3A= 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=qlYMPA1J; 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="qlYMPA1J" Received: by mail-oi1-f197.google.com with SMTP id 5614622812f47-4c307941971so573577b6e.1 for ; Thu, 24 Sep 2026 14:01:32 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1790283692; x=1790888492; 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=ckIPpA4upIUvDnB35HI0PWHSY3LNEH7jGrCRpdYpN1I=; b=qlYMPA1JzSJcJaGUAEKZ11XmMZT8KokNR1D1PSIeNhPhVP5qPjzM6iNO8HpWogN4Wz nN10WmikpLX1TiMmgWaM85Jx89rYmI0Md4rcVupLvdBdf3vrBbP2BtRCniUtBOPUXNdX 3BL/JH1m9bQzR/SRPvSSP03gq1LHUdvG9YpZCz5Q+kQ+JBJfY9JVQ4JCE6d7r5tb6lja 8AkpsbmJIH7GOq+LDW4dOaJiXqGVfx5ubJCvDxWhGSXduLeA0G8FeEBFRnh9fdxC8RNG AkIEuTHw3iyVxpmqsh/DBcJaCHzmjq2ZVLLr+hrYMtt7LevXVNk4Fl/y62NbZJ8OhHIx 4sCw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1790283692; x=1790888492; 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=ckIPpA4upIUvDnB35HI0PWHSY3LNEH7jGrCRpdYpN1I=; b=JY6+gh62ja0mzyaA7cfCwpFXudT7fINiRr0dM+QD+oec1lraLROIBW0D3EzgxI98Ht nVG56ML6g3XwEoz0vEMUpnu2pxHsvBmOby6FTSrGsEMtgi2Fhtee/nOCSvgreHbVe5eJ jbDV6fWBf5ffyZHuuNG3tYyj/ySTN6zc5oYd+GxvXddYbfaxsXhJL0B2OCvPDSpTOVj2 D3uDS9Vi1n5uC5xNrhfyXVuc15R75Fx6FUsPj1lQ76AehWiHZeTjJpN6QnWDoSBhnoB2 gVcPKaElvvQhbjB4g21vuShMZB73KvuLqeG1/vD+Uvp5CnHfuwgc8zvYloXlmT5HLSGU DsGQ== X-Gm-Message-State: AFuF++lbBtXslK40j5SsXkN+BJzNL2UjV0dC8CH3+ly/3tIJBqtv4tss XfRu8SOQdlATGu09d4IuDzEgW1cUBhRcCW8q5zE+RgOvllg4etddT+fZFIkawpI0RsHkpMeQRwA axeE3gA== X-Received: from ilbeo28-n2.prod.google.com ([2002:a05:6e02:291c:20b0:50d:a931:a100]) (user=avagin job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6808:1719:b0:4b2:8d7b:390b with SMTP id 5614622812f47-4d72c6374d8mr3964788b6e.19.1790283691228; Thu, 24 Sep 2026 14:01:31 -0700 (PDT) Date: Thu, 24 Sep 2026 21:01:20 +0000 In-Reply-To: <20260924210121.87032-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: <20260924210121.87032-1-avagin@google.com> X-Mailer: git-send-email 2.56.0.rc1.315.gc6ed9934b7-goog Message-ID: <20260924210121.87032-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.315.gc6ed9934b7-goog From nobody Fri Sep 25 04:07:53 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 D3E414DB547 for ; Thu, 24 Sep 2026 21:01:33 +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=1790283696; cv=none; b=lzvEVCLqjnl4teBCfSlGEu0Ew+IZng0/aLJyUDR16TGzND7nm0NVrJWtz6anCGRqV0uq5AwMZjGNnNFws50LrqYlXQXeiGp/GojYvbdiYeLCjf/Y35W85Lj4UW7EpWBQxEdxZY+P2COB4Tt9SeTDZ2WOneSE0EwvLL73ht3ajN8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790283696; c=relaxed/simple; bh=KNHRBo/wxT6dFC+7L05V76uJZRRqqhVOIHOAUqZTAKg=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=lfgxfSu6rAyyq6Ha1NgYnEvSudh+iXWja1zLO8lmUV39qNeTYH1R/woancZ/NE1L1AqvrCZUaAjqIvi4+eA2geO1gpZpqEWFA2VMk1Scf6SCpqdirD3ddfM51kARwgidVf12mT6MjXz99PV1hvfQ0Ex0D4jmp2Ej3aSQEulF7vU= 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=dgA9gpFr; 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="dgA9gpFr" Received: by mail-oi1-f199.google.com with SMTP id 5614622812f47-4b28d7ffab1so456584b6e.1 for ; Thu, 24 Sep 2026 14:01:33 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1790283693; x=1790888493; 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=OAA2/FqOTi+6BQlM/aHkp+E0OZnnUKxQ+rlsIm8P9+0=; b=dgA9gpFri5JmOLHTGKNYBLoVCspyLWUlevJcYK8tpe4vY4lPDXlIlaZhXFuOHMfNqx F2TsbK48NzPF26JFcwPQNRjvdqDoqsJiMsDC4VHDLUtoz2WUtg9jq7PqP2IyhYlje1Sz ysb67sgIJfCBdJIgJLBCwfkv2lUvVJ5WSsc3WOqzoZQNaoqlHJJ1IxKLmnfLdhIzkbJy iAyYYsxxplTddq7xAtouQ9Pkvhk3WQfWm3DdeikOx6VRycJ9TJgbmiIZXs4oi+7oFePQ 6q9ACq93izxuCRyXKpV+VmTZbqwcrGvkISt3ayvvsHH4XdSuz5caubFweQ9GbwyPvx15 xK8w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1790283693; x=1790888493; 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=OAA2/FqOTi+6BQlM/aHkp+E0OZnnUKxQ+rlsIm8P9+0=; b=ObnANWNAwWmT3zaLKi4/fi5yqivE+NRPZtriIoTyFKKeAQS8EbX6Z33AIVaKYkuu7J DGM7Q2731RpR2EjOOoad3gWFiYecsds/qk6mWhvi2UJN6g6YVu4jusyUo08tCKDg6+1O HjT8Q6fdbaH3QVt/hbjAmEuJGOBMIQRq3d5GKqTdR3MMeyJ6DtfXewl/f61pXWAH0NRo T1z377i9pt2n0m/hssyMMeAsuK3SqWUCSj1jmwOaXqapQ516RQgcTvPHbcoeN7Cu37px Tr9APGoJASLzhfh3WhW1W4kg5lQ4by+0N1v2xlSyVV1pRG2UWkWUONxT8dZydp62gk39 hKzg== X-Gm-Message-State: AFuF++nKRfYwstTNQRf/2lEVJl7we+b7l7dk00CcMOThFyy9qZmUxJMa 1dzHCq6++KTDoVnr/1cR4cv3eLfxnvXWR2yWE7QdtSc/s5U6gKRZGqCnJeNpOxtsYTTVD5xqCLW QODMVEA== X-Received: from ilbdv10-n2.prod.google.com ([2002:a05:6e02:400a:20b0:50d:460d:d08e]) (user=avagin job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6808:124e:b0:4d6:9133:cfea with SMTP id 5614622812f47-4d72d15df51mr2869961b6e.47.1790283692370; Thu, 24 Sep 2026 14:01:32 -0700 (PDT) Date: Thu, 24 Sep 2026 21:01:21 +0000 In-Reply-To: <20260924210121.87032-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: <20260924210121.87032-1-avagin@google.com> X-Mailer: git-send-email 2.56.0.rc1.315.gc6ed9934b7-goog Message-ID: <20260924210121.87032-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 | 235 ++++++++++++++++++ tools/testing/selftests/x86/xstate.c | 12 - tools/testing/selftests/x86/xstate.h | 20 ++ 4 files changed, 259 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..8377de052032 --- /dev/null +++ b/tools/testing/selftests/x86/sigframe_fpu_portability.c @@ -0,0 +1,235 @@ +// 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; + +/* + * Load %ymm0 from @v, invoke SYS_kill to deliver @sig, and store the + * restored %ymm0 state back into @v within a single inline assembly + * block so the compiler cannot clobber %xmm0/%ymm0 between steps. + */ +__attribute__((target("avx"))) +static void raise_with_ymm0(int sig, uint64_t *v) +{ + 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 ("vmovdqu %0, %%ymm0\n\t" + "syscall\n\t" + "vmovdqu %%ymm0, %0" + : "+m" (*(char (*)[32])v), "+r" (rax) + : "r" (rdi), "r" (rsi) + : "rcx", "r11", "ymm0", "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) + +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]; + + 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; + raise_with_ymm0(SIGUSR1, 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]; + + 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; + raise_with_ymm0(SIGUSR1, v); + 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.315.gc6ed9934b7-goog