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