105 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * sysret_ss_attrs.c - test that syscalls return valid hidden SS attributes4 * Copyright (c) 2015 Andrew Lutomirski5 *6 * On AMD CPUs, SYSRET can return with a valid SS descriptor with with7 * the hidden attributes set to an unusable state. Make sure the kernel8 * doesn't let this happen.9 */10 11#define _GNU_SOURCE12 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 23static void *threadproc(void *ctx)24{25 /*26 * Do our best to cause sleeps on this CPU to exit the kernel and27 * re-enter with SS = 0.28 */29 while (true)30 ;31 32 return NULL;33}34 35#ifdef __x86_64__36extern unsigned long call32_from_64(void *stack, void (*function)(void));37 38asm (".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");45extern void test_ss(void);46#endif47 48int 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 will53 * 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#endif72 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-bit78 * 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 the80 * kernel needs some kind of workaround to make sure that we81 * end the system call with a valid stack segment. This82 * 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 enough90 * 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#endif95 }96 97 printf("[OK]\tWe survived\n");98 99#ifdef __x86_64__100 munmap(stack32, 4096);101#endif102 103 return 0;104}105