All of lore.kernel.org
 help / color / mirror / Atom feed
From: Andrei Vagin <avagin@google.com>
To: Thomas Gleixner <tglx@kernel.org>, Ingo Molnar <mingo@redhat.com>,
	Borislav Petkov <bp@alien8.de>,
	 "Chang S. Bae" <chang.seok.bae@intel.com>
Cc: linux-kernel@vger.kernel.org, criu@lists.linux.dev,
	 Dave Hansen <dave.hansen@linux.intel.com>,
	x86@kernel.org,  Andrei Vagin <avagin@google.com>,
	"H. Peter Anvin" <hpa@zytor.com>
Subject: [PATCH 06/10] selftests/x86: Add a test for signal frame FPU portability
Date: Mon, 15 Jun 2026 19:37:12 +0000	[thread overview]
Message-ID: <20260615193716.1843340-7-avagin@google.com> (raw)
In-Reply-To: <20260615193716.1843340-1-avagin@google.com>

Add a new selftest tools/testing/selftests/x86/sigframe_fpu_portability.c
that verifies that the kernel correctly restores the xstate context even
if the frame size has been manually reduced, as long as the
FP_XSTATE_MAGIC2 marker is correctly placed at the end of the specified
xstate_size.

This test simulates a scenario where a signal frame is created on a
system with fewer xstate features and restored on a system with more
features.

Signed-off-by: Andrei Vagin <avagin@google.com>
---
 tools/testing/selftests/x86/Makefile          |   5 +-
 .../selftests/x86/sigframe_fpu_portability.c  | 162 ++++++++++++++++++
 tools/testing/selftests/x86/xstate.c          |   5 -
 tools/testing/selftests/x86/xstate.h          |  12 ++
 4 files changed, 178 insertions(+), 6 deletions(-)
 create mode 100644 tools/testing/selftests/x86/sigframe_fpu_portability.c

diff --git a/tools/testing/selftests/x86/Makefile b/tools/testing/selftests/x86/Makefile
index 434065215d12..72071deda978 100644
--- a/tools/testing/selftests/x86/Makefile
+++ b/tools/testing/selftests/x86/Makefile
@@ -19,7 +19,8 @@ TARGETS_C_32BIT_ONLY := entry_from_vm86 test_syscall_vdso unwind_vdso \
 			test_FCMOV test_FCOMI test_FISTTP \
 			vdso_restorer
 TARGETS_C_64BIT_ONLY := 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 := ldt_gdt ptrace_syscall
 
@@ -138,3 +139,5 @@ $(OUTPUT)/avx_64: CFLAGS += -mno-avx -mno-avx512f
 $(OUTPUT)/amx_64: EXTRA_FILES += xstate.c
 $(OUTPUT)/avx_64: EXTRA_FILES += xstate.c
 $(OUTPUT)/apx_64: EXTRA_FILES += xstate.c
+
+$(OUTPUT)/sigframe_fpu_portability_64: CFLAGS += -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..169548892f92
--- /dev/null
+++ b/tools/testing/selftests/x86/sigframe_fpu_portability.c
@@ -0,0 +1,162 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/ucontext.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <cpuid.h>
+#include <unistd.h>
+#include <sys/syscall.h>
+#include <asm/prctl.h>
+#include <stddef.h>
+
+#include "helpers.h"
+#include "xstate.h"
+
+/*
+ * This test verifies the FPU portability of the signal frame.
+ * It verifies that the kernel correctly restores the xstate context even
+ * if the frame size has been manually reduced (shrunk), as long as the
+ * FP_XSTATE_MAGIC2 marker is correctly placed.
+ */
+
+#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;
+
+/*
+ * 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 = SIGNAL_BUF_LEN - strlen(sig_err_buf) - 1;
+
+	strncat(sig_err_buf, msg, left);
+}
+
+static void check_avx_support(void)
+{
+	struct xstate_info xstate;
+	unsigned long features;
+	long rc;
+
+	/*
+	 * Check if the kernel supports AVX (XFEATURE_YMM).
+	 * This also confirms that the OS has enabled XSAVE.
+	 */
+	rc = syscall(SYS_arch_prctl, ARCH_GET_XCOMP_SUPP, &features);
+	if (rc != 0)
+		ksft_exit_skip("ARCH_GET_XCOMP_SUPP not supported\n");
+
+	if (!(features & (1 << XFEATURE_YMM)))
+		ksft_exit_skip("AVX not supported by kernel/hardware\n");
+
+	xstate = get_xstate_info(XFEATURE_YMM);
+	if (!xstate.size)
+		ksft_exit_skip("AVX not supported by hardware\n");
+
+	ymm_offset = xstate.xbuf_offset;
+	xstate_size_ymm = 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" : "=m"  (*(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)
+{
+	ucontext_t *uc = ucp;
+	void *fp = uc->uc_mcontext.fpregs;
+	struct _fpx_sw_bytes *sw = get_fpx_sw_bytes(fp);
+	struct xsave_buffer *xbuf;
+	uint64_t xfeatures, *ymmh_p;
+
+	if (sw->magic1 != FP_XSTATE_MAGIC1) {
+		sig_print("magic1 is not valid\n");
+		return;
+	}
+
+	xbuf = (struct xsave_buffer *)fp;
+
+	/* Shrink the frame to just YMM size */
+	sw->xstate_size = xstate_size_ymm;
+
+	xfeatures = get_xstatebv(xbuf);
+	xfeatures &= XFEATURE_MASK_FPSSE | (1 << XFEATURE_YMM);
+	set_xstatebv(xbuf, xfeatures);
+	/* Also update sw->xfeatures as the kernel relies on it */
+	set_fpx_sw_bytes_features(fp, xfeatures);
+
+	*(uint32_t *)(fp + sw->xstate_size) = FP_XSTATE_MAGIC2;
+
+	ymmh_p = (uint64_t *)(fp + ymm_offset);
+	ymmh_p[0] = TEST_YMMH_VAL;
+	ymmh_p[1] = TEST_YMMH_VAL+1;
+
+	/* clear everything after MAGIC2. */
+	if (sw->xstate_size + 4 < sw->extended_size)
+		memset(fp + sw->xstate_size + 4, 0, sw->extended_size - sw->xstate_size - 4);
+}
+
+static void test_shrunk_xstate_size(void)
+{
+	uint64_t v[4] = {0, 0, 0, 0};
+
+	sig_err_buf[0] = 0;
+	sethandler(SIGUSR1, handle_shrunk_xstate_size, 0);
+
+	v[0] = 0x1111111111111111ULL;
+	v[1] = 0x2222222222222222ULL;
+	v[2] = 0x3333333333333333ULL;
+	v[3] = 0x4444444444444444ULL;
+	write_ymm0(v);
+
+	raise(SIGUSR1);
+	v[0] = v[1] = v[2] = v[3] = 0;
+	read_ymm0(v);
+
+	if (sig_err_buf[0])
+		ksft_test_result_fail("%s\n", sig_err_buf);
+	else if (v[2] == TEST_YMMH_VAL && v[3] == (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);
+}
+
+
+int main(void)
+{
+	ksft_print_header();
+	ksft_set_plan(1);
+
+	check_avx_support();
+
+	test_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..40062b28c001 100644
--- a/tools/testing/selftests/x86/xstate.c
+++ b/tools/testing/selftests/x86/xstate.c
@@ -42,11 +42,6 @@ static inline uint64_t xgetbv(uint32_t index)
 	return eax + ((uint64_t)edx << 32);
 }
 
-static inline uint64_t get_xstatebv(struct xsave_buffer *xbuf)
-{
-	return *(uint64_t *)(&xbuf->header);
-}
-
 static struct xstate_info xstate;
 
 struct futex_info {
diff --git a/tools/testing/selftests/x86/xstate.h b/tools/testing/selftests/x86/xstate.h
index 6ee816e7625a..c531667b66ad 100644
--- a/tools/testing/selftests/x86/xstate.h
+++ b/tools/testing/selftests/x86/xstate.h
@@ -3,6 +3,8 @@
 #define __SELFTESTS_X86_XSTATE_H
 
 #include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
 
 #include "kselftest.h"
 
@@ -160,6 +162,11 @@ static inline void set_xstatebv(struct xsave_buffer *xbuf, uint64_t bv)
 	*(uint64_t *)(&xbuf->header) = bv;
 }
 
+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 +182,11 @@ static inline uint64_t get_fpx_sw_bytes_features(void *buffer)
 	return *(uint64_t *)(buffer + SW_BYTES_BV_OFFSET);
 }
 
+static inline void set_fpx_sw_bytes_features(void *buffer, uint64_t features)
+{
+	*(uint64_t *)(buffer + SW_BYTES_BV_OFFSET) = features;
+}
+
 static inline void set_rand_data(struct xstate_info *xstate, struct xsave_buffer *xbuf)
 {
 	int *ptr = (int *)&xbuf->bytes[xstate->xbuf_offset];
-- 
2.54.0.1189.g8c84645362-goog


  parent reply	other threads:[~2026-06-15 19:37 UTC|newest]

Thread overview: 30+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-06-15 19:37 [PATCH v3 0/10] x86/fpu: Restore and reinforce signal frame portability Andrei Vagin
2026-06-15 19:37 ` [PATCH 01/10] x86/fpu: Document " Andrei Vagin
2026-06-26 13:51   ` Alexander Mikhalitsyn
2026-06-30 19:23   ` Chang S. Bae
2026-06-15 19:37 ` [PATCH 02/10] x86/fpu: Clean up and rename variables in signal frame handling Andrei Vagin
2026-06-26 17:05   ` Alexander Mikhalitsyn
2026-06-15 19:37 ` [PATCH 03/10] x86/fpu: Split __fpu_restore_sig to extract compat path Andrei Vagin
2026-06-26 17:20   ` Alexander Mikhalitsyn
2026-06-15 19:37 ` [PATCH 04/10] x86/fpu: Document reasoning of FX-only fallback Andrei Vagin
2026-06-26 14:22   ` Alexander Mikhalitsyn
2026-06-30 19:23   ` Chang S. Bae
2026-06-15 19:37 ` [PATCH 05/10] x86/fpu: Fix potential underflow in xstate_calculate_size() Andrei Vagin
2026-06-26 14:32   ` Alexander Mikhalitsyn
2026-06-30 19:23   ` Chang S. Bae
2026-06-15 19:37 ` Andrei Vagin [this message]
2026-06-26 14:39   ` [PATCH 06/10] selftests/x86: Add a test for signal frame FPU portability Alexander Mikhalitsyn
2026-06-15 19:37 ` [PATCH 07/10] x86/fpu: Pre-fault only required size of xstate buffer Andrei Vagin
2026-06-26 17:28   ` Alexander Mikhalitsyn
2026-06-15 19:37 ` [PATCH 08/10] selftests/x86: Add a sigframe insufficient xstate_size test Andrei Vagin
2026-06-26 15:02   ` Alexander Mikhalitsyn
2026-06-15 19:37 ` [PATCH 09/10] x86/fpu: Allow restoring signal frames with larger xstate_size Andrei Vagin
2026-06-26 17:32   ` Alexander Mikhalitsyn
2026-06-30 19:23   ` Chang S. Bae
2026-07-01  0:48     ` Andrei Vagin
2026-07-06 17:08       ` Chang S. Bae
2026-07-07 20:27         ` Andrei Vagin
2026-07-09 21:14           ` Chang S. Bae
2026-07-10 22:24             ` Chang S. Bae
2026-06-15 19:37 ` [PATCH 10/10] selftests/x86: Check restoring FPU state " Andrei Vagin
2026-06-26 15:12   ` Alexander Mikhalitsyn

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260615193716.1843340-7-avagin@google.com \
    --to=avagin@google.com \
    --cc=bp@alien8.de \
    --cc=chang.seok.bae@intel.com \
    --cc=criu@lists.linux.dev \
    --cc=dave.hansen@linux.intel.com \
    --cc=hpa@zytor.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=mingo@redhat.com \
    --cc=tglx@kernel.org \
    --cc=x86@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.