]>
Commit | Line | Data |
---|---|---|
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 | ||
13 | T_GLOBAL_META(T_META_RUN_CONCURRENTLY(true)); | |
14 | ||
15 | #define MSG 1024 | |
16 | #define PG_ALLOC 4096 | |
17 | ||
18 | typedef enum { | |
19 | ReplyWithNoError, | |
20 | ReplyWithReplyPort, | |
21 | ReplyWithReplyPortMove, | |
22 | ReplyWithReplyPortCplxBit, | |
23 | ReplyWithReplyPortMoveCplxBit, | |
24 | ReplyWithPortDesc, | |
25 | ReplyWithOOLDesc, | |
26 | ReplyWithVoucher, | |
27 | ReplyWithVoucherGarbage | |
28 | } ReplyType; | |
29 | ||
30 | struct exc_thread_arg { | |
31 | ReplyType rt; | |
32 | mach_port_t port; | |
33 | }; | |
34 | ||
35 | static const char * | |
36 | reply_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 | ||
60 | static mach_voucher_t | |
61 | create_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 | ||
91 | static void * | |
92 | handle_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 | ||
327 | static sigjmp_buf jb; | |
328 | static int *bad_pointer = NULL; | |
329 | static int s_sigmask = 0; | |
330 | ||
331 | static void | |
332 | signal_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 | ||
341 | static int | |
342 | handle_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 | ||
364 | static void | |
365 | test_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 | ||
413 | T_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 | } | |
418 | T_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 | } | |
423 | T_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 | } | |
428 | T_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 | } | |
433 | T_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 | } | |
438 | T_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 | } | |
443 | T_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 | } | |
448 | T_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 | } | |
453 | T_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 | } |