1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * sysret_ss_attrs.c - test that syscalls return valid hidden SS attributes 4 * Copyright (c) 2015 Andrew Lutomirski 5 * 6 * On AMD CPUs, SYSRET can return with a valid SS descriptor with with 7 * the hidden attributes set to an unusable state. Make sure the kernel 8 * doesn't let this happen. 9 */ 10 11 #define _GNU_SOURCE 12 13 #include <stdlib.h> 14 #include <unistd.h> 15 #include <stdio.h> 16 #include <string.h> 17 #include <sys/mman.h> 18 #include <err.h> 19 #include <stddef.h> 20 #include <stdbool.h> 21 #include <pthread.h> 22 23 static void *threadproc(void *ctx) 24 { 25 /* 26 * Do our best to cause sleeps on this CPU to exit the kernel and 27 * re-enter with SS = 0. 28 */ 29 while (true) 30 ; 31 32 return NULL; 33 } 34 35 #ifdef __x86_64__ 36 extern unsigned long call32_from_64(void *stack, void (*function)(void)); 37 38 asm (".pushsection .text\n\t" 39 ".code32\n\t" 40 "test_ss:\n\t" 41 "pushl $0\n\t" 42 "popl %eax\n\t" 43 "ret\n\t" 44 ".code64"); 45 extern void test_ss(void); 46 #endif 47 48 int main() 49 { 50 /* 51 * Start a busy-looping thread on the same CPU we're on. 52 * For simplicity, just stick everything to CPU 0. This will 53 * fail in some containers, but that's probably okay. 54 */ 55 cpu_set_t cpuset; 56 CPU_ZERO(&cpuset); 57 CPU_SET(0, &cpuset); 58 if (sched_setaffinity(0, sizeof(cpuset), &cpuset) != 0) 59 printf("[WARN]\tsched_setaffinity failed\n"); 60 61 pthread_t thread; 62 if (pthread_create(&thread, 0, threadproc, 0) != 0) 63 err(1, "pthread_create"); 64 65 #ifdef __x86_64__ 66 unsigned char *stack32 = mmap(NULL, 4096, PROT_READ | PROT_WRITE, 67 MAP_32BIT | MAP_ANONYMOUS | MAP_PRIVATE, 68 -1, 0); 69 if (stack32 == MAP_FAILED) 70 err(1, "mmap"); 71 #endif 72 73 printf("[RUN]\tSyscalls followed by SS validation\n"); 74 75 for (int i = 0; i < 1000; i++) { 76 /* 77 * Go to sleep and return using sysret (if we're 64-bit 78 * or we're 32-bit on AMD on a 64-bit kernel). On AMD CPUs, 79 * SYSRET doesn't fix up the cached SS descriptor, so the 80 * kernel needs some kind of workaround to make sure that we 81 * end the system call with a valid stack segment. This 82 * can be a confusing failure because the SS *selector* 83 * is the same regardless. 84 */ 85 usleep(2); 86 87 #ifdef __x86_64__ 88 /* 89 * On 32-bit, just doing a syscall through glibc is enough 90 * to cause a crash if our cached SS descriptor is invalid. 91 * On 64-bit, it's not, so try extra hard. 92 */ 93 call32_from_64(stack32 + 4088, test_ss); 94 #endif 95 } 96 97 printf("[OK]\tWe survived\n"); 98 99 #ifdef __x86_64__ 100 munmap(stack32, 4096); 101 #endif 102 103 return 0; 104 } 105