xref: /xnu-8792.61.2/tests/imm_pinned_control_port_crasher.c (revision 42e220869062b56f8d7d0726fd4c88954f87902c)
1 #include <mach/mach.h>
2 #include <stdlib.h>
3 #include <pthread.h>
4 #include <unistd.h>
5 #include <stdio.h>
6 #include <assert.h>
7 #include <mach/task.h>
8 #include <mach/mk_timer.h>
9 
10 /*
11  * DO NOT run this test file by itself.
12  * This test is meant to be invoked by control_port_options darwintest.
13  *
14  * If hard enforcement for pinned control port is on, pinned tests are
15  * expected to generate fatal EXC_GUARD.
16  *
17  * If hard enforcement for immovable control port is on, immovable tests are
18  * expected to generate fatal EXC_GUARD.
19  *
20  * The type of exception raised (if any) is checked on control_port_options side.
21  */
22 #define MAX_TEST_NUM 21
23 
24 #ifndef MACH64_SEND_ANY
25 #define MACH64_SEND_ANY 0x0000000800000000ull
26 #define MACH64_SEND_MQ_CALL 0x0000000400000000ull
27 #endif
28 
29 static int
attempt_send_immovable_port(mach_port_name_t port,mach_msg_type_name_t disp)30 attempt_send_immovable_port(mach_port_name_t port, mach_msg_type_name_t disp)
31 {
32 	mach_port_t server;
33 	kern_return_t kr;
34 	kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &server);
35 	assert(kr == 0);
36 
37 	kr = mach_port_insert_right(mach_task_self(), server, server, MACH_MSG_TYPE_MAKE_SEND);
38 	assert(kr == 0);
39 
40 	struct {
41 		mach_msg_header_t header;
42 		mach_msg_body_t body;
43 		mach_msg_port_descriptor_t desc;
44 	} msg;
45 
46 	msg.header.msgh_remote_port = server;
47 	msg.header.msgh_local_port = MACH_PORT_NULL;
48 	msg.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0) | MACH_MSGH_BITS_COMPLEX;
49 	msg.header.msgh_size = sizeof msg;
50 
51 	msg.body.msgh_descriptor_count = 1;
52 
53 	msg.desc.name = port;
54 	msg.desc.disposition = disp;
55 	msg.desc.type = MACH_MSG_PORT_DESCRIPTOR;
56 
57 	return mach_msg_send(&msg.header);
58 }
59 
60 static void
pinned_test_main_thread_mod_ref(void)61 pinned_test_main_thread_mod_ref(void)
62 {
63 	printf("[Crasher]: Mod refs main thread's self port to 0\n");
64 	mach_port_t thread_self = mach_thread_self();
65 	kern_return_t kr = mach_port_mod_refs(mach_task_self(), thread_self, MACH_PORT_RIGHT_SEND, -2);
66 
67 	printf("[Crasher pinned_test_main_thread_mod_ref] mach_port_mod_refs returned %s \n.", mach_error_string(kr));
68 }
69 
70 static void*
pthread_run(void)71 pthread_run(void)
72 {
73 	printf("[Crasher]: Deallocate pthread_self\n");
74 	mach_port_t th_self = pthread_mach_thread_np(pthread_self());
75 	kern_return_t kr = mach_port_deallocate(mach_task_self(), th_self);
76 
77 	printf("[Crasher pinned_test_pthread_dealloc] mach_port_deallocate returned %s \n.", mach_error_string(kr));
78 	return NULL;
79 }
80 
81 static void
pinned_test_pthread_dealloc(void)82 pinned_test_pthread_dealloc(void)
83 {
84 	printf("[Crasher]: Create a pthread and deallocate its self port\n");
85 	pthread_t thread;
86 	int ret = pthread_create(&thread, NULL, pthread_run, NULL);
87 	assert(ret == 0);
88 	ret = pthread_join(thread, NULL);
89 	assert(ret == 0);
90 }
91 
92 static void
pinned_test_task_self_dealloc(void)93 pinned_test_task_self_dealloc(void)
94 {
95 	printf("[Crasher]: Deallocate mach_task_self twice\n");
96 	mach_port_t task_self = mach_task_self();
97 	kern_return_t kr = mach_port_deallocate(task_self, task_self);
98 	assert(kr == 0);
99 	kr = mach_port_deallocate(task_self, task_self);
100 
101 	printf("[Crasher pinned_test_task_self_dealloc] mach_port_deallocate returned %s \n.", mach_error_string(kr));
102 }
103 
104 static void
pinned_test_task_self_mod_ref(void)105 pinned_test_task_self_mod_ref(void)
106 {
107 	printf("[Crasher]: Mod refs mach_task_self() to 0\n");
108 	kern_return_t kr = mach_port_mod_refs(mach_task_self(), mach_task_self(), MACH_PORT_RIGHT_SEND, -2);
109 
110 	printf("[Crasher pinned_test_task_self_mod_ref] mach_port_mod_refs returned %s \n.", mach_error_string(kr));
111 }
112 
113 static void
pinned_test_task_threads_mod_ref(void)114 pinned_test_task_threads_mod_ref(void)
115 {
116 	printf("[Crasher]: task_threads should return pinned thread ports. Mod refs them to 0\n");
117 	thread_array_t th_list;
118 	mach_msg_type_number_t th_cnt;
119 	kern_return_t kr;
120 	mach_port_t th_kp = mach_thread_self();
121 	mach_port_deallocate(mach_task_self(), th_kp);
122 
123 	kr = task_threads(mach_task_self(), &th_list, &th_cnt);
124 	mach_port_deallocate(mach_task_self(), th_list[0]);
125 
126 	kr = mach_port_mod_refs(mach_task_self(), th_list[0], MACH_PORT_RIGHT_SEND, -1);
127 
128 	printf("[Crasher pinned_test_task_threads_mod_ref] mach_port_mod_refs returned %s \n.", mach_error_string(kr));
129 }
130 
131 static void
pinned_test_mach_port_destroy(void)132 pinned_test_mach_port_destroy(void)
133 {
134 	kern_return_t kr = mach_port_destroy(mach_task_self(), mach_task_self());
135 	printf("[Crasher pinned_test_mach_port_destroy] mach_port_destroy returned %s \n.", mach_error_string(kr));
136 }
137 
138 static void
pinned_test_move_send_as_remote_port(void)139 pinned_test_move_send_as_remote_port(void)
140 {
141 	struct {
142 		mach_msg_header_t header;
143 	} msg;
144 
145 	kern_return_t kr = mach_port_deallocate(mach_task_self(), mach_task_self());
146 	assert(kr == 0);
147 
148 	/*
149 	 * We allow move send on remote kobject port but this should trip on pinning on last ref.
150 	 * See: IPC_OBJECT_COPYIN_FLAGS_ALLOW_IMMOVABLE_SEND.
151 	 */
152 	msg.header.msgh_remote_port = mach_task_self();
153 	msg.header.msgh_local_port = MACH_PORT_NULL;
154 	msg.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MOVE_SEND, 0);
155 	msg.header.msgh_id = 2000;
156 	msg.header.msgh_size = sizeof msg;
157 
158 	kr = mach_msg_send(&msg.header);
159 
160 	printf("[Crasher pinned_test_move_send_as_remote_port] mach_msg_send returned %s \n.", mach_error_string(kr));
161 }
162 
163 static void
immovable_test_move_send_as_remote_port(void)164 immovable_test_move_send_as_remote_port(void)
165 {
166 	struct {
167 		mach_msg_header_t header;
168 	} msg;
169 
170 	/* Local port cannot be immovable. See: ipc_right_copyin_check_reply() */
171 	msg.header.msgh_remote_port = mach_task_self();
172 	msg.header.msgh_local_port = mach_task_self();
173 	msg.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MOVE_SEND, MACH_MSG_TYPE_MOVE_SEND);
174 	msg.header.msgh_id = 2000;
175 	msg.header.msgh_size = sizeof msg;
176 
177 	kern_return_t kr = mach_msg_send(&msg.header);
178 
179 	printf("[Crasher immovable_test_move_send_as_remote_port] mach_msg_send returned %s \n.", mach_error_string(kr));
180 }
181 
182 static void
immovable_test_move_send_task_self(void)183 immovable_test_move_send_task_self(void)
184 {
185 	kern_return_t kr;
186 	printf("[Crasher]: Move send mach_task_self_\n");
187 	kr = attempt_send_immovable_port(mach_task_self(), MACH_MSG_TYPE_MOVE_SEND);
188 
189 	printf("[Crasher immovable_test_move_send_task_self] attempt_send_immovable_port returned %s \n.", mach_error_string(kr));
190 }
191 
192 static void
immovable_test_copy_send_task_self(void)193 immovable_test_copy_send_task_self(void)
194 {
195 	kern_return_t kr;
196 	printf("[Crasher]: Copy send mach_task_self_\n");
197 	kr = attempt_send_immovable_port(mach_task_self(), MACH_MSG_TYPE_COPY_SEND);
198 
199 	printf("[Crasher immovable_test_copy_send_task_self] attempt_send_immovable_port returned %s \n.", mach_error_string(kr));
200 }
201 
202 static void
immovable_test_move_send_thread_self(void)203 immovable_test_move_send_thread_self(void)
204 {
205 	kern_return_t kr;
206 	printf("[Crasher]: Move send main thread's self port\n");
207 	kr = attempt_send_immovable_port(mach_thread_self(), MACH_MSG_TYPE_MOVE_SEND);
208 
209 	printf("[Crasher immovable_test_move_send_thread_self] attempt_send_immovable_port returned %s \n.", mach_error_string(kr));
210 }
211 
212 static void
immovable_test_copy_send_thread_self(void)213 immovable_test_copy_send_thread_self(void)
214 {
215 	kern_return_t kr;
216 	mach_port_t port;
217 	printf("[Crasher]: Copy send main thread's self port\n");
218 	port = mach_thread_self();
219 	kr = attempt_send_immovable_port(port, MACH_MSG_TYPE_COPY_SEND);
220 	printf("[Crasher immovable_test_copy_send_thread_self] attempt_send_immovable_port returned %s \n.", mach_error_string(kr));
221 
222 	mach_port_deallocate(mach_task_self(), port);
223 }
224 
225 static void
immovable_test_copy_send_task_read(void)226 immovable_test_copy_send_task_read(void)
227 {
228 	kern_return_t kr;
229 	mach_port_t port;
230 	printf("[Crasher]: Copy send task read port\n");
231 	kr = task_get_special_port(mach_task_self(), TASK_READ_PORT, &port);
232 	assert(kr == 0);
233 	kr = attempt_send_immovable_port(port, MACH_MSG_TYPE_COPY_SEND);
234 	printf("[Crasher immovable_test_copy_send_task_read] attempt_send_immovable_port returned %s \n.", mach_error_string(kr));
235 
236 	mach_port_deallocate(mach_task_self(), port);
237 }
238 
239 static void
immovable_test_copy_send_task_inspect(void)240 immovable_test_copy_send_task_inspect(void)
241 {
242 	kern_return_t kr;
243 	mach_port_t port;
244 	printf("[Crasher]: Move send task inspect port\n");
245 	kr = task_get_special_port(mach_task_self(), TASK_INSPECT_PORT, &port);
246 	assert(kr == 0);
247 	kr = attempt_send_immovable_port(port, MACH_MSG_TYPE_MOVE_SEND);
248 	printf("[Crasher immovable_test_copy_send_task_inspect] attempt_send_immovable_port returned %s \n.", mach_error_string(kr));
249 }
250 
251 static void
immovable_test_move_send_thread_inspect(void)252 immovable_test_move_send_thread_inspect(void)
253 {
254 	kern_return_t kr;
255 	mach_port_t port;
256 	mach_port_t th_port = mach_thread_self();
257 
258 	printf("[Crasher]: Move send thread inspect port\n");
259 	kr = thread_get_special_port(th_port, THREAD_INSPECT_PORT, &port);
260 	assert(kr == 0);
261 	kr = attempt_send_immovable_port(port, MACH_MSG_TYPE_MOVE_SEND);
262 	printf("[Crasher immovable_test_move_send_thread_inspect] attempt_send_immovable_port returned %s \n.", mach_error_string(kr));
263 
264 	mach_port_deallocate(mach_task_self(), th_port);
265 }
266 
267 static void
immovable_test_move_send_raw_thread(void)268 immovable_test_move_send_raw_thread(void)
269 {
270 	kern_return_t kr;
271 	mach_port_t port;
272 
273 	kr = thread_create(mach_task_self(), &port);
274 	assert(kr == 0);
275 	kr = mach_port_deallocate(mach_task_self(), port); /* not pinned, should not crash */
276 
277 	kr = thread_create(mach_task_self(), &port);
278 	assert(kr == 0);
279 	kr = attempt_send_immovable_port(port, MACH_MSG_TYPE_MOVE_SEND); /* immovable, should crash here */
280 	printf("[Crasher immovable_test_move_send_raw_thread] attempt_send_immovable_port returned %s \n.", mach_error_string(kr));
281 
282 	kr = thread_terminate(port);
283 	assert(kr == 0);
284 }
285 
286 static void
immovable_test_copy_send_thread_read(void)287 immovable_test_copy_send_thread_read(void)
288 {
289 	kern_return_t kr;
290 	mach_port_t port;
291 	mach_port_t th_port = mach_thread_self();
292 
293 	printf("[Crasher]: Copy send thread read port\n");
294 	kr = thread_get_special_port(th_port, THREAD_READ_PORT, &port);
295 	assert(kr == 0);
296 	kr = attempt_send_immovable_port(port, MACH_MSG_TYPE_COPY_SEND);
297 	printf("[Crasher immovable_test_copy_send_thread_read] attempt_send_immovable_port returned %s \n.", mach_error_string(kr));
298 
299 	mach_port_deallocate(mach_task_self(), port);
300 	mach_port_deallocate(mach_task_self(), th_port);
301 }
302 
303 static void
cfi_test_no_bit_set(void)304 cfi_test_no_bit_set(void)
305 {
306 	printf("[Crasher]: Try sending mach_msg2() without setting CFI bits\n");
307 
308 	mach_msg_header_t header;
309 	kern_return_t kr;
310 
311 	header.msgh_local_port = MACH_PORT_NULL;
312 	header.msgh_remote_port = mach_task_self();
313 	header.msgh_id = 3409;
314 	header.msgh_bits = MACH_MSGH_BITS_SET(MACH_MSG_TYPE_COPY_SEND, 0, 0, 0);
315 	header.msgh_size = sizeof(header);
316 
317 	kr = mach_msg2(&header, MACH64_SEND_MSG, header, header.msgh_size, 0, MACH_PORT_NULL,
318 	    0, MACH_MSG_PRIORITY_UNSPECIFIED);
319 	/* crash */
320 	printf("[Crasher cfi_test_no_bit_set]: mach_msg2() returned %d\n", kr);
321 }
322 
323 static void
cfi_test_two_bits_set(void)324 cfi_test_two_bits_set(void)
325 {
326 	printf("[Crasher]: Try sending mach_msg2() but setting 2 CFI bits\n");
327 
328 	mach_msg_header_t header;
329 	kern_return_t kr;
330 
331 	header.msgh_local_port = MACH_PORT_NULL;
332 	header.msgh_remote_port = mach_task_self();
333 	header.msgh_id = 3409;
334 	header.msgh_bits = MACH_MSGH_BITS_SET(MACH_MSG_TYPE_COPY_SEND, 0, 0, 0);
335 	header.msgh_size = sizeof(header);
336 
337 	kr = mach_msg2(&header, MACH64_SEND_MSG | MACH64_SEND_ANY | MACH64_SEND_KOBJECT_CALL,
338 	    header, header.msgh_size, 0, MACH_PORT_NULL,
339 	    0, MACH_MSG_PRIORITY_UNSPECIFIED);
340 	/* crash */
341 	printf("[Crasher cfi_test_two_bits_set]: mach_msg2() returned %d\n", kr);
342 }
343 
344 static void
cfi_test_msg_to_timer_port(void)345 cfi_test_msg_to_timer_port(void)
346 {
347 	printf("[Crasher]: Try sending mach_msg2() to timer port\n");
348 
349 	mach_port_t timer = MACH_PORT_NULL;
350 	struct oversize_msg {
351 		mach_msg_header_t header;
352 		char data[2048];
353 	} msg;
354 
355 	kern_return_t kr;
356 	natural_t kotype;
357 	mach_vm_address_t addr;
358 
359 #define IKOT_TIMER 8
360 	timer = mk_timer_create();
361 	assert(timer != MACH_PORT_NULL);
362 
363 	/* Make sure it's a kobject port */
364 	kr = mach_port_kobject(mach_task_self(), timer, &kotype, &addr);
365 	assert(kr == KERN_SUCCESS);
366 	assert(kotype == IKOT_TIMER);
367 
368 	msg.header.msgh_local_port = MACH_PORT_NULL;
369 	msg.header.msgh_remote_port = timer;
370 	msg.header.msgh_bits = MACH_MSGH_BITS_SET(MACH_MSG_TYPE_MAKE_SEND, 0, 0, 0);
371 	msg.header.msgh_size = sizeof(msg);
372 
373 	/* Timer port must use MACH64_SEND_MQ_CALL */
374 	kr = mach_msg2(&msg, MACH64_SEND_MSG | MACH64_SEND_MQ_CALL,
375 	    msg.header, msg.header.msgh_size, 0, MACH_PORT_NULL,
376 	    0, MACH_MSG_PRIORITY_UNSPECIFIED);
377 	assert(kr == KERN_SUCCESS);
378 	printf("Message sent to timer port successfully\n");
379 
380 	/* Using MACH64_SEND_KOBJECT_CALL should crash */
381 	kr = mach_msg2(&msg, MACH64_SEND_MSG | MACH64_SEND_KOBJECT_CALL,
382 	    msg.header, msg.header.msgh_size, 0, MACH_PORT_NULL,
383 	    0, MACH_MSG_PRIORITY_UNSPECIFIED);
384 	/* crash */
385 	printf("[Crasher cfi_test_timer_port]: mach_msg2() returned %d\n", kr);
386 }
387 
388 static void
cfi_test_wrong_bit_set(void)389 cfi_test_wrong_bit_set(void)
390 {
391 	printf("[Crasher]: Try sending mach_msg2() but setting wrong CFI bits\n");
392 
393 	mach_msg_header_t header;
394 	kern_return_t kr;
395 
396 	header.msgh_local_port = MACH_PORT_NULL;
397 	header.msgh_remote_port = mach_task_self();
398 	header.msgh_id = 3409;
399 	header.msgh_bits = MACH_MSGH_BITS_SET(MACH_MSG_TYPE_COPY_SEND, 0, 0, 0);
400 	header.msgh_size = sizeof(header);
401 
402 	/* Using MACH64_SEND_MQ_CALL but destination is a kobject port */
403 	kr = mach_msg2(&header, MACH64_SEND_MSG | MACH64_SEND_MQ_CALL,
404 	    header, header.msgh_size, 0, MACH_PORT_NULL,
405 	    0, MACH_MSG_PRIORITY_UNSPECIFIED);
406 	/* crash */
407 	printf("[Crasher cfi_test_wrong_bit_set]: mach_msg2() returned %d\n", kr);
408 }
409 
410 int
main(int argc,char * argv[])411 main(int argc, char *argv[])
412 {
413 	void (*tests[MAX_TEST_NUM])(void) = {
414 		pinned_test_main_thread_mod_ref,
415 		pinned_test_pthread_dealloc,
416 		pinned_test_task_self_dealloc,
417 		pinned_test_task_self_mod_ref,
418 		pinned_test_task_threads_mod_ref,
419 		pinned_test_mach_port_destroy,
420 		pinned_test_move_send_as_remote_port,
421 
422 		immovable_test_move_send_task_self,
423 		immovable_test_copy_send_task_self,
424 		immovable_test_move_send_thread_self,
425 		immovable_test_copy_send_thread_self,
426 		immovable_test_copy_send_task_read,
427 		immovable_test_copy_send_task_inspect,
428 		immovable_test_move_send_thread_inspect,
429 		immovable_test_copy_send_thread_read,
430 		immovable_test_move_send_as_remote_port,
431 		immovable_test_move_send_raw_thread,
432 
433 		cfi_test_no_bit_set,
434 		cfi_test_two_bits_set,
435 		cfi_test_wrong_bit_set,
436 		cfi_test_msg_to_timer_port,
437 	};
438 	printf("[Crasher]: My Pid: %d\n", getpid());
439 
440 	if (argc < 2) {
441 		printf("[Crasher]: Specify a test to run.");
442 		exit(-1);
443 	}
444 
445 	int test_num = atoi(argv[1]);
446 
447 	if (test_num >= 0 && test_num < MAX_TEST_NUM) {
448 		(*tests[test_num])();
449 	} else {
450 		printf("[Crasher]: Invalid test num. Exiting...\n");
451 		exit(-1);
452 	}
453 
454 	exit(0);
455 }
456