1 #include <machine/cpu_capabilities.h>
2 #include <os/thread_self_restrict.h>
3 #include <libkern/OSCacheControl.h>
4 #include <sys/time.h>
5 #include <darwintest.h>
6 #include <sys/types.h>
7 #include <sys/sysctl.h>
8 #include <sys/wait.h>
9 #include <sys/mman.h>
10 #include <ptrauth.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <unistd.h>
14 #include <signal.h>
15
16
17 #define NUM_THREADS 256
18
19 T_GLOBAL_META(T_META_RUN_CONCURRENTLY(true));
20
21
22 #if defined(__arm64__) && defined(__LP64__)
23
24 enum msr_op_reason {
25 msr_get_name, // -> const char *name
26 msr_read, // -> uint64_t val
27 msr_write, // <- uint64_t val
28 };
29
30 typedef void (*msr_op)(enum msr_op_reason why, void *param);
31
32 static pthread_mutex_t threads_please_die_lock = PTHREAD_MUTEX_INITIALIZER;
33 static volatile bool did_fault;
34 static bool threads_please_die;
35
36
37 static void*
thread_func(void * param)38 thread_func(void *param)
39 {
40 bool die;
41
42 (void)param;
43
44 do {
45 pthread_mutex_lock(&threads_please_die_lock);
46 die = threads_please_die;
47 pthread_mutex_unlock(&threads_please_die_lock);
48 } while (!die);
49
50 return NULL;
51 }
52
53 static void
msr_test(msr_op op)54 msr_test(msr_op op)
55 {
56 struct timeval time_start, time_end, time_passed;
57 uint64_t val, new_val, wrote_val;
58 pthread_t threads[NUM_THREADS];
59 bool readable, writeable;
60 const char *reg_name;
61 int i;
62
63 op(msr_get_name, ®_name);
64 T_LOG("sub-test '%s'\n", reg_name);
65
66 //let's see if we can read and write it
67 did_fault = false;
68 op(msr_read, &val);
69 readable = !did_fault;
70
71 did_fault = false;
72 op(msr_write, &val);
73 writeable = !did_fault;
74
75 T_LOG("\tcan read: %s\n", readable ? "YES" : "NO");
76 T_LOG("\tcan write: %s\n", writeable ? "YES" : "NO");
77 if (readable) {
78 T_LOG("\tvalue found: 0x%016llx\n", (unsigned long long)val);
79 }
80
81 if (!readable || !writeable) {
82 T_LOG("\t RW needed for more testing. no further testing will be performed\n");
83 return;
84 }
85
86 //write inverse of what we read and see if the read differs
87 wrote_val = ~val;
88 op(msr_write, &wrote_val);
89 op(msr_read, &new_val);
90
91 if (new_val == val) {
92 T_LOG("\t reg seems to not take writes (0x%016llx). no further testing will be performed\n", val);
93 return;
94 }
95 T_LOG("\twrote 0x%016llx, saw 0x%016llx\n", (unsigned long long)wrote_val, (unsigned long long)new_val);
96 wrote_val = new_val;
97
98 //verify it flips to original value at context switch or otherwise
99 //for context switch to happen, spin up a lot of threads
100 pthread_mutex_lock(&threads_please_die_lock);
101 threads_please_die = false;
102 pthread_mutex_unlock(&threads_please_die_lock);
103 for (i = 0; i < NUM_THREADS; i++) {
104 if (pthread_create(threads + i, NULL, thread_func, NULL)) {
105 T_ASSERT_FAIL("cannot create thread %d\n", i);
106 }
107 }
108
109 gettimeofday(&time_start, NULL);
110 while (1) {
111 op(msr_read, &new_val);
112 if (new_val != wrote_val) {
113 T_LOG("\tvalue reverted to 0x%016llx from 0x%016llx\n", (unsigned long long)new_val, (unsigned long long)wrote_val);
114 break;
115 }
116
117 gettimeofday(&time_end, NULL);
118 timersub(&time_end, &time_start, &time_passed);
119 if (time_passed.tv_sec) { //wait one second at most
120 T_FAIL("\ttoo long for register to be cleared, last read value was 0x%016llx, expected revert to 0x%016llx!", (unsigned long long)new_val, (unsigned long long)val);
121 break;
122 }
123 }
124 pthread_mutex_lock(&threads_please_die_lock);
125 threads_please_die = true;
126 pthread_mutex_unlock(&threads_please_die_lock);
127 for (i = 0; i < NUM_THREADS; i++) {
128 if (pthread_join(threads[i], NULL)) {
129 T_ASSERT_FAIL("cannot join thread %d\n", i);
130 }
131 }
132 }
133
134 static void
msr_test_1(enum msr_op_reason why,void * param)135 msr_test_1(enum msr_op_reason why, void *param)
136 {
137 switch (why) {
138 case msr_get_name:
139 *(const char **)param = "S3_5_C15_C1_4";
140 break;
141
142 case msr_read:
143 *(uint64_t*)param = __builtin_arm_rsr64("S3_5_C15_C1_4");
144 break;
145
146 case msr_write:
147 __builtin_arm_wsr64("S3_5_C15_C1_4", *(const uint64_t*)param);
148 break;
149 }
150 }
151
152 static void
msr_test_2(enum msr_op_reason why,void * param)153 msr_test_2(enum msr_op_reason why, void *param)
154 {
155 switch (why) {
156 case msr_get_name:
157 *(const char **)param = "S3_5_C15_C10_1";
158 break;
159
160 case msr_read:
161 *(uint64_t*)param = __builtin_arm_rsr64("S3_5_C15_C10_1");
162 break;
163
164 case msr_write:
165 __builtin_arm_wsr64("S3_5_C15_C10_1", *(const uint64_t*)param);
166 break;
167 }
168 }
169
170 static void
msr_test_3(enum msr_op_reason why,void * param)171 msr_test_3(enum msr_op_reason why, void *param)
172 {
173 switch (why) {
174 case msr_get_name:
175 *(const char **)param = "TPIDRRO_EL0";
176 break;
177
178 case msr_read:
179 *(uint64_t*)param = __builtin_arm_rsr64("TPIDRRO_EL0");
180 break;
181
182 case msr_write:
183 __builtin_arm_wsr64("TPIDRRO_EL0", *(const uint64_t*)param);
184 break;
185 }
186 }
187
188 static void
msr_test_4(enum msr_op_reason why,void * param)189 msr_test_4(enum msr_op_reason why, void *param)
190 {
191 switch (why) {
192 case msr_get_name:
193 *(const char **)param = "TPIDR_EL1";
194 break;
195
196 case msr_read:
197 *(uint64_t*)param = __builtin_arm_rsr64("TPIDR_EL1");
198 break;
199
200 case msr_write:
201 __builtin_arm_wsr64("TPIDR_EL1", *(const uint64_t*)param);
202 break;
203 }
204 }
205
206 static void
sig_caught(int signo,siginfo_t * sinfop,void * ucontext)207 sig_caught(int signo, siginfo_t *sinfop, void *ucontext)
208 {
209 _STRUCT_MCONTEXT64 *ctx = ((ucontext_t *)ucontext)->uc_mcontext;
210 void (*pc)(void);
211 uint32_t instr;
212
213 (void)sinfop;
214
215 if (signo != SIGILL) {
216 T_ASSERT_FAIL("We did not expect signal %d", signo);
217 }
218
219 pc = (void (*)(void))__darwin_arm_thread_state64_get_pc(ctx->__ss);
220 instr = *(uint32_t*)pc;
221
222 if ((instr & 0xffd00000) != 0xd5100000) {
223 T_ASSERT_FAIL("We did not expect SIGILL on an instr that is not an MSR/MRS");
224 }
225
226 pc = (void (*)(void))(((uintptr_t)pc) + 4);
227 pc = ptrauth_sign_unauthenticated(pc, ptrauth_key_function_pointer, 0);
228
229 did_fault = true;
230
231 // skip the instruction
232 __darwin_arm_thread_state64_set_pc_fptr(ctx->__ss, pc);
233 }
234
235 static bool
is_release_kernel(void)236 is_release_kernel(void)
237 {
238 /*
239 * I apologize to anyone reading this code!! I promise you that I felt
240 * as dirty writing this as you do reading this. I asked in the proper
241 * channels, but nobody had a good idea how to detect a release kernel
242 * from userspace. Sadly, we need that here as the mitigations at hand
243 * are only applied in RELEASE builds. Again: I am sorry.
244 */
245 char ver_str[1024] = {};
246 size_t len = sizeof(ver_str) - 1;
247
248 (void)sysctlbyname("kern.version", ver_str, &len, NULL, 0);
249
250 return !!strstr(ver_str, "/RELEASE_ARM64");
251 }
252
253 #endif // defined(__arm64__) && defined(__LP64__)
254
255 T_DECL(user_msrs, "Userspace MSR access test")
256 {
257 #if defined(__arm64__) && defined(__LP64__)
258 if (is_release_kernel()) {
259 struct sigaction sa_old, sa_new = {.__sigaction_u = { .__sa_sigaction = sig_caught, }, .sa_flags = SA_SIGINFO, };
260
261 sigaction(SIGILL, &sa_new, &sa_old);
262 msr_test(msr_test_1);
263 msr_test(msr_test_2);
264 msr_test(msr_test_3);
265 msr_test(msr_test_4);
266 //todo: macro autogen all of them
267 sigaction(SIGILL, &sa_old, NULL);
268
269 T_PASS("Userspace MSR access test passed");
270 } else {
271 T_PASS("Userspace MSR access test only runs on release kernels");
272 }
273 #else // defined(__arm64__) && defined(__LP64__)
274 T_SKIP("userspace MSR access test skipped - not ARM64.64");
275 #endif // defined(__arm64__) && defined(__LP64__)
276 }
277