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