xref: /xnu-8019.80.24/tests/prioritize_process_launch.c (revision a325d9c4a84054e40bbe985afedcb50ab80993ea)
1 /*
2  * prioritize process launch: Tests prioritized process launch across posix spawn and exec.
3  */
4 
5 #ifdef T_NAMESPACE
6 #undef T_NAMESPACE
7 #endif
8 
9 #include <darwintest.h>
10 #include <darwintest_multiprocess.h>
11 
12 #include <dispatch/dispatch.h>
13 #include <pthread.h>
14 #include <launch.h>
15 #include <mach/mach.h>
16 #include <mach/message.h>
17 #include <mach/mach_voucher.h>
18 #include <pthread/workqueue_private.h>
19 #include <voucher/ipc_pthread_priority_types.h>
20 #include <servers/bootstrap.h>
21 #include <stdlib.h>
22 #include <sys/event.h>
23 #include <unistd.h>
24 #include <crt_externs.h>
25 #include <signal.h>
26 #include <sys/types.h>
27 #include <sys/sysctl.h>
28 #include <libkern/OSAtomic.h>
29 #include <sys/wait.h>
30 #include <spawn.h>
31 #include <spawn_private.h>
32 
33 T_GLOBAL_META(T_META_NAMESPACE("xnu.prioritize_process_launch"),
34     T_META_RUN_CONCURRENTLY(true));
35 
36 #define HELPER_TIMEOUT_SECS (3000)
37 #define MACH_RCV_OPTIONS  (MACH_RCV_MSG | MACH_RCV_LARGE | MACH_RCV_LARGE_IDENTITY | \
38 	            MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_CTX) | \
39 	            MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0))
40 
41 static pthread_t
42 thread_create_at_qos(qos_class_t qos, void * (*function)(void *), void *arg);
43 static mach_port_t sr_port;
44 
45 
46 #pragma mark Mach receive
47 
48 static mach_voucher_t
create_pthpriority_voucher(mach_msg_priority_t qos)49 create_pthpriority_voucher(mach_msg_priority_t qos)
50 {
51 	char voucher_buf[sizeof(mach_voucher_attr_recipe_data_t) + sizeof(ipc_pthread_priority_value_t)];
52 
53 	mach_voucher_t voucher = MACH_PORT_NULL;
54 	kern_return_t ret;
55 	ipc_pthread_priority_value_t ipc_pthread_priority_value =
56 	    (ipc_pthread_priority_value_t)qos;
57 
58 	mach_voucher_attr_raw_recipe_array_t recipes;
59 	mach_voucher_attr_raw_recipe_size_t recipe_size = 0;
60 	mach_voucher_attr_recipe_t recipe =
61 	    (mach_voucher_attr_recipe_t)&voucher_buf[recipe_size];
62 
63 	recipe->key = MACH_VOUCHER_ATTR_KEY_PTHPRIORITY;
64 	recipe->command = MACH_VOUCHER_ATTR_PTHPRIORITY_CREATE;
65 	recipe->previous_voucher = MACH_VOUCHER_NULL;
66 	memcpy((char *)&recipe->content[0], &ipc_pthread_priority_value, sizeof(ipc_pthread_priority_value));
67 	recipe->content_size = sizeof(ipc_pthread_priority_value_t);
68 	recipe_size += sizeof(mach_voucher_attr_recipe_data_t) + recipe->content_size;
69 
70 	recipes = (mach_voucher_attr_raw_recipe_array_t)&voucher_buf[0];
71 
72 	ret = host_create_mach_voucher(mach_host_self(),
73 	    recipes,
74 	    recipe_size,
75 	    &voucher);
76 
77 	T_QUIET; T_ASSERT_MACH_SUCCESS(ret, "client host_create_mach_voucher");
78 	return voucher;
79 }
80 
81 static void
send(mach_port_t send_port,mach_port_t reply_port,mach_port_t msg_port,mach_msg_priority_t qos,mach_msg_option_t options,int send_disposition)82 send(
83 	mach_port_t send_port,
84 	mach_port_t reply_port,
85 	mach_port_t msg_port,
86 	mach_msg_priority_t qos,
87 	mach_msg_option_t options,
88 	int send_disposition)
89 {
90 	kern_return_t ret = 0;
91 
92 	struct {
93 		mach_msg_header_t header;
94 		mach_msg_body_t body;
95 		mach_msg_port_descriptor_t port_descriptor;
96 	} send_msg = {
97 		.header = {
98 			.msgh_remote_port = send_port,
99 			.msgh_local_port  = reply_port,
100 			.msgh_bits        = MACH_MSGH_BITS_SET(send_disposition,
101 	    reply_port ? MACH_MSG_TYPE_MAKE_SEND_ONCE : 0,
102 	    MACH_MSG_TYPE_MOVE_SEND,
103 	    MACH_MSGH_BITS_COMPLEX),
104 			.msgh_id          = 0x100,
105 			.msgh_size        = sizeof(send_msg),
106 		},
107 		.body = {
108 			.msgh_descriptor_count = 1,
109 		},
110 		.port_descriptor = {
111 			.name        = msg_port,
112 			.disposition = MACH_MSG_TYPE_MOVE_RECEIVE,
113 			.type        = MACH_MSG_PORT_DESCRIPTOR,
114 		},
115 	};
116 
117 	if (options & MACH_SEND_SYNC_USE_THRPRI) {
118 		send_msg.header.msgh_voucher_port = create_pthpriority_voucher(qos);
119 	}
120 
121 	if (msg_port == MACH_PORT_NULL) {
122 		send_msg.body.msgh_descriptor_count = 0;
123 	}
124 
125 	ret = mach_msg(&(send_msg.header),
126 	    MACH_SEND_MSG |
127 	    MACH_SEND_TIMEOUT |
128 	    MACH_SEND_OVERRIDE |
129 	    options,
130 	    send_msg.header.msgh_size,
131 	    0,
132 	    MACH_PORT_NULL,
133 	    10000,
134 	    0);
135 
136 	T_QUIET; T_ASSERT_MACH_SUCCESS(ret, "client mach_msg");
137 }
138 
139 static void
receive(mach_port_t rcv_port,mach_port_t notify_port)140 receive(
141 	mach_port_t rcv_port,
142 	mach_port_t notify_port)
143 {
144 	kern_return_t ret = 0;
145 
146 	struct {
147 		mach_msg_header_t header;
148 		mach_msg_body_t body;
149 		mach_msg_port_descriptor_t port_descriptor;
150 		mach_msg_trailer_t trailer;
151 	} rcv_msg = {
152 		.header =
153 		{
154 			.msgh_remote_port = MACH_PORT_NULL,
155 			.msgh_local_port  = rcv_port,
156 			.msgh_size        = sizeof(rcv_msg),
157 		},
158 	};
159 
160 	T_LOG("Client: Starting sync receive\n");
161 
162 	ret = mach_msg(&(rcv_msg.header),
163 	    MACH_RCV_MSG |
164 	    MACH_RCV_SYNC_WAIT,
165 	    0,
166 	    rcv_msg.header.msgh_size,
167 	    rcv_port,
168 	    0,
169 	    notify_port);
170 }
171 
172 static int
get_pri(thread_t thread_port)173 get_pri(thread_t thread_port)
174 {
175 	kern_return_t kr;
176 
177 	thread_extended_info_data_t extended_info;
178 	mach_msg_type_number_t count = THREAD_EXTENDED_INFO_COUNT;
179 	kr = thread_info(thread_port, THREAD_EXTENDED_INFO,
180 	    (thread_info_t)&extended_info, &count);
181 
182 	T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "thread_info");
183 
184 	return extended_info.pth_curpri;
185 }
186 
187 static void
set_thread_name(const char * fn_name)188 set_thread_name(const char *fn_name)
189 {
190 	char name[50] = "";
191 
192 	thread_t thread_port = pthread_mach_thread_np(pthread_self());
193 
194 	int pri = get_pri(thread_port);
195 
196 	snprintf(name, sizeof(name), "%s at pri %2d", fn_name, pri);
197 	pthread_setname_np(name);
198 }
199 
200 static void
thread_wait_to_block(mach_port_t thread_port)201 thread_wait_to_block(mach_port_t thread_port)
202 {
203 	thread_extended_info_data_t extended_info;
204 	kern_return_t kr;
205 
206 	while (1) {
207 		mach_msg_type_number_t count = THREAD_EXTENDED_INFO_COUNT;
208 		kr = thread_info(thread_port, THREAD_EXTENDED_INFO,
209 		    (thread_info_t)&extended_info, &count);
210 
211 		T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "thread_info");
212 
213 		if (extended_info.pth_run_state == TH_STATE_WAITING) {
214 			T_LOG("Target thread blocked\n");
215 			break;
216 		}
217 		thread_switch(thread_port, SWITCH_OPTION_DEPRESS, 0);
218 	}
219 }
220 
221 static void *
thread_sync_rcv(void * arg)222 thread_sync_rcv(void *arg)
223 {
224 	mach_port_t port = (mach_port_t)arg;
225 	mach_port_t special_reply_port;
226 
227 	set_thread_name(__FUNCTION__);
228 	special_reply_port = thread_get_special_reply_port();
229 	T_QUIET; T_ASSERT_TRUE(MACH_PORT_VALID(special_reply_port), "get_thread_special_reply_port");
230 
231 	sr_port = special_reply_port;
232 	/* Do a sync rcv on special reply port and push on given arg port */
233 	receive(special_reply_port, port);
234 	return NULL;
235 }
236 
237 static pthread_t
thread_create_at_qos(qos_class_t qos,void * (* function)(void *),void * arg)238 thread_create_at_qos(qos_class_t qos, void * (*function)(void *), void *arg)
239 {
240 	qos_class_t qos_thread;
241 	pthread_t pthread;
242 	pthread_attr_t attr;
243 	int ret;
244 
245 	ret = setpriority(PRIO_DARWIN_ROLE, 0, PRIO_DARWIN_ROLE_UI_FOCAL);
246 	if (ret != 0) {
247 		T_LOG("set priority failed\n");
248 	}
249 
250 	pthread_attr_init(&attr);
251 	pthread_attr_set_qos_class_np(&attr, qos, 0);
252 	pthread_create(&pthread, &attr, function, arg);
253 
254 	T_LOG("pthread created\n");
255 	pthread_get_qos_class_np(pthread, &qos_thread, NULL);
256 	return pthread;
257 }
258 
259 static mach_port_t
get_sync_push_port_at_qos(qos_class_t qos)260 get_sync_push_port_at_qos(qos_class_t qos)
261 {
262 	mach_port_t port;
263 	kern_return_t kr;
264 	pthread_t pthread;
265 	thread_t thread;
266 
267 	/* Create a rcv right to have a sync ipc push from a thread */
268 	kr = mach_port_allocate(mach_task_self(),
269 	    MACH_PORT_RIGHT_RECEIVE,
270 	    &port);
271 
272 	T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "sync push port  mach_port_allocate");
273 
274 	kr = mach_port_insert_right(mach_task_self(),
275 	    port,
276 	    port,
277 	    MACH_MSG_TYPE_MAKE_SEND);
278 
279 	T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "sync push port mach_port_insert_right");
280 
281 	/* Create a thread at given qos and start a sync push on given port */
282 	pthread = thread_create_at_qos(qos, thread_sync_rcv, (void *)(uintptr_t)port);
283 	thread = pthread_mach_thread_np(pthread);
284 	thread_wait_to_block(thread);
285 
286 	return port;
287 }
288 
289 static mach_port_t
create_port_and_copyin_a_port(mach_port_t port)290 create_port_and_copyin_a_port(mach_port_t port)
291 {
292 	mach_port_t new_port;
293 	kern_return_t kr;
294 
295 	/* Create a rcv right */
296 	kr = mach_port_allocate(mach_task_self(),
297 	    MACH_PORT_RIGHT_RECEIVE,
298 	    &new_port);
299 
300 	T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "copyin  mach_port_allocate");
301 
302 	kr = mach_port_insert_right(mach_task_self(),
303 	    new_port,
304 	    new_port,
305 	    MACH_MSG_TYPE_MAKE_SEND);
306 
307 	T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "copyin mach_port_insert_right");
308 
309 	send(new_port, MACH_PORT_NULL, port, 0, 0, MACH_MSG_TYPE_COPY_SEND);
310 	return new_port;
311 }
312 
313 static pid_t
posix_spawn_child_with_watch_ports(char * binary,char * arg,mach_port_t * port_array,int arrayCnt)314 posix_spawn_child_with_watch_ports(
315 	char *binary,
316 	char *arg,
317 	mach_port_t *port_array,
318 	int arrayCnt)
319 {
320 	pid_t child_pid = 0;
321 	char *new_argv[] = { binary, arg, NULL};
322 	errno_t ret;
323 	posix_spawnattr_t attr;
324 
325 	ret = posix_spawnattr_init(&attr);
326 	T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "posix_spawnattr_init");
327 
328 	ret = posix_spawnattr_set_importancewatch_port_np(&attr, arrayCnt, port_array);
329 	T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "posix_spawnattr_set_importancewatch_port_np");
330 
331 	ret = posix_spawn(&child_pid, binary, NULL, &attr, new_argv, NULL);
332 	T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "posix_spawn");
333 
334 	ret = posix_spawnattr_destroy(&attr);
335 	T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "posix_spawnattr_destroy");
336 
337 	return child_pid;
338 }
339 
340 static void
worker_cb(pthread_priority_t __unused priority)341 worker_cb(pthread_priority_t __unused priority)
342 {
343 	T_FAIL("a worker thread was created");
344 }
345 
346 static void
event_cb(void ** __unused events,int * __unused nevents)347 event_cb(void ** __unused events, int * __unused nevents)
348 {
349 	T_FAIL("a kevent routine was called instead of workloop");
350 }
351 
352 static void
workloop_cb_test_intransit(uint64_t * workloop_id __unused,void ** eventslist,int * events)353 workloop_cb_test_intransit(uint64_t *workloop_id __unused, void **eventslist, int *events)
354 {
355 	pid_t pid;
356 	int stat;
357 	int priority;
358 	mach_port_t port;
359 	struct kevent_qos_s *kev = *eventslist;
360 	mach_msg_header_t *hdr = (mach_msg_header_t *)kev->ext[0];
361 	port = hdr->msgh_local_port;
362 
363 	T_LOG("Workloop handler workloop_cb_test_intransit called. ");
364 	T_LOG("The total events returned is %d", *events);
365 
366 	priority = get_pri(mach_thread_self());
367 	T_EXPECT_EQ(priority, 47, "Priority of servicer is %d", priority);
368 
369 	pid = posix_spawn_child_with_watch_ports("prioritize_process_launch_helper", "WAIT", &port, 1);
370 
371 	/* Make sure our priority has dropped */
372 	priority = get_pri(mach_thread_self());
373 	T_EXPECT_EQ(priority, 31, "Priority of servicer is %d", priority);
374 
375 	sleep(2);
376 
377 	/*enqueue the port to sever the temp onwer boost */
378 	create_port_and_copyin_a_port(port);
379 
380 	waitpid(pid, &stat, 0);
381 
382 	*events = 0;
383 
384 	T_QUIET; T_LOG("The return stat is %d", WEXITSTATUS(stat));
385 	T_EXPECT_EQ(WEXITSTATUS(stat), 31, "Temp owner boost did not work correctly with knotes");
386 	T_END;
387 }
388 
389 static void
workloop_cb_test_knote_kill(uint64_t * workloop_id __unused,void ** eventslist,int * events)390 workloop_cb_test_knote_kill(uint64_t *workloop_id __unused, void **eventslist, int *events)
391 {
392 	pid_t pid;
393 	int stat;
394 	int priority;
395 	mach_port_t port;
396 	struct kevent_qos_s *kev = *eventslist;
397 	mach_msg_header_t *hdr = (mach_msg_header_t *)kev->ext[0];
398 	port = hdr->msgh_local_port;
399 
400 	T_LOG("Workloop handler workloop_cb_test_knote_kill called. ");
401 	T_LOG("The total events returned is %d", *events);
402 
403 	priority = get_pri(mach_thread_self());
404 	T_EXPECT_EQ(priority, 47, "Priority of servicer is %d", priority);
405 
406 	pid = posix_spawn_child_with_watch_ports("prioritize_process_launch_helper", "EXIT", &port, 1);
407 
408 	sleep(2);
409 
410 	/* Make sure our priority is boosted again */
411 	priority = get_pri(mach_thread_self());
412 	T_EXPECT_EQ(priority, 47, "Priority of servicer is %d", priority);
413 
414 	waitpid(pid, &stat, 0);
415 
416 	*events = 0;
417 
418 	T_QUIET; T_LOG("The return stat is %d", WEXITSTATUS(stat));
419 	T_EXPECT_EQ(WEXITSTATUS(stat), 47, "Temp owner boost did not work correctly with knotes");
420 	T_END;
421 }
422 
423 static void
workloop_cb_test_sync_bootstrap(uint64_t * workloop_id __unused,void ** eventslist,int * events)424 workloop_cb_test_sync_bootstrap(uint64_t *workloop_id __unused, void **eventslist, int *events)
425 {
426 	static pid_t pid = 0;
427 	int stat;
428 	int priority;
429 	static mach_port_t port = MACH_PORT_NULL;
430 	struct kevent_qos_s *kev = *eventslist;
431 	mach_msg_header_t *hdr = (mach_msg_header_t *)kev->ext[0];
432 
433 	T_LOG("Workloop handler workloop_cb_test_knote_kill called. ");
434 	T_LOG("The total events returned is %d", *events);
435 
436 	/* Check if called for peek */
437 	if (hdr == NULL) {
438 		priority = get_pri(mach_thread_self());
439 		T_EXPECT_EQ(priority, 47, "Priority of servicer is %d", priority);
440 
441 		port = (mach_port_t)kev->ident;
442 		pid = posix_spawn_child_with_watch_ports("prioritize_process_launch_helper", "MSGSYNC", &port, 1);
443 	} else {
444 		/* Wait till the priority of servicer is 47 */
445 		T_LOG("Waiting for the servicer to be boosted");
446 		do {
447 			sleep(1);
448 			priority = get_pri(mach_thread_self());
449 		} while (priority != 47);
450 
451 		T_EXPECT_EQ(priority, 47, "Priority of servicer is %d", priority);
452 
453 		/* Get the reply port and send the receive right in it */
454 		mach_port_t reply_port = hdr->msgh_remote_port;
455 		T_LOG("The rcv right to send is %d", port);
456 		send(reply_port, MACH_PORT_NULL, port, 0, 0, MACH_MSG_TYPE_MOVE_SEND_ONCE);
457 
458 		waitpid(pid, &stat, 0);
459 
460 		/* The handler priority should not be boosted anymore */
461 		priority = get_pri(mach_thread_self());
462 		T_EXPECT_EQ(priority, 31, "Priority of servicer is %d", priority);
463 
464 		T_QUIET; T_LOG("The return stat is %d", WEXITSTATUS(stat));
465 		T_EXPECT_EQ(WEXITSTATUS(stat), 31, "Temp owner boost did not work correctly with knotes");
466 		T_END;
467 	}
468 	*events = 0;
469 }
470 
471 static void
register_workloop_for_port(mach_port_t port,pthread_workqueue_function_workloop_t func,unsigned int options)472 register_workloop_for_port(
473 	mach_port_t port,
474 	pthread_workqueue_function_workloop_t func,
475 	unsigned int options)
476 {
477 	int r;
478 
479 	/* register workloop handler with pthread */
480 	if (func != NULL) {
481 		T_QUIET; T_ASSERT_POSIX_ZERO(_pthread_workqueue_init_with_workloop(
482 			    worker_cb, event_cb,
483 			    (pthread_workqueue_function_workloop_t)func, 0, 0), NULL);
484 	}
485 
486 	/* attach port to workloop */
487 	struct kevent_qos_s kev[] = {{
488 					     .ident = port,
489 					     .filter = EVFILT_MACHPORT,
490 					     .flags = EV_ADD | EV_UDATA_SPECIFIC | EV_DISPATCH | EV_VANISHED,
491 					     .fflags = options,
492 					     .data = 1,
493 					     .qos = (int32_t)_pthread_qos_class_encode(QOS_CLASS_DEFAULT, 0, 0)
494 				     }};
495 
496 	struct kevent_qos_s kev_err[] = {{ 0 }};
497 
498 	/* Setup workloop for mach msg rcv */
499 	r = kevent_id(25, kev, 1, kev_err, 1, NULL,
500 	    NULL, KEVENT_FLAG_WORKLOOP | KEVENT_FLAG_ERROR_EVENTS);
501 
502 	T_QUIET; T_ASSERT_POSIX_SUCCESS(r, "kevent_id");
503 	T_QUIET; T_ASSERT_EQ(r, 0, "no errors returned from kevent_id");
504 }
505 
506 /*
507  * Test 1: Test turnstile boosting for temp owner ports for posix_spawn.
508  *
509  * Create a port with sync IPC push and then pass the port to posix_spawn as a watch port and
510  * test that spawned binary has the temp owner push of the port.
511  */
512 T_DECL(posix_spawn_basic_priority, "Basic posix spawn temp owner priority test", T_META_ASROOT(YES))
513 {
514 	mach_port_t port;
515 	pid_t pid;
516 	int stat;
517 
518 	port = get_sync_push_port_at_qos(QOS_CLASS_USER_INTERACTIVE);
519 	pid = posix_spawn_child_with_watch_ports("prioritize_process_launch_helper", "EXIT", &port, 1);
520 
521 	waitpid(pid, &stat, 0);
522 
523 	T_QUIET; T_LOG("The return stat is %d", WEXITSTATUS(stat));
524 	T_EXPECT_EQ(WEXITSTATUS(stat), 47, "spawn did not properly boost main thread");
525 	T_END;
526 }
527 
528 /*
529  * Test 2: Test turnstile boosting for temp owner ports for posix_spawn and exec.
530  *
531  * Create a port with sync IPC push and then pass the port to posix_spawn as a watch port and
532  * test that spawned binary has the temp owner push of the port. The spawned binary will exec
533  * and verify that it still has the push.
534  */
535 T_DECL(posix_spawn_exec_basic_priority, "Basic posix spawn/exec temp owner priority test", T_META_ASROOT(YES))
536 {
537 	mach_port_t port;
538 	pid_t pid;
539 	int stat;
540 
541 	port = get_sync_push_port_at_qos(QOS_CLASS_USER_INTERACTIVE);
542 	pid = posix_spawn_child_with_watch_ports("prioritize_process_launch_helper", "EXEC", &port, 1);
543 
544 	waitpid(pid, &stat, 0);
545 
546 	T_QUIET; T_LOG("The return stat is %d", WEXITSTATUS(stat));
547 	T_EXPECT_EQ(WEXITSTATUS(stat), 47, "spawn/exec did not properly boost main thread");
548 	T_END;
549 }
550 
551 /*
552  * Test 3: Test turnstile boosting for temp owner ports for posix_spawn and set exec.
553  *
554  * Create a port with sync IPC push and then pass the port to posix_spawn as a watch port and
555  * test that spawned binary has the temp owner push of the port. The spawned binary will
556  * posix_spawn set exec and verify that it still has the push.
557  */
558 T_DECL(posix_spawn_set_exec_basic_priority, "Basic posix spawn set exec temp owner priority test", T_META_ASROOT(YES))
559 {
560 	mach_port_t port;
561 	pid_t pid;
562 	int stat;
563 
564 	port = get_sync_push_port_at_qos(QOS_CLASS_USER_INTERACTIVE);
565 	pid = posix_spawn_child_with_watch_ports("prioritize_process_launch_helper", "SETEXEC", &port, 1);
566 
567 	waitpid(pid, &stat, 0);
568 
569 	T_QUIET; T_LOG("The return stat is %d", WEXITSTATUS(stat));
570 	T_EXPECT_EQ(WEXITSTATUS(stat), 47, "spawn set exec did not properly boost main thread");
571 	T_END;
572 }
573 
574 /*
575  * Test 4: Test turnstile boosting for temp owner ports for posix_spawn and set exec.
576  *
577  * Create a port with sync IPC push and then pass the port to posix_spawn as a watch port and
578  * test that spawned binary has the temp owner push of the port. The spawned binary already
579  * having the temp owner push will try to do set exec with watchports which should fail.
580  */
581 T_DECL(posix_spawn_set_exec_with_more_ports, "posix spawn set exec with more watch ports", T_META_ASROOT(YES))
582 {
583 	mach_port_t port;
584 	pid_t pid;
585 	int stat;
586 
587 	port = get_sync_push_port_at_qos(QOS_CLASS_USER_INTERACTIVE);
588 	pid = posix_spawn_child_with_watch_ports("prioritize_process_launch_helper", "SETEXEC_PORTS", &port, 1);
589 
590 	waitpid(pid, &stat, 0);
591 
592 	T_QUIET; T_LOG("The return stat is %d", WEXITSTATUS(stat));
593 	T_EXPECT_EQ(WEXITSTATUS(stat), EINVAL, "spawn set exec did not error out when watchports were passed to already boosted process");
594 	T_END;
595 }
596 
597 /*
598  * Test 5: Test turnstile boosting for temp owner ports for multiple posix_spawns.
599  *
600  * Create a port with sync IPC push and then pass the port to posix_spawn as a watch port, then
601  * pass the same port as a watchport to another posix_spawn and verify that the boost was
602  * transferred to the new process.
603  */
604 T_DECL(posix_spawn_multiple, "multiple posix_spawn with same watchport", T_META_ASROOT(YES))
605 {
606 	mach_port_t port;
607 	pid_t pid1, pid2;
608 	int stat1, stat2;
609 
610 	port = get_sync_push_port_at_qos(QOS_CLASS_USER_INTERACTIVE);
611 	pid1 = posix_spawn_child_with_watch_ports("prioritize_process_launch_helper", "WAIT", &port, 1);
612 
613 	/* Let the child 1 execute a little, the sleep here is optional */
614 	sleep(2);
615 
616 	pid2 = posix_spawn_child_with_watch_ports("prioritize_process_launch_helper", "EXIT", &port, 1);
617 
618 	waitpid(pid2, &stat2, 0);
619 	waitpid(pid1, &stat1, 0);
620 
621 	T_QUIET; T_LOG("The return stat for child 1 is is %d", WEXITSTATUS(stat1));
622 	T_QUIET; T_LOG("The return stat for child 2 is is %d", WEXITSTATUS(stat2));
623 	T_EXPECT_EQ(WEXITSTATUS(stat2), 47, "spawn of multiple processes with same watchport did not transfer the boost correctly");
624 	T_EXPECT_EQ(WEXITSTATUS(stat1), 31, "spawn of multiple processes with same watchport did not transfer the boost correctly");
625 	T_END;
626 }
627 
628 /*
629  * Test 6: Test turnstile boosting for temp owner ports for posix_spawn for dead port.
630  *
631  * Create a port with sync IPC push and then pass the port to posix_spawn as a watch port and
632  * test that spawned binary has the temp owner push of the port. Destroy the port and verify
633  * the temp owner push has gone away.
634  */
635 T_DECL(posix_spawn_dead_reply_port, "posix spawn with reply port destory", T_META_ASROOT(YES))
636 {
637 	mach_port_t port;
638 	kern_return_t kr;
639 	pid_t pid;
640 	int stat;
641 
642 	port = get_sync_push_port_at_qos(QOS_CLASS_USER_INTERACTIVE);
643 	pid = posix_spawn_child_with_watch_ports("prioritize_process_launch_helper", "WAIT", &port, 1);
644 
645 	/* Let the child execute a little, the sleep here is optional */
646 	sleep(2);
647 
648 	/* Destory the special reply port */
649 	kr = mach_port_mod_refs(mach_task_self(), sr_port, MACH_PORT_RIGHT_RECEIVE, -1);
650 	T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "posix_spaw_dead_port  mach_port_mod_refs");
651 
652 	waitpid(pid, &stat, 0);
653 
654 	T_QUIET; T_LOG("The return stat is %d", WEXITSTATUS(stat));
655 	T_EXPECT_EQ(WEXITSTATUS(stat), 31, "Temp owner boost was not removed on port death");
656 	T_END;
657 }
658 
659 /*
660  * Test 7: Test turnstile boosting for temp owner ports for posix_spawn for dead port.
661  *
662  * Create a port with sync IPC push and then pass the port to posix_spawn as a watch port and
663  * test that spawned binary has the temp owner push of the port. Destroy the port and verify
664  * the temp owner push has gone.
665  */
666 T_DECL(posix_spawn_dead_port, "posix spawn with port destory", T_META_ASROOT(YES))
667 {
668 	mach_port_t port;
669 	kern_return_t kr;
670 	pid_t pid;
671 	int stat;
672 
673 	port = get_sync_push_port_at_qos(QOS_CLASS_USER_INTERACTIVE);
674 	pid = posix_spawn_child_with_watch_ports("prioritize_process_launch_helper", "WAIT", &port, 1);
675 
676 	/* Destory the port */
677 	kr = mach_port_mod_refs(mach_task_self(), port, MACH_PORT_RIGHT_RECEIVE, -1);
678 	T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "posix_spaw_dead_port  mach_port_mod_refs");
679 
680 	waitpid(pid, &stat, 0);
681 
682 	T_QUIET; T_LOG("The return stat is %d", WEXITSTATUS(stat));
683 	T_EXPECT_EQ(WEXITSTATUS(stat), 31, "Temp owner boost was not removed on port death");
684 	T_END;
685 }
686 
687 /*
688  * Test 8: Test turnstile boosting for temp owner ports for posix_spawn when port is copied in.
689  *
690  * Create a port with sync IPC push and then pass the port to posix_spawn as a watch port and
691  * test that spawned binary has the temp owner push of the port. Copyin the port and verify
692  * the temp owner push has gone.
693  */
694 T_DECL(posix_spawn_copyin_port, "posix spawn with copyin port", T_META_ASROOT(YES))
695 {
696 	mach_port_t port;
697 	pid_t pid;
698 	int stat;
699 
700 	port = get_sync_push_port_at_qos(QOS_CLASS_USER_INTERACTIVE);
701 	pid = posix_spawn_child_with_watch_ports("prioritize_process_launch_helper", "WAIT", &port, 1);
702 
703 	/* Let the child execute a little, the sleep here is optional */
704 	sleep(2);
705 
706 	/* Copyin the port in another port */
707 	create_port_and_copyin_a_port(port);
708 
709 	waitpid(pid, &stat, 0);
710 
711 	T_QUIET; T_LOG("The return stat is %d", WEXITSTATUS(stat));
712 	T_EXPECT_EQ(WEXITSTATUS(stat), 31, "Temp owner boost was not removed on port copyin");
713 	T_END;
714 }
715 
716 /*
717  * Test 9: Test turnstile boosting for temp owner ports for posix_spawn with multiple ports.
718  *
719  * Create multiple ports with sync IPC push and then pass the port to posix_spawn as watch ports and
720  * test that spawned binary has the temp owner push of the ports. Copyin ports one by one and verify
721  * the push has gone.
722  */
723 T_DECL(posix_spawn_multiple_port, "posix spawn with multiple ports", T_META_ASROOT(YES))
724 {
725 	mach_port_t port[2];
726 	pid_t pid;
727 	int stat;
728 
729 	port[0] = get_sync_push_port_at_qos(QOS_CLASS_USER_INTERACTIVE);
730 	port[1] = get_sync_push_port_at_qos(QOS_CLASS_USER_INITIATED);
731 	pid = posix_spawn_child_with_watch_ports("prioritize_process_launch_helper", "MULTIWAIT", port, 2);
732 
733 	/* Let the child execute a little, the sleep here is optional */
734 	sleep(2);
735 
736 	/* Copyin the port in another port */
737 	create_port_and_copyin_a_port(port[0]);
738 
739 	/* Let the child execute a little, the sleep here is optional */
740 	sleep(2);
741 
742 	/* Copyin the port in another port */
743 	create_port_and_copyin_a_port(port[1]);
744 
745 	waitpid(pid, &stat, 0);
746 
747 	T_QUIET; T_LOG("The return stat is %d", WEXITSTATUS(stat));
748 	T_EXPECT_EQ(WEXITSTATUS(stat), 31, "Temp owner boost did not work correctly with multiple ports");
749 	T_END;
750 }
751 
752 /*
753  * Test 10: Test turnstile boosting for temp owner ports for posix_spawn when port attached to a knote.
754  *
755  * Create a port with sync IPC push attach a workloop knote to it, send a message on the port, then in the
756  * servicer pass the port to posix_spawn as a watch port and test that spawned binary has the temp owner
757  * push of the port and the servicer looses the boost.
758  */
759 T_DECL(posix_spawn_knote, "posix spawn with temp owner port attached to knote", T_META_ASROOT(YES))
760 {
761 	mach_port_t port;
762 
763 	port = get_sync_push_port_at_qos(QOS_CLASS_USER_INTERACTIVE);
764 
765 	/* attach port to a workloop */
766 	register_workloop_for_port(port, workloop_cb_test_intransit, MACH_RCV_OPTIONS);
767 
768 	/* send a message on port to activate workloop handler */
769 	send(port, MACH_PORT_NULL, MACH_PORT_NULL, QOS_CLASS_DEFAULT, 0, MACH_MSG_TYPE_COPY_SEND);
770 	sigsuspend(0);
771 }
772 
773 /*
774  * Test 11: Test turnstile boosting for temp owner ports for posix_spawn when port attached to a knote.
775  *
776  * Create a port with sync IPC push attach a workloop knote to it, send a message on the port, then in the
777  * servicer pass the port to posix_spawn as a watch port and test that spawned binary has the temp owner
778  * push of the port and the servicer looses the boost, verify that once the spawned binary dies, the servicer
779  * gets the push.
780  */
781 T_DECL(posix_spawn_knote_ret, "posix spawn with temp owner port attached to knote with spawned binary dead", T_META_ASROOT(YES))
782 {
783 	mach_port_t port;
784 
785 	port = get_sync_push_port_at_qos(QOS_CLASS_USER_INTERACTIVE);
786 
787 	register_workloop_for_port(port, workloop_cb_test_knote_kill, MACH_RCV_OPTIONS);
788 
789 	/* send a message on port to activate workloop handler */
790 	send(port, MACH_PORT_NULL, MACH_PORT_NULL, QOS_CLASS_DEFAULT, 0, MACH_MSG_TYPE_COPY_SEND);
791 	sigsuspend(0);
792 }
793 
794 /*
795  * Test 12: Test turnstile boosting for temp owner ports and mach msg option for sync bootstrap_checkin.
796  *
797  * Create a port with sync IPC push attach a workloop knote to it, send a message on the port, then in the
798  * servicer pass the port to posix_spawn as a watch port and test that spawned binary has the temp owner
799  * push of the port and the servicer looses the boost, the spawn binary then does a sync bootstrap_checkin
800  * with test binary to get the receive right and verify that is still has the boost.
801  */
802 T_DECL(mach_msg_sync_boostrap_checkin, "test mach msg option for sync bootstrap_checkin", T_META_ASROOT(YES))
803 {
804 	mach_port_t port;
805 	mach_port_t sync_port;
806 	kern_return_t kr;
807 
808 	port = get_sync_push_port_at_qos(QOS_CLASS_USER_INTERACTIVE);
809 
810 	register_workloop_for_port(port, workloop_cb_test_sync_bootstrap, MACH_RCV_SYNC_PEEK);
811 
812 	/* Create a mach port for spawned binary to do bootstrap checkin */
813 	kr = mach_port_allocate(mach_task_self(),
814 	    MACH_PORT_RIGHT_RECEIVE,
815 	    &sync_port);
816 
817 	T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "mach_msg_sync_boostrap_checkin mach_port_allocate");
818 
819 	kr = mach_port_insert_right(mach_task_self(),
820 	    sync_port,
821 	    sync_port,
822 	    MACH_MSG_TYPE_MAKE_SEND);
823 
824 	T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "mach_msg_sync_boostrap_checkin mach_port_insert_right");
825 
826 	kr = mach_port_mod_refs(mach_task_self(), sync_port, MACH_PORT_RIGHT_SEND, 1);
827 	T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "mach_msg_sync_boostrap_checkin mach_port_mod_refs");
828 
829 	register_workloop_for_port(sync_port, NULL, MACH_RCV_OPTIONS);
830 
831 	/* Stash the port in task to make sure child also gets it */
832 	kr = mach_ports_register(mach_task_self(), &sync_port, 1);
833 	T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "mach_msg_sync_boostrap_checkin mach_ports_register");
834 
835 	/* send a message on port to activate workloop handler */
836 	send(port, MACH_PORT_NULL, MACH_PORT_NULL, QOS_CLASS_DEFAULT, 0, MACH_MSG_TYPE_COPY_SEND);
837 	sigsuspend(0);
838 }
839