Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

selftests/x86: Test signal frame XSTATE header corruption handling

This is very heavily based on some code from Thomas Gleixner. On a system
without XSAVES, it triggers the WARN_ON():

Bad FPU state detected at copy_kernel_to_fpregs+0x2f/0x40, reinitializing FPU registers.

[ bp: Massage in nitpicks. ]

Signed-off-by: Andy Lutomirski <luto@kernel.org>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Borislav Petkov <bp@suse.de>
Acked-by: Dave Hansen <dave.hansen@linux.intel.com>
Acked-by: Rik van Riel <riel@surriel.com>
Link: https://lkml.kernel.org/r/20210608144346.234764986@linutronix.de

authored by

Andy Lutomirski and committed by
Borislav Petkov
b7c11876 f72a249b

+116 -1
+2 -1
tools/testing/selftests/x86/Makefile
··· 17 17 TARGETS_C_32BIT_ONLY := entry_from_vm86 test_syscall_vdso unwind_vdso \ 18 18 test_FCMOV test_FCOMI test_FISTTP \ 19 19 vdso_restorer 20 - TARGETS_C_64BIT_ONLY := fsgsbase sysret_rip syscall_numbering 20 + TARGETS_C_64BIT_ONLY := fsgsbase sysret_rip syscall_numbering \ 21 + corrupt_xstate_header 21 22 # Some selftests require 32bit support enabled also on 64bit systems 22 23 TARGETS_C_32BIT_NEEDED := ldt_gdt ptrace_syscall 23 24
+114
tools/testing/selftests/x86/corrupt_xstate_header.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * Corrupt the XSTATE header in a signal frame 4 + * 5 + * Based on analysis and a test case from Thomas Gleixner. 6 + */ 7 + 8 + #define _GNU_SOURCE 9 + 10 + #include <stdlib.h> 11 + #include <stdio.h> 12 + #include <string.h> 13 + #include <sched.h> 14 + #include <signal.h> 15 + #include <err.h> 16 + #include <unistd.h> 17 + #include <stdint.h> 18 + #include <sys/wait.h> 19 + 20 + static inline void __cpuid(unsigned int *eax, unsigned int *ebx, 21 + unsigned int *ecx, unsigned int *edx) 22 + { 23 + asm volatile( 24 + "cpuid;" 25 + : "=a" (*eax), 26 + "=b" (*ebx), 27 + "=c" (*ecx), 28 + "=d" (*edx) 29 + : "0" (*eax), "2" (*ecx)); 30 + } 31 + 32 + static inline int xsave_enabled(void) 33 + { 34 + unsigned int eax, ebx, ecx, edx; 35 + 36 + eax = 0x1; 37 + ecx = 0x0; 38 + __cpuid(&eax, &ebx, &ecx, &edx); 39 + 40 + /* Is CR4.OSXSAVE enabled ? */ 41 + return ecx & (1U << 27); 42 + } 43 + 44 + static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *), 45 + int flags) 46 + { 47 + struct sigaction sa; 48 + 49 + memset(&sa, 0, sizeof(sa)); 50 + sa.sa_sigaction = handler; 51 + sa.sa_flags = SA_SIGINFO | flags; 52 + sigemptyset(&sa.sa_mask); 53 + if (sigaction(sig, &sa, 0)) 54 + err(1, "sigaction"); 55 + } 56 + 57 + static void sigusr1(int sig, siginfo_t *info, void *uc_void) 58 + { 59 + ucontext_t *uc = uc_void; 60 + uint8_t *fpstate = (uint8_t *)uc->uc_mcontext.fpregs; 61 + uint64_t *xfeatures = (uint64_t *)(fpstate + 512); 62 + 63 + printf("\tWreck XSTATE header\n"); 64 + /* Wreck the first reserved bytes in the header */ 65 + *(xfeatures + 2) = 0xfffffff; 66 + } 67 + 68 + static void sigsegv(int sig, siginfo_t *info, void *uc_void) 69 + { 70 + printf("\tGot SIGSEGV\n"); 71 + } 72 + 73 + int main(void) 74 + { 75 + cpu_set_t set; 76 + 77 + sethandler(SIGUSR1, sigusr1, 0); 78 + sethandler(SIGSEGV, sigsegv, 0); 79 + 80 + if (!xsave_enabled()) { 81 + printf("[SKIP] CR4.OSXSAVE disabled.\n"); 82 + return 0; 83 + } 84 + 85 + CPU_ZERO(&set); 86 + CPU_SET(0, &set); 87 + 88 + /* 89 + * Enforce that the child runs on the same CPU 90 + * which in turn forces a schedule. 91 + */ 92 + sched_setaffinity(getpid(), sizeof(set), &set); 93 + 94 + printf("[RUN]\tSend ourselves a signal\n"); 95 + raise(SIGUSR1); 96 + 97 + printf("[OK]\tBack from the signal. Now schedule.\n"); 98 + pid_t child = fork(); 99 + if (child < 0) 100 + err(1, "fork"); 101 + if (child == 0) 102 + return 0; 103 + if (child) 104 + waitpid(child, NULL, 0); 105 + printf("[OK]\tBack in the main thread.\n"); 106 + 107 + /* 108 + * We could try to confirm that extended state is still preserved 109 + * when we schedule. For now, the only indication of failure is 110 + * a warning in the kernel logs. 111 + */ 112 + 113 + return 0; 114 + }