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