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