xref: /xnu-11215.81.4/tests/prioritize_process_launch_helper.c (revision d4514f0bc1d3f944c22d92e68b646ac3fb40d452)
1 /*
2  * prioritize process launch: Tests prioritized process launch across posix spawn and exec.
3  */
4 
5 #include <dispatch/dispatch.h>
6 #include <pthread.h>
7 #include <launch.h>
8 #include <mach/mach.h>
9 #include <mach/message.h>
10 #include <mach/mach_voucher.h>
11 #include <pthread/workqueue_private.h>
12 #include <voucher/ipc_pthread_priority_types.h>
13 #include <servers/bootstrap.h>
14 #include <stdlib.h>
15 #include <sys/event.h>
16 #include <stdio.h>
17 #include <unistd.h>
18 #include <crt_externs.h>
19 #include <signal.h>
20 #include <sys/types.h>
21 #include <sys/sysctl.h>
22 #include <libkern/OSAtomic.h>
23 #include <sys/wait.h>
24 #include <spawn.h>
25 #include <spawn_private.h>
26 #include <string.h>
27 
28 
29 mach_port_t
30 receive(
31 	mach_port_t rcv_port,
32 	mach_port_t notify_port);
33 
34 static int
get_pri(thread_t thread_port)35 get_pri(thread_t thread_port)
36 {
37 	kern_return_t kr;
38 
39 	thread_extended_info_data_t extended_info;
40 	mach_msg_type_number_t count = THREAD_EXTENDED_INFO_COUNT;
41 	kr = thread_info(thread_port, THREAD_EXTENDED_INFO,
42 	    (thread_info_t)&extended_info, &count);
43 
44 	if (kr != KERN_SUCCESS) {
45 		printf("thread info failed to get current priority of the thread\n");
46 	}
47 	return extended_info.pth_priority;
48 }
49 
50 static void
set_thread_name(const char * fn_name)51 set_thread_name(const char *fn_name)
52 {
53 	char name[50] = "";
54 
55 	thread_t thread_port = pthread_mach_thread_np(pthread_self());
56 
57 	int pri = get_pri(thread_port);
58 
59 	snprintf(name, sizeof(name), "%s at pri %2d", fn_name, pri);
60 	pthread_setname_np(name);
61 }
62 
63 static void
send(mach_port_t send_port,mach_port_t reply_port,mach_port_t msg_port,mach_msg_option_t options,int send_disposition)64 send(
65 	mach_port_t send_port,
66 	mach_port_t reply_port,
67 	mach_port_t msg_port,
68 	mach_msg_option_t options,
69 	int send_disposition)
70 {
71 	kern_return_t ret = 0;
72 
73 	struct {
74 		mach_msg_header_t header;
75 		mach_msg_body_t body;
76 		mach_msg_port_descriptor_t port_descriptor;
77 	} send_msg = {
78 		.header = {
79 			.msgh_remote_port = send_port,
80 			.msgh_local_port  = reply_port,
81 			.msgh_bits        = MACH_MSGH_BITS_SET(send_disposition,
82 	    reply_port ? MACH_MSG_TYPE_MAKE_SEND_ONCE : 0,
83 	    MACH_MSG_TYPE_MOVE_SEND,
84 	    MACH_MSGH_BITS_COMPLEX),
85 			.msgh_id          = 0x100,
86 			.msgh_size        = sizeof(send_msg),
87 		},
88 		.body = {
89 			.msgh_descriptor_count = 1,
90 		},
91 		.port_descriptor = {
92 			.name        = msg_port,
93 			.disposition = MACH_MSG_TYPE_MOVE_RECEIVE,
94 			.type        = MACH_MSG_PORT_DESCRIPTOR,
95 		},
96 	};
97 
98 	if (msg_port == MACH_PORT_NULL) {
99 		send_msg.body.msgh_descriptor_count = 0;
100 	}
101 
102 	ret = mach_msg(&(send_msg.header),
103 	    MACH_SEND_MSG |
104 	    MACH_SEND_TIMEOUT |
105 	    MACH_SEND_OVERRIDE |
106 	    options,
107 	    send_msg.header.msgh_size,
108 	    0,
109 	    MACH_PORT_NULL,
110 	    10000,
111 	    0);
112 
113 	if (ret != KERN_SUCCESS) {
114 		printf("mach_msg_send failed with error %d\n", ret);
115 	}
116 }
117 
118 mach_port_t
receive(mach_port_t rcv_port,mach_port_t notify_port)119 receive(
120 	mach_port_t rcv_port,
121 	mach_port_t notify_port)
122 {
123 	kern_return_t ret = 0;
124 	mach_port_t service_port;
125 
126 	struct {
127 		mach_msg_header_t header;
128 		mach_msg_body_t body;
129 		mach_msg_port_descriptor_t port_descriptor;
130 		mach_msg_trailer_t trailer;
131 	} rcv_msg = {
132 		.header =
133 		{
134 			.msgh_remote_port = MACH_PORT_NULL,
135 			.msgh_local_port  = rcv_port,
136 			.msgh_size        = sizeof(rcv_msg),
137 		},
138 	};
139 
140 	printf("Client: Starting sync receive\n");
141 
142 	ret = mach_msg(&(rcv_msg.header),
143 	    MACH_RCV_MSG | MACH_RCV_LARGE |
144 	    (notify_port ? MACH_RCV_SYNC_WAIT : 0),
145 	    0,
146 	    rcv_msg.header.msgh_size,
147 	    rcv_port,
148 	    0,
149 	    notify_port);
150 
151 	printf("mach msg rcv returned %d\n", ret);
152 
153 
154 	if (rcv_msg.body.msgh_descriptor_count != 1) {
155 		if (notify_port) {
156 			printf("Did not receive a service port in mach msg %d\n", rcv_msg.body.msgh_descriptor_count);
157 		}
158 		return MACH_PORT_NULL;
159 	}
160 
161 	service_port = rcv_msg.port_descriptor.name;
162 	return service_port;
163 }
164 
165 int
main(int argc,char * argv[])166 main(int argc __attribute__((unused)), char *argv[])
167 {
168 	int priority;
169 	set_thread_name(__FUNCTION__);
170 
171 	/* Check for priority */
172 	priority = get_pri(mach_thread_self());
173 	printf("The priority of child is %d\n", priority);
174 
175 	if (strcmp(argv[1], "EXIT") == 0) {
176 		printf("Helper process exiting\n");
177 		exit(priority);
178 	} else if (strcmp(argv[1], "EXEC") == 0) {
179 		int ret;
180 
181 		printf("Helper process execing\n");
182 		/* exec the same binary with EXIT arg */
183 		char *binary = "prioritize_process_launch_helper";
184 		char *new_argv[] = {binary, "EXIT", NULL};
185 		ret = execve(binary, new_argv, NULL);
186 		exit(ret);
187 	} else if (strcmp(argv[1], "SETEXEC") == 0) {
188 		int ret;
189 		int child_pid;
190 		posix_spawnattr_t attr;
191 
192 		ret = posix_spawnattr_init(&attr);
193 		if (ret != 0) {
194 			printf("posix_spawnattr_init failed \n");
195 			exit(ret);
196 		}
197 		ret = posix_spawnattr_setflags(&attr, POSIX_SPAWN_SETEXEC);
198 		if (ret != 0) {
199 			printf("posix_spawnattr_setflags failed \n");
200 			exit(ret);
201 		}
202 
203 		printf("Helper process doing posix_spawn set_exec\n");
204 		/* set exec the same binary with EXIT arg */
205 		char *binary = "prioritize_process_launch_helper";
206 		char *new_argv[] = {binary, "EXIT", NULL};
207 
208 		ret = posix_spawn(&child_pid, binary, NULL, &attr, new_argv, NULL);
209 		exit(ret);
210 	} else if (strcmp(argv[1], "SETEXEC_PORTS") == 0) {
211 		int ret;
212 		int child_pid;
213 		posix_spawnattr_t attr;
214 		mach_port_t port;
215 
216 		kern_return_t kr =  mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port);
217 		if (kr != KERN_SUCCESS) {
218 			printf("mach_port_allocate failed with error %d\n", kr);
219 			exit(kr);
220 		}
221 
222 		kr = mach_port_insert_right(mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND);
223 		if (kr != KERN_SUCCESS) {
224 			printf("mach_port_insert_right failed with error %d\n", kr);
225 			exit(kr);
226 		}
227 
228 		ret = posix_spawnattr_init(&attr);
229 		if (ret != 0) {
230 			printf("posix_spawnattr_init failed \n");
231 			exit(ret);
232 		}
233 
234 		ret = posix_spawnattr_setflags(&attr, POSIX_SPAWN_SETEXEC);
235 		if (ret != 0) {
236 			printf("posix_spawnattr_setflags failed \n");
237 			exit(ret);
238 		}
239 
240 		ret = posix_spawnattr_set_importancewatch_port_np(&attr, 1, &port);
241 		if (ret != 0) {
242 			printf("posix_spawnattr_set_importance_port_np failed \n");
243 			exit(ret);
244 		}
245 
246 		printf("Helper process doing posix_spawn set_exec\n");
247 		/* set exec the same binary with EXIT arg */
248 		char *binary = "prioritize_process_launch_helper";
249 		char *new_argv[] = {binary, "EXIT", NULL};
250 
251 		ret = posix_spawn(&child_pid, binary, NULL, &attr, new_argv, NULL);
252 		printf("spawned failed with error %d\n", ret);
253 		exit(ret);
254 	} else if (strcmp(argv[1], "WAIT") == 0) {
255 		do {
256 			sleep(1);
257 			priority = get_pri(mach_thread_self());
258 		} while (priority == 47);
259 		exit(priority);
260 	} else if (strcmp(argv[1], "MULTIWAIT") == 0) {
261 		do {
262 			sleep(1);
263 			priority = get_pri(mach_thread_self());
264 		} while (priority == 47);
265 		printf("The priority came down to %d\n", priority);
266 		do {
267 			sleep(1);
268 			priority = get_pri(mach_thread_self());
269 		} while (priority == 37);
270 		printf("The priority came down to %d\n", priority);
271 		exit(priority);
272 	} else if (strcmp(argv[1], "MSGSYNC") == 0) {
273 		int ret_val = 31;
274 		mach_port_array_t port_array = NULL;
275 		unsigned int portCnt = 0;
276 		mach_port_t send_port;
277 		mach_port_t special_reply_port;
278 		mach_port_t service_port;
279 		kern_return_t kr;
280 
281 		priority = get_pri(mach_thread_self());
282 		printf("The priority of spawned binary is  to %d\n", priority);
283 		if (priority != 47) {
284 			ret_val = 0;
285 		}
286 
287 		/* Get the stashed send right using mach_ports_lookup */
288 		kr = mach_ports_lookup(mach_task_self(), &port_array, &portCnt);
289 		if (kr != KERN_SUCCESS) {
290 			printf("mach_ports_lookup failed with return value %d and port count %d\n", kr, portCnt);
291 			exit(0);
292 		}
293 
294 		send_port = port_array[0];
295 		special_reply_port = thread_get_special_reply_port();
296 		if (!MACH_PORT_VALID(special_reply_port)) {
297 			printf("Failed to special reply port for thread\n");
298 			exit(0);
299 		}
300 
301 		/* Perform a Sync bootstrap checkin */
302 		send(send_port, special_reply_port, MACH_PORT_NULL, MACH_SEND_SYNC_BOOTSTRAP_CHECKIN, MACH_MSG_TYPE_COPY_SEND);
303 		sleep(2);
304 
305 		/* Make sure we are still boosted */
306 		priority = get_pri(mach_thread_self());
307 		printf("The priority of spawned binary is  to %d\n", priority);
308 		if (priority != 47) {
309 			ret_val = 0;
310 		}
311 
312 		/* Receive the service port */
313 		service_port = receive(special_reply_port, send_port);
314 
315 		/* Make sure we are still boosted */
316 		priority = get_pri(mach_thread_self());
317 		printf("The priority of spawned binary is  to %d\n", priority);
318 		if (priority != 47) {
319 			ret_val = 0;
320 		}
321 
322 		/* Try to receive on service port */
323 		receive(service_port, MACH_PORT_NULL);
324 
325 		/* Make sure we are no longer boosted */
326 		priority = get_pri(mach_thread_self());
327 		printf("The priority of spawned binary is  to %d\n", priority);
328 		if (priority != 31) {
329 			ret_val = 0;
330 		}
331 		exit(ret_val);
332 	}
333 
334 	exit(0);
335 }
336