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