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