From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-oo1-f74.google.com (mail-oo1-f74.google.com [209.85.161.74]) (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 79A8D4949E9 for ; Thu, 4 Jun 2026 16:56:10 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.161.74 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780592174; cv=none; b=ouXGQwUgfaLZ03dqXYII3WtsKvlb/QQ8kbsOdwWKxzInAkq9UlsBvR9LingjfIXa8nd6D6hD530WJ5EryxB+LnR3j6kK3U4Uq/8ddjLes9L8jorcOLK6LysZuPAkb6t2oE/M5E9Gp1mdJTAwov/jqVetWJekXiquEEnFq0R8VG0= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780592174; c=relaxed/simple; bh=ekigQyV/jQI92pR236pyinuAEASB8wnGlmIyLrBVUmg=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=q7yUc+UDiet6n3FJxAFf+aBRIr4FgXBXXXKI3QSfFdraVbFR1FLbWS+GLWmpbGRvwsMeb7v+0WiLC/BHEC8az/UfXvUny8gNU2WIEYxGfyEcSfBl3KqW/NVMh47NFkskMX52yjY4PKbFzj3Il2wSHC/U1iyLu8Ym4j6TbJ+doDg= 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=Z+hJwEqx; arc=none smtp.client-ip=209.85.161.74 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="Z+hJwEqx" Received: by mail-oo1-f74.google.com with SMTP id 006d021491bc7-69da719e003so1306421eaf.2 for ; Thu, 04 Jun 2026 09:56:10 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1780592169; x=1781196969; darn=lists.linux.dev; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=KPEnqE/zE0ic5HdWxS6IoJSQzA2QtUrz99Wvcf3heHQ=; b=Z+hJwEqxyHSDfhDD9Rp95MB+fhzBtYtHkgP8DuSBPvEKQBsQDLiftumLpM0MUcudL6 ao5X26vHrsEVBW3c7u/9sv698o51MtgMCwepnKkTUKdFnnVBHHYmkgLxzd4i7w3MXNmj sAFR3Vn9J+3P45rCvAWvo0XgKqxOV9/0vsshQxtqDe+MrbDgLmDTEf+W8wRuLf6pfvly Wc1k0O3OSDna9GqrNESrEX66iJSdY5Tr5ozQ8eCsP6T2ajGZWAnKfszzQCD4KW5xl5Sc jK6WFpXkTWRWXF0pVBdrlL8dYQpFab6A7GT6sUelRHXfo4gjxg1xtbATCdusDsjEyenl kM3w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1780592169; x=1781196969; h=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; bh=KPEnqE/zE0ic5HdWxS6IoJSQzA2QtUrz99Wvcf3heHQ=; b=ft5AMqpp/QV8TC/jCw5LkGOVSqc9w2DcxPGnXxZ6a8GEyfpaXZIKc8GQVeq1UDKT+c CX9jX5UagV6dN8bcFlfrUdeg6CD5GPuSJ1xmzMXbX6b08ooROdIMEKaf5CqYqTVe681j 5BnG8u1nJStlN+rmnuw9Xb/SDMYENcmDUUXIhtGFGH7yNPexOO+KnSgYD+qTFNwblO6L M4FMSPE7+39mte59Fe7qfA7vnYmNXpnOyeWj1GJqfhPmGqPN82zqu5sVFxJX1Vxr67Jh uBlYrMWHkgJjgFL9LPpIbCVMMqVPuyM97JyINf+iK/7koglruksiPGGTxzcADi3rHIiQ fmaw== X-Forwarded-Encrypted: i=1; AFNElJ+43uqPid5RzWZn9Cp7dKE665w4F2OVDwKXqwJDYi5wYSvpysLVwei0c6365MGV5472IvXt@lists.linux.dev X-Gm-Message-State: AOJu0YyD+BEt+DQTvKx+RmdyDfvRdhbuX4WHK46K0zFygAhLgnW+mCR+ FH/NUr6f/ghaZrNaKXMtgOzSbKQYCowwsVLzY590dm7oeZTJar5GN/J1yxGjm8HYSAM+zeEheU9 VQIp8BA== X-Received: from iodf20.prod.google.com ([2002:a05:6602:62d4:b0:963:c1c8:da0e]) (user=avagin job=prod-delivery.src-stubby-dispatcher) by 2002:a4a:d017:0:b0:694:8c67:5cb6 with SMTP id 006d021491bc7-69e48093670mr4111052eaf.41.1780592169187; Thu, 04 Jun 2026 09:56:09 -0700 (PDT) Date: Thu, 4 Jun 2026 16:56:02 +0000 In-Reply-To: <20260604165604.1195243-1-avagin@google.com> Precedence: bulk X-Mailing-List: criu@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260604165604.1195243-1-avagin@google.com> X-Mailer: git-send-email 2.54.0.1032.g2f8565e1d1-goog Message-ID: <20260604165604.1195243-3-avagin@google.com> Subject: [PATCH 2/4] selftests/x86: Add a test for signal frame 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 , "H. Peter Anvin" Content-Type: text/plain; charset="UTF-8" Add a new selftest tools/testing/selftests/x86/sigframe_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 --- tools/testing/selftests/x86/Makefile | 5 +- .../selftests/x86/sigframe_portability.c | 156 ++++++++++++++++++ tools/testing/selftests/x86/xstate.c | 5 - tools/testing/selftests/x86/xstate.h | 12 ++ 4 files changed, 172 insertions(+), 6 deletions(-) create mode 100644 tools/testing/selftests/x86/sigframe_portability.c diff --git a/tools/testing/selftests/x86/Makefile b/tools/testing/selftests/x86/Makefile index 434065215d12..bd59ee3df61d 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_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_portability_64: CFLAGS += -mno-avx -mno-avx512f diff --git a/tools/testing/selftests/x86/sigframe_portability.c b/tools/testing/selftests/x86/sigframe_portability.c new file mode 100644 index 000000000000..54ba182a792f --- /dev/null +++ b/tools/testing/selftests/x86/sigframe_portability.c @@ -0,0 +1,156 @@ +// 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" + +/* + * This test verifies the portability of the signal frame. + * It simulates a scenario where a signal frame is created on a system with + * fewer xstate features and restored on a system with more features. + */ + +#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_signal(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); +} + +int main(void) +{ + uint64_t v[4] = {0, 0, 0, 0}; + + ksft_print_header(); + ksft_set_plan(1); + + check_avx_support(); + + sethandler(SIGUSR1, handle_signal, 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\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); + + 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 +#include +#include #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.1032.g2f8565e1d1-goog