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