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(stack: stack32 + 4088, function: 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 | |