1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Corrupt the XSTATE header in a signal frame |
4 | * |
5 | * Based on analysis and a test case from Thomas Gleixner. |
6 | */ |
7 | |
8 | #define _GNU_SOURCE |
9 | |
10 | #include <stdlib.h> |
11 | #include <stdio.h> |
12 | #include <string.h> |
13 | #include <sched.h> |
14 | #include <signal.h> |
15 | #include <err.h> |
16 | #include <unistd.h> |
17 | #include <stdint.h> |
18 | #include <sys/wait.h> |
19 | |
20 | #include "../kselftest.h" /* For __cpuid_count() */ |
21 | |
22 | static inline int xsave_enabled(void) |
23 | { |
24 | unsigned int eax, ebx, ecx, edx; |
25 | |
26 | __cpuid_count(0x1, 0x0, eax, ebx, ecx, edx); |
27 | |
28 | /* Is CR4.OSXSAVE enabled ? */ |
29 | return ecx & (1U << 27); |
30 | } |
31 | |
32 | static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *), |
33 | int flags) |
34 | { |
35 | struct sigaction sa; |
36 | |
37 | memset(&sa, 0, sizeof(sa)); |
38 | sa.sa_sigaction = handler; |
39 | sa.sa_flags = SA_SIGINFO | flags; |
40 | sigemptyset(&sa.sa_mask); |
41 | if (sigaction(sig, &sa, 0)) |
42 | err(1, "sigaction" ); |
43 | } |
44 | |
45 | static void sigusr1(int sig, siginfo_t *info, void *uc_void) |
46 | { |
47 | ucontext_t *uc = uc_void; |
48 | uint8_t *fpstate = (uint8_t *)uc->uc_mcontext.fpregs; |
49 | uint64_t *xfeatures = (uint64_t *)(fpstate + 512); |
50 | |
51 | printf("\tWreck XSTATE header\n" ); |
52 | /* Wreck the first reserved bytes in the header */ |
53 | *(xfeatures + 2) = 0xfffffff; |
54 | } |
55 | |
56 | static void sigsegv(int sig, siginfo_t *info, void *uc_void) |
57 | { |
58 | printf("\tGot SIGSEGV\n" ); |
59 | } |
60 | |
61 | int main(void) |
62 | { |
63 | cpu_set_t set; |
64 | |
65 | sethandler(sig: SIGUSR1, handler: sigusr1, flags: 0); |
66 | sethandler(sig: SIGSEGV, handler: sigsegv, flags: 0); |
67 | |
68 | if (!xsave_enabled()) { |
69 | printf("[SKIP] CR4.OSXSAVE disabled.\n" ); |
70 | return 0; |
71 | } |
72 | |
73 | CPU_ZERO(&set); |
74 | CPU_SET(0, &set); |
75 | |
76 | /* |
77 | * Enforce that the child runs on the same CPU |
78 | * which in turn forces a schedule. |
79 | */ |
80 | sched_setaffinity(getpid(), sizeof(set), &set); |
81 | |
82 | printf("[RUN]\tSend ourselves a signal\n" ); |
83 | raise(SIGUSR1); |
84 | |
85 | printf("[OK]\tBack from the signal. Now schedule.\n" ); |
86 | pid_t child = fork(); |
87 | if (child < 0) |
88 | err(1, "fork" ); |
89 | if (child == 0) |
90 | return 0; |
91 | if (child) |
92 | waitpid(child, NULL, 0); |
93 | printf("[OK]\tBack in the main thread.\n" ); |
94 | |
95 | /* |
96 | * We could try to confirm that extended state is still preserved |
97 | * when we schedule. For now, the only indication of failure is |
98 | * a warning in the kernel logs. |
99 | */ |
100 | |
101 | return 0; |
102 | } |
103 | |