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 } 115