xref: /xnu-8019.80.24/tests/mach_exception_reply.c (revision a325d9c4a84054e40bbe985afedcb50ab80993ea)
1 #include <darwintest.h>
2 
3 #include <pthread.h>
4 #include <setjmp.h>
5 #include <signal.h>
6 #include <stdlib.h>
7 #include <unistd.h>
8 #include <mach/mach.h>
9 #include <pthread/qos_private.h>
10 #include <voucher/ipc_pthread_priority_types.h>
11 
12 T_GLOBAL_META(T_META_RUN_CONCURRENTLY(true),
13     T_META_NAMESPACE("xnu.ipc"),
14     T_META_RADAR_COMPONENT_NAME("xnu"),
15     T_META_RADAR_COMPONENT_VERSION("IPC"));
16 
17 #define MSG      1024
18 #define PG_ALLOC 4096
19 
20 typedef enum {
21 	ReplyWithNoError,
22 	ReplyWithReplyPort,
23 	ReplyWithReplyPortMove,
24 	ReplyWithReplyPortCplxBit,
25 	ReplyWithReplyPortMoveCplxBit,
26 	ReplyWithPortDesc,
27 	ReplyWithOOLDesc,
28 	ReplyWithVoucher,
29 	ReplyWithVoucherGarbage
30 } ReplyType;
31 
32 struct exc_thread_arg {
33 	ReplyType    rt;
34 	mach_port_t  port;
35 };
36 
37 static const char *
reply_type_str(ReplyType rt)38 reply_type_str(ReplyType rt)
39 {
40 	switch (rt) {
41 	case ReplyWithNoError:
42 		return "ReplyWithNoError";
43 	case ReplyWithReplyPort:
44 		return "ReplyWithReplyPort";
45 	case ReplyWithReplyPortMove:
46 		return "ReplyWithReplyPortMove";
47 	case ReplyWithReplyPortCplxBit:
48 		return "ReplyWithReplyPortCplxBit";
49 	case ReplyWithReplyPortMoveCplxBit:
50 		return "ReplyWithReplyPortMoveCplxBit";
51 	case ReplyWithPortDesc:
52 		return "ReplyWithPortDesc";
53 	case ReplyWithOOLDesc:
54 		return "ReplyWithOOLDesc";
55 	case ReplyWithVoucher:
56 		return "ReplyWithVoucher";
57 	case ReplyWithVoucherGarbage:
58 		return "ReplyWithVoucherGarbage";
59 	}
60 }
61 
62 static mach_voucher_t
create_pthpriority_voucher(void)63 create_pthpriority_voucher(void)
64 {
65 	char voucher_buf[sizeof(mach_voucher_attr_recipe_data_t) + sizeof(ipc_pthread_priority_value_t)];
66 
67 	mach_voucher_t voucher = MACH_PORT_NULL;
68 	kern_return_t kr;
69 	ipc_pthread_priority_value_t ipc_pthread_priority_value =
70 	    (ipc_pthread_priority_value_t)_pthread_qos_class_encode(QOS_CLASS_USER_INTERACTIVE, 0, 0);
71 
72 	mach_voucher_attr_raw_recipe_size_t recipe_size = 0;
73 	mach_voucher_attr_recipe_t recipe =
74 	    (mach_voucher_attr_recipe_t)&voucher_buf[0];
75 
76 	recipe->key = MACH_VOUCHER_ATTR_KEY_PTHPRIORITY;
77 	recipe->command = MACH_VOUCHER_ATTR_PTHPRIORITY_CREATE;
78 	recipe->previous_voucher = MACH_VOUCHER_NULL;
79 
80 	memcpy((char *)&recipe->content[0], &ipc_pthread_priority_value, sizeof(ipc_pthread_priority_value));
81 	recipe->content_size = sizeof(ipc_pthread_priority_value_t);
82 	recipe_size += sizeof(mach_voucher_attr_recipe_data_t) + recipe->content_size;
83 
84 	kr = host_create_mach_voucher(mach_host_self(),
85 	    (mach_voucher_attr_raw_recipe_array_t)&voucher_buf[0],
86 	    recipe_size,
87 	    &voucher);
88 
89 	T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "host_create_mach_voucher");
90 	return voucher;
91 }
92 
93 static void *
handle_exceptions(void * arg)94 handle_exceptions(void *arg)
95 {
96 	struct exc_thread_arg *ta = (struct exc_thread_arg *)arg;
97 	mach_port_t ePort = ta->port;
98 	ReplyType reply_type = ta->rt;
99 
100 	char msg_store[MSG + MAX_TRAILER_SIZE];
101 	char reply_store[MSG];
102 	mach_msg_header_t *msg = (mach_msg_header_t *)msg_store;
103 	vm_address_t page;
104 	kern_return_t kr;
105 
106 	kr = vm_allocate(mach_task_self(), &page, PG_ALLOC, VM_FLAGS_ANYWHERE);
107 	T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "ool page allocation of %d bytes", PG_ALLOC);
108 
109 	mach_voucher_t voucher = create_pthpriority_voucher();
110 
111 	while (1) {
112 		bzero(msg, sizeof(msg_store));
113 
114 		msg->msgh_local_port = ePort;
115 		msg->msgh_size = MSG;
116 		kr = mach_msg_receive(msg);
117 		T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "exception msg recv");
118 
119 		bzero(reply_store, sizeof(reply_store));
120 
121 		switch (reply_type) {
122 		case ReplyWithNoError: {
123 #pragma pack(4)
124 			typedef struct {
125 				mach_msg_header_t hdr;
126 				NDR_record_t ndr;
127 				kern_return_t kr;
128 			} reply_fmt_t;
129 #pragma pack()
130 			reply_fmt_t *reply = (reply_fmt_t *)reply_store;
131 
132 			reply->hdr.msgh_bits = MACH_MSGH_BITS_SET(MACH_MSG_TYPE_MOVE_SEND_ONCE, 0, 0, 0);
133 			reply->hdr.msgh_remote_port = msg->msgh_remote_port;
134 			reply->hdr.msgh_local_port = MACH_PORT_NULL;
135 			reply->hdr.msgh_size = sizeof(*reply);
136 			reply->hdr.msgh_id = msg->msgh_id + 100;
137 			break;
138 		}
139 
140 		case ReplyWithReplyPort: {
141 #pragma pack(4)
142 			typedef struct {
143 				mach_msg_header_t hdr;
144 				NDR_record_t ndr;
145 				kern_return_t kr;
146 			} reply_fmt_t;
147 #pragma pack()
148 			reply_fmt_t *reply = (reply_fmt_t *)reply_store;
149 
150 			reply->hdr.msgh_bits = MACH_MSGH_BITS_SET(MACH_MSG_TYPE_MOVE_SEND_ONCE, MACH_MSG_TYPE_COPY_SEND, 0, 0);
151 			reply->hdr.msgh_remote_port = msg->msgh_remote_port;
152 			reply->hdr.msgh_local_port = ePort; /* Bogus */
153 			reply->hdr.msgh_size = sizeof(*reply);
154 			reply->hdr.msgh_id = msg->msgh_id + 100;
155 			break;
156 		}
157 
158 		case ReplyWithReplyPortMove: {
159 #pragma pack(4)
160 			typedef struct {
161 				mach_msg_header_t hdr;
162 				NDR_record_t ndr;
163 				kern_return_t kr;
164 			} reply_fmt_t;
165 #pragma pack()
166 			reply_fmt_t *reply = (reply_fmt_t *)reply_store;
167 
168 			reply->hdr.msgh_bits = MACH_MSGH_BITS_SET(MACH_MSG_TYPE_MOVE_SEND_ONCE, MACH_MSG_TYPE_MOVE_SEND, 0, 0);
169 			reply->hdr.msgh_remote_port = msg->msgh_remote_port;
170 			reply->hdr.msgh_local_port = ePort; /* Bogus */
171 			reply->hdr.msgh_size = sizeof(*reply);
172 			reply->hdr.msgh_id = msg->msgh_id + 100;
173 			break;
174 		}
175 
176 		case ReplyWithReplyPortCplxBit: {
177 #pragma pack(4)
178 			typedef struct {
179 				mach_msg_header_t hdr;
180 				mach_msg_body_t body;
181 			} reply_fmt_t;
182 #pragma pack()
183 			reply_fmt_t *reply = (reply_fmt_t *)reply_store;
184 
185 			reply->hdr.msgh_bits = MACH_MSGH_BITS_SET(MACH_MSG_TYPE_MOVE_SEND_ONCE, MACH_MSG_TYPE_COPY_SEND, 0, MACH_MSGH_BITS_COMPLEX);
186 			reply->hdr.msgh_remote_port = msg->msgh_remote_port;
187 			reply->hdr.msgh_local_port = ePort; /* Bogus */
188 			reply->hdr.msgh_size = sizeof(*reply);
189 			reply->hdr.msgh_id = msg->msgh_id + 100;
190 			reply->body.msgh_descriptor_count = 0;
191 			break;
192 		}
193 
194 		case ReplyWithReplyPortMoveCplxBit: {
195 #pragma pack(4)
196 			typedef struct {
197 				mach_msg_header_t hdr;
198 				mach_msg_body_t body;
199 			} reply_fmt_t;
200 #pragma pack()
201 			reply_fmt_t *reply = (reply_fmt_t *)reply_store;
202 
203 			reply->hdr.msgh_bits = MACH_MSGH_BITS_SET(MACH_MSG_TYPE_MOVE_SEND_ONCE, MACH_MSG_TYPE_MOVE_SEND, 0, MACH_MSGH_BITS_COMPLEX);
204 			reply->hdr.msgh_remote_port = msg->msgh_remote_port;
205 			reply->hdr.msgh_local_port = ePort; /* Bogus */
206 			reply->hdr.msgh_size = sizeof(*reply);
207 			reply->hdr.msgh_id = msg->msgh_id + 100;
208 			reply->body.msgh_descriptor_count = 0;
209 			break;
210 		}
211 
212 		case ReplyWithPortDesc: {
213 #pragma pack(4)
214 			typedef struct {
215 				mach_msg_header_t hdr;
216 				mach_msg_body_t body;
217 				mach_msg_port_descriptor_t port;
218 			} reply_fmt_t;
219 #pragma pack()
220 			reply_fmt_t *reply = (reply_fmt_t *)reply_store;
221 
222 			reply->hdr.msgh_bits = MACH_MSGH_BITS_SET(MACH_MSG_TYPE_MOVE_SEND_ONCE, 0, 0, MACH_MSGH_BITS_COMPLEX);
223 			reply->hdr.msgh_remote_port = msg->msgh_remote_port;
224 			reply->hdr.msgh_local_port = MACH_PORT_NULL;
225 			reply->hdr.msgh_size = sizeof(*reply);
226 			reply->hdr.msgh_id = msg->msgh_id + 100;
227 			reply->body.msgh_descriptor_count = 1;
228 			reply->port.type = MACH_MSG_PORT_DESCRIPTOR;
229 			reply->port.name = ePort;
230 			reply->port.disposition = MACH_MSG_TYPE_COPY_SEND;
231 			break;
232 		}
233 
234 		case ReplyWithOOLDesc: {
235 #pragma pack(4)
236 			typedef struct {
237 				mach_msg_header_t hdr;
238 				mach_msg_body_t body;
239 				mach_msg_ool_descriptor_t ool;
240 			} reply_fmt_t;
241 #pragma pack()
242 			reply_fmt_t *reply = (reply_fmt_t *)reply_store;
243 
244 			reply->hdr.msgh_bits = MACH_MSGH_BITS_SET(MACH_MSG_TYPE_MOVE_SEND_ONCE, 0, 0, MACH_MSGH_BITS_COMPLEX);
245 			reply->hdr.msgh_remote_port = msg->msgh_remote_port;
246 			reply->hdr.msgh_local_port = MACH_PORT_NULL;
247 			reply->hdr.msgh_size = sizeof(*reply);
248 			reply->hdr.msgh_id = msg->msgh_id + 100;
249 			reply->body.msgh_descriptor_count = 1;
250 			reply->ool.type = MACH_MSG_OOL_DESCRIPTOR;
251 			reply->ool.address = (void *)page;
252 			reply->ool.size = PG_ALLOC;
253 			reply->ool.deallocate = 0;
254 			reply->ool.copy = MACH_MSG_VIRTUAL_COPY;
255 			break;
256 		}
257 
258 		case ReplyWithVoucher: {
259 #pragma pack(4)
260 			typedef struct {
261 				mach_msg_header_t hdr;
262 				NDR_record_t ndr;
263 				kern_return_t kr;
264 			} reply_fmt_t;
265 #pragma pack()
266 			reply_fmt_t *reply = (reply_fmt_t *)reply_store;
267 
268 			reply->hdr.msgh_remote_port = msg->msgh_remote_port;
269 			reply->hdr.msgh_local_port = MACH_PORT_NULL;
270 			reply->hdr.msgh_size = sizeof(*reply);
271 			reply->hdr.msgh_id = msg->msgh_id + 100;
272 			reply->kr = KERN_SUCCESS;
273 
274 			/* try to send a voucher */
275 			reply->hdr.msgh_bits = MACH_MSGH_BITS_SET(MACH_MSG_TYPE_MOVE_SEND_ONCE,
276 			    0,
277 			    MACH_MSG_TYPE_MOVE_SEND,
278 			    0);
279 			reply->hdr.msgh_voucher_port = voucher;
280 			voucher = MACH_VOUCHER_NULL;
281 			break;
282 		}
283 
284 		case ReplyWithVoucherGarbage: {
285 #pragma pack(4)
286 			typedef struct {
287 				mach_msg_header_t hdr;
288 				NDR_record_t ndr;
289 				kern_return_t kr;
290 			} reply_fmt_t;
291 #pragma pack()
292 			reply_fmt_t *reply = (reply_fmt_t *)reply_store;
293 
294 			reply->hdr.msgh_remote_port = msg->msgh_remote_port;
295 			reply->hdr.msgh_local_port = MACH_PORT_NULL;
296 			reply->hdr.msgh_size = sizeof(*reply);
297 			reply->hdr.msgh_id = msg->msgh_id + 100;
298 			reply->kr = KERN_SUCCESS;
299 
300 			/* don't claim to send a voucher */
301 			reply->hdr.msgh_bits = MACH_MSGH_BITS_SET(MACH_MSG_TYPE_MOVE_SEND_ONCE,
302 			    0, 0, 0);
303 			/* but put some bits in the field */
304 			reply->hdr.msgh_voucher_port = (mach_voucher_t)0xdead;
305 			break;
306 		}
307 
308 		default:
309 			T_ASSERT_FAIL("Invalid ReplyType: %d", reply_type);
310 			T_END;
311 		}
312 
313 		if (voucher) {
314 			kr = mach_port_mod_refs(mach_task_self(), voucher,
315 			    MACH_PORT_RIGHT_SEND, -1);
316 			T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "destroy voucher");
317 		}
318 
319 		T_LOG("sending exception reply of type (%s)", reply_type_str(reply_type));
320 		kr = mach_msg_send((mach_msg_header_t *)reply_store);
321 		T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "exception reply msg send");
322 
323 		T_PASS("Successfully delivered exception reply message of type %s", reply_type_str(reply_type));
324 		T_END;
325 		return NULL;
326 	}
327 }
328 
329 static sigjmp_buf jb;
330 static int *bad_pointer = NULL;
331 static int s_sigmask = 0;
332 
333 static void
signal_handler(int sig,siginfo_t * sip __unused,void * ucontext __unused)334 signal_handler(int sig, siginfo_t *sip __unused, void *ucontext __unused)
335 {
336 	if (sigmask(sig) & s_sigmask) { /* TODO: check that the fault was generated by us */
337 		siglongjmp(jb, sig);
338 	} else {
339 		siglongjmp(jb, -sig);
340 	}
341 }
342 
343 static int
handle_signals(void)344 handle_signals(void)
345 {
346 	int mask = 0;
347 
348 	struct sigaction sa = {
349 		.sa_sigaction = signal_handler,
350 		.sa_flags = SA_SIGINFO
351 	};
352 	sigfillset(&sa.sa_mask);
353 
354 	T_QUIET; T_ASSERT_POSIX_ZERO(sigaction(SIGTRAP, &sa, NULL), NULL);
355 	mask |= sigmask(SIGTRAP);
356 
357 	T_QUIET; T_ASSERT_POSIX_ZERO(sigaction(SIGSEGV, &sa, NULL), NULL);
358 	mask |= sigmask(SIGSEGV);
359 
360 	T_QUIET; T_ASSERT_POSIX_ZERO(sigaction(SIGILL, &sa, NULL), NULL);
361 	mask |= sigmask(SIGILL);
362 
363 	return mask;
364 }
365 
366 static void
test_exc_reply_type(ReplyType reply_type)367 test_exc_reply_type(ReplyType reply_type)
368 {
369 	kern_return_t kr;
370 	task_t me = mach_task_self();
371 	thread_t self = mach_thread_self();
372 	pthread_t handler_thread;
373 	pthread_attr_t  attr;
374 	mach_port_t ePort;
375 
376 	s_sigmask = handle_signals();
377 	T_LOG("task self = 0x%x, thread self = 0x%x\n", me, self);
378 
379 	kr = mach_port_allocate(me, MACH_PORT_RIGHT_RECEIVE, &ePort);
380 	T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "allocate receive right");
381 
382 	kr = mach_port_insert_right(me, ePort, ePort, MACH_MSG_TYPE_MAKE_SEND);
383 	T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "insert right into port=[%d]", ePort);
384 
385 	kr = thread_set_exception_ports(self, EXC_MASK_ALL, ePort, EXCEPTION_DEFAULT, THREAD_STATE_NONE);
386 	T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "set exception ports on self=[%d], handler=[%d]", self, ePort);
387 
388 	pthread_attr_init(&attr);
389 	pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
390 	struct exc_thread_arg *ta = (struct exc_thread_arg *)malloc(sizeof(*ta));
391 	T_QUIET; T_ASSERT_NOTNULL(ta, "exception handler thread args allocation");
392 	ta->port = ePort;
393 	ta->rt = reply_type;
394 
395 	T_QUIET; T_ASSERT_POSIX_SUCCESS(pthread_create(&handler_thread, &attr, handle_exceptions, (void *)ta),
396 	    "pthread creation");
397 
398 	pthread_attr_destroy(&attr);
399 
400 	/* cause exception! */
401 	int x = sigsetjmp(jb, 0); //s_sigmask);
402 	if (x == 0) {
403 		*bad_pointer = 0;
404 	} else if (x < 0) {
405 		T_FAIL("Unexpected state on return-from-exception");
406 		T_END;
407 	} else {
408 		T_PASS("Successfully recovered from exception");
409 		T_END;
410 	}
411 	T_FAIL("Unexpected end of test!");
412 	T_END;
413 }
414 
415 T_DECL(mach_exc_ReplyNoError, "exception server reply with no error",
416     T_META_CHECK_LEAKS(false), T_META_IGNORECRASHES(".*mach_exception_reply.*"))
417 {
418 	test_exc_reply_type(ReplyWithNoError);
419 }
420 T_DECL(mach_exc_ReplyWithReplyPort, "exception server reply with reply port",
421     T_META_CHECK_LEAKS(false), T_META_IGNORECRASHES(".*mach_exception_reply.*"))
422 {
423 	test_exc_reply_type(ReplyWithReplyPort);
424 }
425 T_DECL(mach_exc_ReplyWithReplyPortMove, "exception server reply with reply port as MOVE_SEND",
426     T_META_CHECK_LEAKS(false), T_META_IGNORECRASHES(".*mach_exception_reply.*"))
427 {
428 	test_exc_reply_type(ReplyWithReplyPortMove);
429 }
430 T_DECL(mach_exc_ReplyWithReplyPortCplxBit, "exception server reply with reply port and complex bit set",
431     T_META_CHECK_LEAKS(false), T_META_IGNORECRASHES(".*mach_exception_reply.*"))
432 {
433 	test_exc_reply_type(ReplyWithReplyPortCplxBit);
434 }
435 T_DECL(mach_exc_ReplyWithReplyPortMoveCplxBit, "exception server reply with reply port as MOVE_SEND and complex bit set",
436     T_META_CHECK_LEAKS(false), T_META_IGNORECRASHES(".*mach_exception_reply.*"))
437 {
438 	test_exc_reply_type(ReplyWithReplyPortMoveCplxBit);
439 }
440 T_DECL(mach_exc_ReplyWithOOLPort, "exception server reply with OOL port descriptor",
441     T_META_CHECK_LEAKS(false), T_META_IGNORECRASHES(".*mach_exception_reply.*"))
442 {
443 	test_exc_reply_type(ReplyWithPortDesc);
444 }
445 T_DECL(mach_exc_ReplyWithOOLDesc, "exception server reply with OOL memory descriptor",
446     T_META_CHECK_LEAKS(false), T_META_IGNORECRASHES(".*mach_exception_reply.*"))
447 {
448 	test_exc_reply_type(ReplyWithOOLDesc);
449 }
450 T_DECL(mach_exc_ReplyWithVoucher, "exception server reply with a voucher",
451     T_META_CHECK_LEAKS(false), T_META_IGNORECRASHES(".*mach_exception_reply.*"))
452 {
453 	test_exc_reply_type(ReplyWithVoucher);
454 }
455 T_DECL(mach_exc_ReplyWithVoucherGarbage, "exception server reply with bits in msgh_voucher_port",
456     T_META_CHECK_LEAKS(false), T_META_IGNORECRASHES(".*mach_exception_reply.*"))
457 {
458 	test_exc_reply_type(ReplyWithVoucherGarbage);
459 }
460