162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * sysret_ss_attrs.c - test that syscalls return valid hidden SS attributes 462306a36Sopenharmony_ci * Copyright (c) 2015 Andrew Lutomirski 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * On AMD CPUs, SYSRET can return with a valid SS descriptor with with 762306a36Sopenharmony_ci * the hidden attributes set to an unusable state. Make sure the kernel 862306a36Sopenharmony_ci * doesn't let this happen. 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#define _GNU_SOURCE 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <stdlib.h> 1462306a36Sopenharmony_ci#include <unistd.h> 1562306a36Sopenharmony_ci#include <stdio.h> 1662306a36Sopenharmony_ci#include <string.h> 1762306a36Sopenharmony_ci#include <sys/mman.h> 1862306a36Sopenharmony_ci#include <err.h> 1962306a36Sopenharmony_ci#include <stddef.h> 2062306a36Sopenharmony_ci#include <stdbool.h> 2162306a36Sopenharmony_ci#include <pthread.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_cistatic void *threadproc(void *ctx) 2462306a36Sopenharmony_ci{ 2562306a36Sopenharmony_ci /* 2662306a36Sopenharmony_ci * Do our best to cause sleeps on this CPU to exit the kernel and 2762306a36Sopenharmony_ci * re-enter with SS = 0. 2862306a36Sopenharmony_ci */ 2962306a36Sopenharmony_ci while (true) 3062306a36Sopenharmony_ci ; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci return NULL; 3362306a36Sopenharmony_ci} 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#ifdef __x86_64__ 3662306a36Sopenharmony_ciextern unsigned long call32_from_64(void *stack, void (*function)(void)); 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ciasm (".pushsection .text\n\t" 3962306a36Sopenharmony_ci ".code32\n\t" 4062306a36Sopenharmony_ci "test_ss:\n\t" 4162306a36Sopenharmony_ci "pushl $0\n\t" 4262306a36Sopenharmony_ci "popl %eax\n\t" 4362306a36Sopenharmony_ci "ret\n\t" 4462306a36Sopenharmony_ci ".code64"); 4562306a36Sopenharmony_ciextern void test_ss(void); 4662306a36Sopenharmony_ci#endif 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ciint main() 4962306a36Sopenharmony_ci{ 5062306a36Sopenharmony_ci /* 5162306a36Sopenharmony_ci * Start a busy-looping thread on the same CPU we're on. 5262306a36Sopenharmony_ci * For simplicity, just stick everything to CPU 0. This will 5362306a36Sopenharmony_ci * fail in some containers, but that's probably okay. 5462306a36Sopenharmony_ci */ 5562306a36Sopenharmony_ci cpu_set_t cpuset; 5662306a36Sopenharmony_ci CPU_ZERO(&cpuset); 5762306a36Sopenharmony_ci CPU_SET(0, &cpuset); 5862306a36Sopenharmony_ci if (sched_setaffinity(0, sizeof(cpuset), &cpuset) != 0) 5962306a36Sopenharmony_ci printf("[WARN]\tsched_setaffinity failed\n"); 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci pthread_t thread; 6262306a36Sopenharmony_ci if (pthread_create(&thread, 0, threadproc, 0) != 0) 6362306a36Sopenharmony_ci err(1, "pthread_create"); 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci#ifdef __x86_64__ 6662306a36Sopenharmony_ci unsigned char *stack32 = mmap(NULL, 4096, PROT_READ | PROT_WRITE, 6762306a36Sopenharmony_ci MAP_32BIT | MAP_ANONYMOUS | MAP_PRIVATE, 6862306a36Sopenharmony_ci -1, 0); 6962306a36Sopenharmony_ci if (stack32 == MAP_FAILED) 7062306a36Sopenharmony_ci err(1, "mmap"); 7162306a36Sopenharmony_ci#endif 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci printf("[RUN]\tSyscalls followed by SS validation\n"); 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci for (int i = 0; i < 1000; i++) { 7662306a36Sopenharmony_ci /* 7762306a36Sopenharmony_ci * Go to sleep and return using sysret (if we're 64-bit 7862306a36Sopenharmony_ci * or we're 32-bit on AMD on a 64-bit kernel). On AMD CPUs, 7962306a36Sopenharmony_ci * SYSRET doesn't fix up the cached SS descriptor, so the 8062306a36Sopenharmony_ci * kernel needs some kind of workaround to make sure that we 8162306a36Sopenharmony_ci * end the system call with a valid stack segment. This 8262306a36Sopenharmony_ci * can be a confusing failure because the SS *selector* 8362306a36Sopenharmony_ci * is the same regardless. 8462306a36Sopenharmony_ci */ 8562306a36Sopenharmony_ci usleep(2); 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci#ifdef __x86_64__ 8862306a36Sopenharmony_ci /* 8962306a36Sopenharmony_ci * On 32-bit, just doing a syscall through glibc is enough 9062306a36Sopenharmony_ci * to cause a crash if our cached SS descriptor is invalid. 9162306a36Sopenharmony_ci * On 64-bit, it's not, so try extra hard. 9262306a36Sopenharmony_ci */ 9362306a36Sopenharmony_ci call32_from_64(stack32 + 4088, test_ss); 9462306a36Sopenharmony_ci#endif 9562306a36Sopenharmony_ci } 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci printf("[OK]\tWe survived\n"); 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci#ifdef __x86_64__ 10062306a36Sopenharmony_ci munmap(stack32, 4096); 10162306a36Sopenharmony_ci#endif 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci return 0; 10462306a36Sopenharmony_ci} 105