1 /*
2 * Copyright (c) 1999-2013 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28 /*
29 * Mach Operating System
30 * Copyright (c) 1991,1990,1989 Carnegie Mellon University
31 * All Rights Reserved.
32 *
33 * Permission to use, copy, modify and distribute this software and its
34 * documentation is hereby granted, provided that both the copyright
35 * notice and this permission notice appear in all copies of the
36 * software, derivative works or modified versions, and any portions
37 * thereof, and that both notices appear in supporting documentation.
38 *
39 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
40 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
41 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
42 *
43 * Carnegie Mellon requests users of this software to return to
44 *
45 * Software Distribution Coordinator or [email protected]
46 * School of Computer Science
47 * Carnegie Mellon University
48 * Pittsburgh PA 15213-3890
49 *
50 * any improvements or extensions that they make and grant Carnegie Mellon
51 * the rights to redistribute these changes.
52 */
53
54 #include <stdlib.h>
55 #include <mach/mach.h>
56 #include <mach/boolean.h>
57 #include <mach/kern_return.h>
58 #include <mach/message.h>
59 #include <mach/mig_errors.h>
60 #include <mach/vm_statistics.h>
61 #include <TargetConditionals.h>
62 #include <os/tsd.h>
63 #include <machine/cpu_capabilities.h>
64
65
66 extern int proc_importance_assertion_begin_with_msg(mach_msg_header_t * msg, mach_msg_trailer_t * trailer, uint64_t * assertion_handlep);
67 extern int proc_importance_assertion_complete(uint64_t assertion_handle);
68
69 extern mach_msg_size_t voucher_mach_msg_fill_aux(mach_msg_aux_header_t *aux_hdr, mach_msg_size_t sz);
70 extern boolean_t voucher_mach_msg_fill_aux_supported(void);
71
72 #define LIBMACH_OPTIONS (MACH_SEND_INTERRUPT|MACH_RCV_INTERRUPT)
73 #define LIBMACH_OPTIONS64 (MACH64_SEND_INTERRUPT|MACH64_RCV_INTERRUPT)
74
75 static inline mach_msg_option64_t
mach_msg_options_after_interruption(mach_msg_option64_t option64)76 mach_msg_options_after_interruption(mach_msg_option64_t option64)
77 {
78 if ((option64 & MACH64_SEND_MSG) && (option64 & MACH64_RCV_MSG)) {
79 /*
80 * If MACH_RCV_SYNC_WAIT was passed for a combined send-receive it must
81 * be cleared for receive-only retries, as the kernel has no way to
82 * discover the destination.
83 */
84 option64 &= ~MACH64_RCV_SYNC_WAIT;
85 }
86 option64 &= ~(LIBMACH_OPTIONS64 | MACH64_SEND_MSG);
87 return option64;
88 }
89
90
91 #if defined(__LP64__) || defined(__arm64__)
92 mach_msg_return_t
mach_msg2_internal(void * data,mach_msg_option64_t option64,uint64_t msgh_bits_and_send_size,uint64_t msgh_remote_and_local_port,uint64_t msgh_voucher_and_id,uint64_t desc_count_and_rcv_name,uint64_t rcv_size_and_priority,uint64_t timeout)93 mach_msg2_internal(
94 void *data,
95 mach_msg_option64_t option64,
96 uint64_t msgh_bits_and_send_size,
97 uint64_t msgh_remote_and_local_port,
98 uint64_t msgh_voucher_and_id,
99 uint64_t desc_count_and_rcv_name,
100 uint64_t rcv_size_and_priority,
101 uint64_t timeout)
102 {
103 mach_msg_return_t mr;
104
105
106 mr = mach_msg2_trap(data,
107 option64 & ~LIBMACH_OPTIONS64,
108 msgh_bits_and_send_size,
109 msgh_remote_and_local_port,
110 msgh_voucher_and_id,
111 desc_count_and_rcv_name,
112 rcv_size_and_priority,
113 timeout);
114
115
116 if (mr == MACH_MSG_SUCCESS) {
117 return MACH_MSG_SUCCESS;
118 }
119
120 if ((option64 & MACH64_SEND_INTERRUPT) == 0) {
121 while (mr == MACH_SEND_INTERRUPTED) {
122 mr = mach_msg2_trap(data,
123 option64 & ~LIBMACH_OPTIONS64,
124 msgh_bits_and_send_size,
125 msgh_remote_and_local_port,
126 msgh_voucher_and_id,
127 desc_count_and_rcv_name,
128 rcv_size_and_priority,
129 timeout);
130 }
131 }
132
133 if ((option64 & MACH64_RCV_INTERRUPT) == 0) {
134 while (mr == MACH_RCV_INTERRUPTED) {
135 mr = mach_msg2_trap(data,
136 mach_msg_options_after_interruption(option64),
137 msgh_bits_and_send_size & 0xffffffffull, /* zero send size */
138 msgh_remote_and_local_port,
139 msgh_voucher_and_id,
140 desc_count_and_rcv_name,
141 rcv_size_and_priority,
142 timeout);
143 }
144 }
145
146 return mr;
147
148 }
149 #endif
150
151 /*
152 * Routine: mach_msg
153 * Purpose:
154 * Send and/or receive a message. If the message operation
155 * is interrupted, and the user did not request an indication
156 * of that fact, then restart the appropriate parts of the
157 * operation.
158 */
159 mach_msg_return_t
mach_msg(mach_msg_header_t * msg,mach_msg_option_t option,mach_msg_size_t send_size,mach_msg_size_t rcv_size,mach_port_t rcv_name,mach_msg_timeout_t timeout,mach_port_t notify)160 mach_msg(
161 mach_msg_header_t *msg,
162 mach_msg_option_t option,
163 mach_msg_size_t send_size,
164 mach_msg_size_t rcv_size,
165 mach_port_t rcv_name,
166 mach_msg_timeout_t timeout,
167 mach_port_t notify)
168 {
169 return mach_msg_overwrite(msg, option, send_size, rcv_size, rcv_name,
170 timeout, notify, NULL, 0);
171 }
172
173 /*
174 * Routine: mach_msg_overwrite
175 * Purpose:
176 * Send and/or receive a message. If the message operation
177 * is interrupted, and the user did not request an indication
178 * of that fact, then restart the appropriate parts of the
179 * operation.
180 *
181 * Distinct send and receive buffers may be specified. If
182 * no separate receive buffer is specified, the msg parameter
183 * will be used for both send and receive operations.
184 *
185 * In addition to a distinct receive buffer, that buffer may
186 * already contain scatter control information to direct the
187 * receiving of the message.
188 */
189 mach_msg_return_t
mach_msg_overwrite(mach_msg_header_t * msg,mach_msg_option_t option,mach_msg_size_t send_size,mach_msg_size_t rcv_limit,mach_port_t rcv_name,mach_msg_timeout_t timeout,mach_port_t notify,mach_msg_header_t * rcv_msg,__unused mach_msg_size_t rcv_scatter_size)190 mach_msg_overwrite(
191 mach_msg_header_t *msg,
192 mach_msg_option_t option,
193 mach_msg_size_t send_size,
194 mach_msg_size_t rcv_limit,
195 mach_port_t rcv_name,
196 mach_msg_timeout_t timeout,
197 mach_port_t notify,
198 mach_msg_header_t *rcv_msg,
199 __unused mach_msg_size_t rcv_scatter_size)
200 {
201 mach_msg_return_t mr;
202
203 #if defined(__LP64__) || defined(__arm64__)
204 mach_msg_aux_header_t *aux;
205 mach_msg_vector_t vecs[2];
206
207 uint8_t inline_aux_buf[LIBSYSCALL_MSGV_AUX_MAX_SIZE];
208
209 mach_msg_priority_t priority = 0;
210 mach_msg_size_t aux_sz = 0;
211 mach_msg_option64_t option64 = (mach_msg_option64_t)option;
212
213 aux = (mach_msg_aux_header_t *)inline_aux_buf;
214
215
216 /*
217 * For the following cases, we have to use vector send/receive; otherwise
218 * we can use scalar mach_msg2() for a slightly better performance due to
219 * fewer copyio operations.
220 *
221 * 1. Attempting to receive voucher.
222 * 2. Caller provides a different receive msg buffer. (scalar mach_msg2()
223 * does not support mach_msg_overwrite()).
224 * 3. Libdispatch has an aux data to send.
225 */
226
227 /*
228 * voucher_mach_msg_fill_aux_supported() is FALSE if libsyscall is linked against
229 * an old libdispatch (e.g. old Simulator on new system), or if we are building
230 * Libsyscall_static due to weak linking (e.g. dyld).
231 *
232 * Hoist this check to guard the malloc().
233 *
234 * See: _libc_weak_funcptr.c
235 */
236 if (voucher_mach_msg_fill_aux_supported() &&
237 (option64 & MACH64_RCV_MSG) && (option64 & MACH64_RCV_VOUCHER)) {
238 option64 |= MACH64_MSG_VECTOR;
239 if (!(aux = _os_tsd_get_direct(__TSD_MACH_MSG_AUX))) {
240 aux = malloc(LIBSYSCALL_MSGV_AUX_MAX_SIZE);
241 if (aux) {
242 /* will be freed during TSD teardown */
243 _os_tsd_set_direct(__TSD_MACH_MSG_AUX, aux);
244 } else {
245 /* revert to use on stack buffer */
246 aux = (mach_msg_aux_header_t *)inline_aux_buf;
247 option64 &= ~MACH64_MSG_VECTOR;
248 }
249 }
250 }
251
252 if ((option64 & MACH64_RCV_MSG) && rcv_msg != NULL) {
253 option64 |= MACH64_MSG_VECTOR;
254 }
255
256 if ((option64 & MACH64_SEND_MSG) &&
257 /* this returns 0 for Libsyscall_static due to weak linking */
258 ((aux_sz = voucher_mach_msg_fill_aux(aux, LIBSYSCALL_MSGV_AUX_MAX_SIZE)) != 0)) {
259 option64 |= MACH64_MSG_VECTOR;
260 }
261
262 if (option64 & MACH64_MSG_VECTOR) {
263 vecs[MACH_MSGV_IDX_MSG] = (mach_msg_vector_t){
264 .msgv_data = msg,
265 .msgv_rcv_addr = rcv_msg, /* if 0, just use msg as rcv address */
266 .msgv_send_size = send_size,
267 .msgv_rcv_size = rcv_limit,
268 };
269 vecs[MACH_MSGV_IDX_AUX] = (mach_msg_vector_t){
270 .msgv_data = aux,
271 .msgv_rcv_addr = 0,
272 .msgv_send_size = aux_sz,
273 .msgv_rcv_size = LIBSYSCALL_MSGV_AUX_MAX_SIZE,
274 };
275 }
276
277 if (option64 & MACH64_SEND_MSG) {
278 priority = (mach_msg_priority_t)notify;
279 }
280
281 if ((option64 & MACH64_RCV_MSG) &&
282 !(option64 & MACH64_SEND_MSG) &&
283 (option64 & MACH64_RCV_SYNC_WAIT)) {
284 msg->msgh_remote_port = notify;
285 }
286
287 #if TARGET_OS_OSX
288 if (voucher_mach_msg_fill_aux_supported()) {
289 option64 |= MACH64_SEND_MQ_CALL;
290 } else {
291 /*
292 * Special flag for old simulators on new system to skip mach_msg2()
293 * CFI enforcement.
294 */
295 option64 |= MACH64_SEND_ANY;
296 }
297 #else
298 option64 |= MACH64_SEND_MQ_CALL;
299 #endif /* TARGET_OS_OSX */
300
301 if (option64 & MACH64_MSG_VECTOR) {
302 mr = mach_msg2(vecs, option64, *msg, 2, 2,
303 rcv_name, timeout, priority);
304 } else {
305 mr = mach_msg2(msg, option64, *msg, send_size,
306 rcv_limit, rcv_name, timeout, priority);
307 }
308
309 return mr;
310 #endif /* defined(__LP64__) || defined(__arm64__) */
311
312 }
313
314 mach_msg_return_t
mach_msg_send(mach_msg_header_t * msg)315 mach_msg_send(mach_msg_header_t *msg)
316 {
317 return mach_msg(msg, MACH_SEND_MSG,
318 msg->msgh_size, 0, MACH_PORT_NULL,
319 MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
320 }
321
322 mach_msg_return_t
mach_msg_receive(mach_msg_header_t * msg)323 mach_msg_receive(mach_msg_header_t *msg)
324 {
325 return mach_msg(msg, MACH_RCV_MSG,
326 0, msg->msgh_size, msg->msgh_local_port,
327 MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
328 }
329
330
331 static void
mach_msg_destroy_port(mach_port_t port,mach_msg_type_name_t type)332 mach_msg_destroy_port(mach_port_t port, mach_msg_type_name_t type)
333 {
334 if (MACH_PORT_VALID(port)) {
335 switch (type) {
336 case MACH_MSG_TYPE_MOVE_SEND:
337 case MACH_MSG_TYPE_MOVE_SEND_ONCE:
338 /* destroy the send/send-once right */
339 (void) mach_port_deallocate(mach_task_self_, port);
340 break;
341
342 case MACH_MSG_TYPE_MOVE_RECEIVE:
343 /* destroy the receive right */
344 (void) mach_port_mod_refs(mach_task_self_, port,
345 MACH_PORT_RIGHT_RECEIVE, -1);
346 break;
347
348 case MACH_MSG_TYPE_MAKE_SEND:
349 /* create a send right and then destroy it */
350 (void) mach_port_insert_right(mach_task_self_, port,
351 port, MACH_MSG_TYPE_MAKE_SEND);
352 (void) mach_port_deallocate(mach_task_self_, port);
353 break;
354
355 case MACH_MSG_TYPE_MAKE_SEND_ONCE:
356 /* create a send-once right and then destroy it */
357 (void) mach_port_extract_right(mach_task_self_, port,
358 MACH_MSG_TYPE_MAKE_SEND_ONCE,
359 &port, &type);
360 (void) mach_port_deallocate(mach_task_self_, port);
361 break;
362 }
363 }
364 }
365
366 static void
mach_msg_destroy_memory(vm_offset_t addr,vm_size_t size)367 mach_msg_destroy_memory(vm_offset_t addr, vm_size_t size)
368 {
369 if (size != 0) {
370 (void) vm_deallocate(mach_task_self_, addr, size);
371 }
372 }
373
374
375 /*
376 * Routine: mach_msg_destroy
377 * Purpose:
378 * mach_msg_destroy is useful in two contexts.
379 *
380 * First, it can deallocate all port rights and
381 * out-of-line memory in a received message.
382 * When a server receives a request it doesn't want,
383 * it needs this functionality.
384 *
385 * Second, it can mimic the side-effects of a msg-send
386 * operation. The effect is as if the message were sent
387 * and then destroyed inside the kernel. When a server
388 * can't send a reply (because the client died),
389 * it needs this functionality.
390 */
391 void
mach_msg_destroy(mach_msg_header_t * msg)392 mach_msg_destroy(mach_msg_header_t *msg)
393 {
394 mach_msg_bits_t mbits = msg->msgh_bits;
395
396 /*
397 * The msgh_local_port field doesn't hold a port right.
398 * The receive operation consumes the destination port right.
399 */
400
401 mach_msg_destroy_port(msg->msgh_remote_port, MACH_MSGH_BITS_REMOTE(mbits));
402 mach_msg_destroy_port(msg->msgh_voucher_port, MACH_MSGH_BITS_VOUCHER(mbits));
403
404 if (mbits & MACH_MSGH_BITS_COMPLEX) {
405 mach_msg_base_t *base;
406 mach_msg_type_number_t count, i;
407 mach_msg_descriptor_t *daddr;
408
409 base = (mach_msg_base_t *) msg;
410 count = base->body.msgh_descriptor_count;
411
412 daddr = (mach_msg_descriptor_t *) (base + 1);
413 for (i = 0; i < count; i++) {
414 switch (daddr->type.type) {
415 case MACH_MSG_PORT_DESCRIPTOR: {
416 mach_msg_port_descriptor_t *dsc;
417
418 /*
419 * Destroy port rights carried in the message
420 */
421 dsc = &daddr->port;
422 mach_msg_destroy_port(dsc->name, dsc->disposition);
423 daddr = (mach_msg_descriptor_t *)(dsc + 1);
424 break;
425 }
426
427 case MACH_MSG_OOL_DESCRIPTOR: {
428 mach_msg_ool_descriptor_t *dsc;
429
430 /*
431 * Destroy memory carried in the message
432 */
433 dsc = &daddr->out_of_line;
434 if (dsc->deallocate) {
435 mach_msg_destroy_memory((vm_offset_t)dsc->address,
436 dsc->size);
437 }
438 daddr = (mach_msg_descriptor_t *)(dsc + 1);
439 break;
440 }
441
442 case MACH_MSG_OOL_VOLATILE_DESCRIPTOR: {
443 mach_msg_ool_descriptor_t *dsc;
444
445 /*
446 * Just skip it.
447 */
448 dsc = &daddr->out_of_line;
449 daddr = (mach_msg_descriptor_t *)(dsc + 1);
450 break;
451 }
452
453 case MACH_MSG_OOL_PORTS_DESCRIPTOR: {
454 mach_port_t *ports;
455 mach_msg_ool_ports_descriptor_t *dsc;
456 mach_msg_type_number_t j;
457
458 /*
459 * Destroy port rights carried in the message
460 */
461 dsc = &daddr->ool_ports;
462 ports = (mach_port_t *) dsc->address;
463 for (j = 0; j < dsc->count; j++, ports++) {
464 mach_msg_destroy_port(*ports, dsc->disposition);
465 }
466
467 /*
468 * Destroy memory carried in the message
469 */
470 if (dsc->deallocate) {
471 mach_msg_destroy_memory((vm_offset_t)dsc->address,
472 dsc->count * sizeof(mach_port_t));
473 }
474 daddr = (mach_msg_descriptor_t *)(dsc + 1);
475 break;
476 }
477
478 case MACH_MSG_GUARDED_PORT_DESCRIPTOR: {
479 mach_msg_guarded_port_descriptor_t *dsc;
480 mach_msg_guard_flags_t flags;
481 /*
482 * Destroy port right carried in the message
483 */
484 dsc = &daddr->guarded_port;
485 flags = dsc->flags;
486 if ((flags & MACH_MSG_GUARD_FLAGS_UNGUARDED_ON_SEND) == 0) {
487 /* Need to unguard before destroying the port */
488 mach_port_unguard(mach_task_self_, dsc->name, (uint64_t)dsc->context);
489 }
490 mach_msg_destroy_port(dsc->name, dsc->disposition);
491 daddr = (mach_msg_descriptor_t *)(dsc + 1);
492 break;
493 }
494 }
495 }
496 }
497 }
498
499 static inline boolean_t
mach_msg_server_is_recoverable_send_error(kern_return_t kr)500 mach_msg_server_is_recoverable_send_error(kern_return_t kr)
501 {
502 switch (kr) {
503 case MACH_SEND_INVALID_DEST:
504 case MACH_SEND_TIMED_OUT:
505 case MACH_SEND_INTERRUPTED:
506 return TRUE;
507 default:
508 /*
509 * Other errors mean that the message may have been partially destroyed
510 * by the kernel, and these can't be recovered and may leak resources.
511 */
512 return FALSE;
513 }
514 }
515
516 static kern_return_t
mach_msg_server_mig_return_code(mig_reply_error_t * reply)517 mach_msg_server_mig_return_code(mig_reply_error_t *reply)
518 {
519 /*
520 * If the message is complex, it is assumed that the reply was successful,
521 * as the RetCode is where the count of out of line descriptors is.
522 *
523 * If not, we read RetCode.
524 */
525 if (reply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) {
526 return KERN_SUCCESS;
527 }
528 return reply->RetCode;
529 }
530
531 static void
mach_msg_server_consume_unsent_message(mach_msg_header_t * hdr)532 mach_msg_server_consume_unsent_message(mach_msg_header_t *hdr)
533 {
534 /* mach_msg_destroy doesn't handle the local port */
535 mach_port_t port = hdr->msgh_local_port;
536 if (MACH_PORT_VALID(port)) {
537 switch (MACH_MSGH_BITS_LOCAL(hdr->msgh_bits)) {
538 case MACH_MSG_TYPE_MOVE_SEND:
539 case MACH_MSG_TYPE_MOVE_SEND_ONCE:
540 /* destroy the send/send-once right */
541 (void) mach_port_deallocate(mach_task_self_, port);
542 hdr->msgh_local_port = MACH_PORT_NULL;
543 break;
544 }
545 }
546 mach_msg_destroy(hdr);
547 }
548
549 /*
550 * Routine: mach_msg_server_once
551 * Purpose:
552 * A simple generic server function. It allows more flexibility
553 * than mach_msg_server by processing only one message request
554 * and then returning to the user. Note that more in the way
555 * of error codes are returned to the user; specifically, any
556 * failing error from mach_msg calls will be returned
557 * (though errors from the demux routine or the routine it
558 * calls will not be).
559 */
560 mach_msg_return_t
mach_msg_server_once(boolean_t (* demux)(mach_msg_header_t *,mach_msg_header_t *),mach_msg_size_t max_size,mach_port_t rcv_name,mach_msg_options_t options)561 mach_msg_server_once(
562 boolean_t (*demux)(mach_msg_header_t *, mach_msg_header_t *),
563 mach_msg_size_t max_size,
564 mach_port_t rcv_name,
565 mach_msg_options_t options)
566 {
567 mig_reply_error_t *bufRequest, *bufReply;
568 mach_msg_size_t request_size;
569 mach_msg_size_t request_alloc;
570 mach_msg_size_t trailer_alloc;
571 mach_msg_size_t reply_alloc;
572 mach_msg_return_t mr;
573 kern_return_t kr;
574 mach_port_t self = mach_task_self_;
575 voucher_mach_msg_state_t old_state = VOUCHER_MACH_MSG_STATE_UNCHANGED;
576
577 options &= ~(MACH_SEND_MSG | MACH_RCV_MSG | MACH_RCV_VOUCHER);
578
579 trailer_alloc = REQUESTED_TRAILER_SIZE(options);
580 request_alloc = (mach_msg_size_t)round_page(max_size + trailer_alloc);
581
582 request_size = (options & MACH_RCV_LARGE) ?
583 request_alloc : max_size + trailer_alloc;
584
585 reply_alloc = (mach_msg_size_t)round_page((options & MACH_SEND_TRAILER) ?
586 (max_size + MAX_TRAILER_SIZE) :
587 max_size);
588
589 kr = vm_allocate(self,
590 (vm_address_t *)&bufReply,
591 reply_alloc,
592 VM_MAKE_TAG(VM_MEMORY_MACH_MSG) | TRUE);
593 if (kr != KERN_SUCCESS) {
594 return kr;
595 }
596
597 for (;;) {
598 mach_msg_size_t new_request_alloc;
599
600 kr = vm_allocate(self,
601 (vm_address_t *)&bufRequest,
602 request_alloc,
603 VM_MAKE_TAG(VM_MEMORY_MACH_MSG) | TRUE);
604 if (kr != KERN_SUCCESS) {
605 vm_deallocate(self,
606 (vm_address_t)bufReply,
607 reply_alloc);
608 return kr;
609 }
610
611 mr = mach_msg(&bufRequest->Head, MACH_RCV_MSG | MACH_RCV_VOUCHER | options,
612 0, request_size, rcv_name,
613 MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
614
615 if (!((mr == MACH_RCV_TOO_LARGE) && (options & MACH_RCV_LARGE))) {
616 break;
617 }
618
619 new_request_alloc = (mach_msg_size_t)round_page(bufRequest->Head.msgh_size +
620 trailer_alloc);
621 vm_deallocate(self,
622 (vm_address_t) bufRequest,
623 request_alloc);
624 request_size = request_alloc = new_request_alloc;
625 }
626
627 if (mr == MACH_MSG_SUCCESS) {
628 /* we have a request message */
629
630 old_state = voucher_mach_msg_adopt(&bufRequest->Head);
631
632 (void) (*demux)(&bufRequest->Head, &bufReply->Head);
633
634 switch (mach_msg_server_mig_return_code(bufReply)) {
635 case KERN_SUCCESS:
636 break;
637 case MIG_NO_REPLY:
638 bufReply->Head.msgh_remote_port = MACH_PORT_NULL;
639 break;
640 default:
641 /*
642 * destroy the request - but not the reply port
643 * (MIG moved it into the bufReply).
644 */
645 bufRequest->Head.msgh_remote_port = MACH_PORT_NULL;
646 mach_msg_destroy(&bufRequest->Head);
647 }
648
649 /*
650 * We don't want to block indefinitely because the client
651 * isn't receiving messages from the reply port.
652 * If we have a send-once right for the reply port, then
653 * this isn't a concern because the send won't block.
654 * If we have a send right, we need to use MACH_SEND_TIMEOUT.
655 * To avoid falling off the kernel's fast RPC path unnecessarily,
656 * we only supply MACH_SEND_TIMEOUT when absolutely necessary.
657 */
658 if (bufReply->Head.msgh_remote_port != MACH_PORT_NULL) {
659 mr = mach_msg(&bufReply->Head,
660 (MACH_MSGH_BITS_REMOTE(bufReply->Head.msgh_bits) ==
661 MACH_MSG_TYPE_MOVE_SEND_ONCE) ?
662 MACH_SEND_MSG | options :
663 MACH_SEND_MSG | MACH_SEND_TIMEOUT | options,
664 bufReply->Head.msgh_size, 0, MACH_PORT_NULL,
665 MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
666
667 if (mach_msg_server_is_recoverable_send_error(mr)) {
668 mach_msg_server_consume_unsent_message(&bufReply->Head);
669 mr = MACH_MSG_SUCCESS;
670 }
671 }
672 }
673
674 voucher_mach_msg_revert(old_state);
675
676 (void)vm_deallocate(self,
677 (vm_address_t) bufRequest,
678 request_alloc);
679 (void)vm_deallocate(self,
680 (vm_address_t) bufReply,
681 reply_alloc);
682 return mr;
683 }
684
685 /*
686 * Routine: mach_msg_server
687 * Purpose:
688 * A simple generic server function. Note that changes here
689 * should be considered for duplication above.
690 */
691 mach_msg_return_t
mach_msg_server(boolean_t (* demux)(mach_msg_header_t *,mach_msg_header_t *),mach_msg_size_t max_size,mach_port_t rcv_name,mach_msg_options_t options)692 mach_msg_server(
693 boolean_t (*demux)(mach_msg_header_t *, mach_msg_header_t *),
694 mach_msg_size_t max_size,
695 mach_port_t rcv_name,
696 mach_msg_options_t options)
697 {
698 mig_reply_error_t *bufRequest, *bufReply;
699 mach_msg_size_t request_size;
700 mach_msg_size_t new_request_alloc;
701 mach_msg_size_t request_alloc;
702 mach_msg_size_t trailer_alloc;
703 mach_msg_size_t reply_alloc;
704 mach_msg_return_t mr;
705 kern_return_t kr;
706 mach_port_t self = mach_task_self_;
707 voucher_mach_msg_state_t old_state = VOUCHER_MACH_MSG_STATE_UNCHANGED;
708 boolean_t buffers_swapped = FALSE;
709
710 options &= ~(MACH_SEND_MSG | MACH_RCV_MSG | MACH_RCV_VOUCHER);
711
712 reply_alloc = (mach_msg_size_t)round_page((options & MACH_SEND_TRAILER) ?
713 (max_size + MAX_TRAILER_SIZE) : max_size);
714
715 kr = vm_allocate(self,
716 (vm_address_t *)&bufReply,
717 reply_alloc,
718 VM_MAKE_TAG(VM_MEMORY_MACH_MSG) | TRUE);
719 if (kr != KERN_SUCCESS) {
720 return kr;
721 }
722
723 request_alloc = 0;
724 trailer_alloc = REQUESTED_TRAILER_SIZE(options);
725 new_request_alloc = (mach_msg_size_t)round_page(max_size + trailer_alloc);
726
727 request_size = (options & MACH_RCV_LARGE) ?
728 new_request_alloc : max_size + trailer_alloc;
729
730 for (;;) {
731 if (request_alloc < new_request_alloc) {
732 request_alloc = new_request_alloc;
733 kr = vm_allocate(self,
734 (vm_address_t *)&bufRequest,
735 request_alloc,
736 VM_MAKE_TAG(VM_MEMORY_MACH_MSG) | TRUE);
737 if (kr != KERN_SUCCESS) {
738 vm_deallocate(self,
739 (vm_address_t)bufReply,
740 reply_alloc);
741 return kr;
742 }
743 }
744
745 mr = mach_msg(&bufRequest->Head, MACH_RCV_MSG | MACH_RCV_VOUCHER | options,
746 0, request_size, rcv_name,
747 MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
748
749 while (mr == MACH_MSG_SUCCESS) {
750 /* we have another request message */
751
752 buffers_swapped = FALSE;
753 old_state = voucher_mach_msg_adopt(&bufRequest->Head);
754 bufReply->Head = (mach_msg_header_t){};
755
756 (void) (*demux)(&bufRequest->Head, &bufReply->Head);
757
758 switch (mach_msg_server_mig_return_code(bufReply)) {
759 case KERN_SUCCESS:
760 break;
761 case MIG_NO_REPLY:
762 bufReply->Head.msgh_remote_port = MACH_PORT_NULL;
763 break;
764 default:
765 /*
766 * destroy the request - but not the reply port
767 * (MIG moved it into the bufReply).
768 */
769 bufRequest->Head.msgh_remote_port = MACH_PORT_NULL;
770 mach_msg_destroy(&bufRequest->Head);
771 }
772
773 /*
774 * We don't want to block indefinitely because the client
775 * isn't receiving messages from the reply port.
776 * If we have a send-once right for the reply port, then
777 * this isn't a concern because the send won't block.
778 * If we have a send right, we need to use MACH_SEND_TIMEOUT.
779 * To avoid falling off the kernel's fast RPC path,
780 * we only supply MACH_SEND_TIMEOUT when absolutely necessary.
781 */
782 if (bufReply->Head.msgh_remote_port != MACH_PORT_NULL) {
783 if (request_alloc == reply_alloc) {
784 mig_reply_error_t *bufTemp;
785
786 mr = mach_msg(
787 &bufReply->Head,
788 (MACH_MSGH_BITS_REMOTE(bufReply->Head.msgh_bits) ==
789 MACH_MSG_TYPE_MOVE_SEND_ONCE) ?
790 MACH_SEND_MSG | MACH_RCV_MSG | MACH_RCV_TIMEOUT | MACH_RCV_VOUCHER | options :
791 MACH_SEND_MSG | MACH_RCV_MSG | MACH_SEND_TIMEOUT | MACH_RCV_TIMEOUT | MACH_RCV_VOUCHER | options,
792 bufReply->Head.msgh_size, request_size, rcv_name,
793 MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
794
795 /* swap request and reply */
796 bufTemp = bufRequest;
797 bufRequest = bufReply;
798 bufReply = bufTemp;
799 buffers_swapped = TRUE;
800 } else {
801 mr = mach_msg_overwrite(
802 &bufReply->Head,
803 (MACH_MSGH_BITS_REMOTE(bufReply->Head.msgh_bits) ==
804 MACH_MSG_TYPE_MOVE_SEND_ONCE) ?
805 MACH_SEND_MSG | MACH_RCV_MSG | MACH_RCV_TIMEOUT | MACH_RCV_VOUCHER | options :
806 MACH_SEND_MSG | MACH_RCV_MSG | MACH_SEND_TIMEOUT | MACH_RCV_TIMEOUT | MACH_RCV_VOUCHER | options,
807 bufReply->Head.msgh_size, request_size, rcv_name,
808 MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL,
809 &bufRequest->Head, 0);
810 }
811
812 /*
813 * Need to destroy the reply msg in case if there was a send timeout or
814 * invalid destination. The reply msg would be swapped with request msg
815 * if buffers_swapped is true, thus destroy request msg instead of
816 * reply msg in such cases.
817 */
818 if (mach_msg_server_is_recoverable_send_error(mr)) {
819 if (buffers_swapped) {
820 mach_msg_server_consume_unsent_message(&bufRequest->Head);
821 } else {
822 mach_msg_server_consume_unsent_message(&bufReply->Head);
823 }
824 } else if (mr != MACH_RCV_TIMED_OUT) {
825 voucher_mach_msg_revert(old_state);
826 old_state = VOUCHER_MACH_MSG_STATE_UNCHANGED;
827
828 continue;
829 }
830 }
831 voucher_mach_msg_revert(old_state);
832 old_state = VOUCHER_MACH_MSG_STATE_UNCHANGED;
833
834 mr = mach_msg(&bufRequest->Head, MACH_RCV_MSG | MACH_RCV_VOUCHER | options,
835 0, request_size, rcv_name,
836 MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
837 } /* while (mr == MACH_MSG_SUCCESS) */
838
839 if ((mr == MACH_RCV_TOO_LARGE) && (options & MACH_RCV_LARGE)) {
840 new_request_alloc = (mach_msg_size_t)round_page(bufRequest->Head.msgh_size +
841 trailer_alloc);
842 request_size = new_request_alloc;
843 vm_deallocate(self,
844 (vm_address_t) bufRequest,
845 request_alloc);
846 continue;
847 }
848
849 break;
850 } /* for(;;) */
851
852 (void)vm_deallocate(self,
853 (vm_address_t) bufRequest,
854 request_alloc);
855 (void)vm_deallocate(self,
856 (vm_address_t) bufReply,
857 reply_alloc);
858 return mr;
859 }
860
861 /*
862 * Routine: mach_msg_server_importance
863 * Purpose:
864 * A simple generic server function which handles importance
865 * promotion assertions for adaptive daemons.
866 */
867 mach_msg_return_t
mach_msg_server_importance(boolean_t (* demux)(mach_msg_header_t *,mach_msg_header_t *),mach_msg_size_t max_size,mach_port_t rcv_name,mach_msg_options_t options)868 mach_msg_server_importance(
869 boolean_t (*demux)(mach_msg_header_t *, mach_msg_header_t *),
870 mach_msg_size_t max_size,
871 mach_port_t rcv_name,
872 mach_msg_options_t options)
873 {
874 return mach_msg_server(demux, max_size, rcv_name, options);
875 }
876
877 kern_return_t
mach_voucher_deallocate(mach_voucher_t voucher)878 mach_voucher_deallocate(
879 mach_voucher_t voucher)
880 {
881 return mach_port_deallocate(mach_task_self(), voucher);
882 }
883
884 #undef mach_msg_priority_is_pthread_priority
885 int
mach_msg_priority_is_pthread_priority(mach_msg_priority_t pri)886 mach_msg_priority_is_pthread_priority(mach_msg_priority_t pri)
887 {
888 return mach_msg_priority_is_pthread_priority_inline(pri);
889 }
890
891 #undef mach_msg_priority_encode
892 mach_msg_priority_t
mach_msg_priority_encode(mach_msg_qos_t override_qos,mach_msg_qos_t qos,int relpri)893 mach_msg_priority_encode(mach_msg_qos_t override_qos, mach_msg_qos_t qos, int relpri)
894 {
895 return mach_msg_priority_encode_inline(override_qos, qos, relpri);
896 }
897
898 #undef mach_msg_priority_overide_qos
899 mach_msg_qos_t
mach_msg_priority_overide_qos(mach_msg_priority_t pri)900 mach_msg_priority_overide_qos(mach_msg_priority_t pri)
901 {
902 return mach_msg_priority_overide_qos_inline(pri);
903 }
904
905 #undef mach_msg_priority_qos
906 mach_msg_qos_t
mach_msg_priority_qos(mach_msg_priority_t pri)907 mach_msg_priority_qos(mach_msg_priority_t pri)
908 {
909 return mach_msg_priority_qos_inline(pri);
910 }
911
912 #undef mach_msg_priority_relpri
913 int
mach_msg_priority_relpri(mach_msg_priority_t pri)914 mach_msg_priority_relpri(mach_msg_priority_t pri)
915 {
916 return mach_msg_priority_relpri_inline(pri);
917 }
918