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
63 extern int proc_importance_assertion_begin_with_msg(mach_msg_header_t * msg, mach_msg_trailer_t * trailer, uint64_t * assertion_handlep);
64 extern int proc_importance_assertion_complete(uint64_t assertion_handle);
65
66 #define MACH_MSG_TRAP(msg, opt, ssize, rsize, rname, to, not) \
67 mach_msg_trap((msg), (opt), (ssize), (rsize), (rname), (to), (not))
68
69 #define LIBMACH_OPTIONS (MACH_SEND_INTERRUPT|MACH_RCV_INTERRUPT)
70
71 static inline mach_msg_options_t
mach_msg_options_after_interruption(mach_msg_options_t options)72 mach_msg_options_after_interruption(mach_msg_options_t options)
73 {
74 if ((options & MACH_SEND_MSG) && (options & MACH_RCV_MSG)) {
75 /*
76 * If MACH_RCV_SYNC_WAIT was passed for a combined send-receive it must
77 * be cleared for receive-only retries, as the kernel has no way to
78 * discover the destination.
79 */
80 options &= ~MACH_RCV_SYNC_WAIT;
81 }
82 options &= ~(LIBMACH_OPTIONS | MACH_SEND_MSG);
83 return options;
84 }
85
86
87 /*
88 * Routine: mach_msg
89 * Purpose:
90 * Send and/or receive a message. If the message operation
91 * is interrupted, and the user did not request an indication
92 * of that fact, then restart the appropriate parts of the
93 * operation.
94 */
95 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)96 mach_msg(
97 mach_msg_header_t *msg,
98 mach_msg_option_t option,
99 mach_msg_size_t send_size,
100 mach_msg_size_t rcv_size,
101 mach_port_t rcv_name,
102 mach_msg_timeout_t timeout,
103 mach_port_t notify)
104 {
105 mach_msg_return_t mr;
106
107 /*
108 * Consider the following cases:
109 * 1) Errors in pseudo-receive (eg, MACH_SEND_INTERRUPTED
110 * plus special bits).
111 * 2) Use of MACH_SEND_INTERRUPT/MACH_RCV_INTERRUPT options.
112 * 3) RPC calls with interruptions in one/both halves.
113 *
114 * We refrain from passing the option bits that we implement
115 * to the kernel. This prevents their presence from inhibiting
116 * the kernel's fast paths (when it checks the option value).
117 */
118
119 mr = MACH_MSG_TRAP(msg, option & ~LIBMACH_OPTIONS,
120 send_size, rcv_size, rcv_name,
121 timeout, notify);
122 if (mr == MACH_MSG_SUCCESS) {
123 return MACH_MSG_SUCCESS;
124 }
125
126 if ((option & MACH_SEND_INTERRUPT) == 0) {
127 while (mr == MACH_SEND_INTERRUPTED) {
128 mr = MACH_MSG_TRAP(msg,
129 option & ~LIBMACH_OPTIONS,
130 send_size, rcv_size, rcv_name,
131 timeout, notify);
132 }
133 }
134
135 if ((option & MACH_RCV_INTERRUPT) == 0) {
136 while (mr == MACH_RCV_INTERRUPTED) {
137 mr = MACH_MSG_TRAP(msg,
138 mach_msg_options_after_interruption(option),
139 0, rcv_size, rcv_name,
140 timeout, notify);
141 }
142 }
143
144 return mr;
145 }
146
147 /*
148 * Routine: mach_msg_overwrite
149 * Purpose:
150 * Send and/or receive a message. If the message operation
151 * is interrupted, and the user did not request an indication
152 * of that fact, then restart the appropriate parts of the
153 * operation.
154 *
155 * Distinct send and receive buffers may be specified. If
156 * no separate receive buffer is specified, the msg parameter
157 * will be used for both send and receive operations.
158 *
159 * In addition to a distinct receive buffer, that buffer may
160 * already contain scatter control information to direct the
161 * receiving of the message.
162 */
163 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,mach_msg_size_t rcv_scatter_size)164 mach_msg_overwrite(
165 mach_msg_header_t *msg,
166 mach_msg_option_t option,
167 mach_msg_size_t send_size,
168 mach_msg_size_t rcv_limit,
169 mach_port_t rcv_name,
170 mach_msg_timeout_t timeout,
171 mach_port_t notify,
172 mach_msg_header_t *rcv_msg,
173 mach_msg_size_t rcv_scatter_size)
174 {
175 mach_msg_return_t mr;
176
177 /*
178 * Consider the following cases:
179 * 1) Errors in pseudo-receive (eg, MACH_SEND_INTERRUPTED
180 * plus special bits).
181 * 2) Use of MACH_SEND_INTERRUPT/MACH_RCV_INTERRUPT options.
182 * 3) RPC calls with interruptions in one/both halves.
183 *
184 * We refrain from passing the option bits that we implement
185 * to the kernel. This prevents their presence from inhibiting
186 * the kernel's fast paths (when it checks the option value).
187 */
188
189 mr = mach_msg_overwrite_trap(msg, option & ~LIBMACH_OPTIONS,
190 send_size, rcv_limit, rcv_name,
191 timeout, notify, rcv_msg, rcv_scatter_size);
192 if (mr == MACH_MSG_SUCCESS) {
193 return MACH_MSG_SUCCESS;
194 }
195
196 if ((option & MACH_SEND_INTERRUPT) == 0) {
197 while (mr == MACH_SEND_INTERRUPTED) {
198 mr = mach_msg_overwrite_trap(msg,
199 option & ~LIBMACH_OPTIONS,
200 send_size, rcv_limit, rcv_name,
201 timeout, notify, rcv_msg, rcv_scatter_size);
202 }
203 }
204
205 if ((option & MACH_RCV_INTERRUPT) == 0) {
206 while (mr == MACH_RCV_INTERRUPTED) {
207 mr = mach_msg_overwrite_trap(msg,
208 mach_msg_options_after_interruption(option),
209 0, rcv_limit, rcv_name,
210 timeout, notify, rcv_msg, rcv_scatter_size);
211 }
212 }
213
214 return mr;
215 }
216
217
218 mach_msg_return_t
mach_msg_send(mach_msg_header_t * msg)219 mach_msg_send(mach_msg_header_t *msg)
220 {
221 return mach_msg(msg, MACH_SEND_MSG,
222 msg->msgh_size, 0, MACH_PORT_NULL,
223 MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
224 }
225
226 mach_msg_return_t
mach_msg_receive(mach_msg_header_t * msg)227 mach_msg_receive(mach_msg_header_t *msg)
228 {
229 return mach_msg(msg, MACH_RCV_MSG,
230 0, msg->msgh_size, msg->msgh_local_port,
231 MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
232 }
233
234
235 static void
mach_msg_destroy_port(mach_port_t port,mach_msg_type_name_t type)236 mach_msg_destroy_port(mach_port_t port, mach_msg_type_name_t type)
237 {
238 if (MACH_PORT_VALID(port)) {
239 switch (type) {
240 case MACH_MSG_TYPE_MOVE_SEND:
241 case MACH_MSG_TYPE_MOVE_SEND_ONCE:
242 /* destroy the send/send-once right */
243 (void) mach_port_deallocate(mach_task_self_, port);
244 break;
245
246 case MACH_MSG_TYPE_MOVE_RECEIVE:
247 /* destroy the receive right */
248 (void) mach_port_mod_refs(mach_task_self_, port,
249 MACH_PORT_RIGHT_RECEIVE, -1);
250 break;
251
252 case MACH_MSG_TYPE_MAKE_SEND:
253 /* create a send right and then destroy it */
254 (void) mach_port_insert_right(mach_task_self_, port,
255 port, MACH_MSG_TYPE_MAKE_SEND);
256 (void) mach_port_deallocate(mach_task_self_, port);
257 break;
258
259 case MACH_MSG_TYPE_MAKE_SEND_ONCE:
260 /* create a send-once right and then destroy it */
261 (void) mach_port_extract_right(mach_task_self_, port,
262 MACH_MSG_TYPE_MAKE_SEND_ONCE,
263 &port, &type);
264 (void) mach_port_deallocate(mach_task_self_, port);
265 break;
266 }
267 }
268 }
269
270 static void
mach_msg_destroy_memory(vm_offset_t addr,vm_size_t size)271 mach_msg_destroy_memory(vm_offset_t addr, vm_size_t size)
272 {
273 if (size != 0) {
274 (void) vm_deallocate(mach_task_self_, addr, size);
275 }
276 }
277
278
279 /*
280 * Routine: mach_msg_destroy
281 * Purpose:
282 * mach_msg_destroy is useful in two contexts.
283 *
284 * First, it can deallocate all port rights and
285 * out-of-line memory in a received message.
286 * When a server receives a request it doesn't want,
287 * it needs this functionality.
288 *
289 * Second, it can mimic the side-effects of a msg-send
290 * operation. The effect is as if the message were sent
291 * and then destroyed inside the kernel. When a server
292 * can't send a reply (because the client died),
293 * it needs this functionality.
294 */
295 void
mach_msg_destroy(mach_msg_header_t * msg)296 mach_msg_destroy(mach_msg_header_t *msg)
297 {
298 mach_msg_bits_t mbits = msg->msgh_bits;
299
300 /*
301 * The msgh_local_port field doesn't hold a port right.
302 * The receive operation consumes the destination port right.
303 */
304
305 mach_msg_destroy_port(msg->msgh_remote_port, MACH_MSGH_BITS_REMOTE(mbits));
306 mach_msg_destroy_port(msg->msgh_voucher_port, MACH_MSGH_BITS_VOUCHER(mbits));
307
308 if (mbits & MACH_MSGH_BITS_COMPLEX) {
309 mach_msg_base_t *base;
310 mach_msg_type_number_t count, i;
311 mach_msg_descriptor_t *daddr;
312
313 base = (mach_msg_base_t *) msg;
314 count = base->body.msgh_descriptor_count;
315
316 daddr = (mach_msg_descriptor_t *) (base + 1);
317 for (i = 0; i < count; i++) {
318 switch (daddr->type.type) {
319 case MACH_MSG_PORT_DESCRIPTOR: {
320 mach_msg_port_descriptor_t *dsc;
321
322 /*
323 * Destroy port rights carried in the message
324 */
325 dsc = &daddr->port;
326 mach_msg_destroy_port(dsc->name, dsc->disposition);
327 daddr = (mach_msg_descriptor_t *)(dsc + 1);
328 break;
329 }
330
331 case MACH_MSG_OOL_DESCRIPTOR: {
332 mach_msg_ool_descriptor_t *dsc;
333
334 /*
335 * Destroy memory carried in the message
336 */
337 dsc = &daddr->out_of_line;
338 if (dsc->deallocate) {
339 mach_msg_destroy_memory((vm_offset_t)dsc->address,
340 dsc->size);
341 }
342 daddr = (mach_msg_descriptor_t *)(dsc + 1);
343 break;
344 }
345
346 case MACH_MSG_OOL_VOLATILE_DESCRIPTOR: {
347 mach_msg_ool_descriptor_t *dsc;
348
349 /*
350 * Just skip it.
351 */
352 dsc = &daddr->out_of_line;
353 daddr = (mach_msg_descriptor_t *)(dsc + 1);
354 break;
355 }
356
357 case MACH_MSG_OOL_PORTS_DESCRIPTOR: {
358 mach_port_t *ports;
359 mach_msg_ool_ports_descriptor_t *dsc;
360 mach_msg_type_number_t j;
361
362 /*
363 * Destroy port rights carried in the message
364 */
365 dsc = &daddr->ool_ports;
366 ports = (mach_port_t *) dsc->address;
367 for (j = 0; j < dsc->count; j++, ports++) {
368 mach_msg_destroy_port(*ports, dsc->disposition);
369 }
370
371 /*
372 * Destroy memory carried in the message
373 */
374 if (dsc->deallocate) {
375 mach_msg_destroy_memory((vm_offset_t)dsc->address,
376 dsc->count * sizeof(mach_port_t));
377 }
378 daddr = (mach_msg_descriptor_t *)(dsc + 1);
379 break;
380 }
381
382 case MACH_MSG_GUARDED_PORT_DESCRIPTOR: {
383 mach_msg_guarded_port_descriptor_t *dsc;
384 mach_msg_guard_flags_t flags;
385 /*
386 * Destroy port right carried in the message
387 */
388 dsc = &daddr->guarded_port;
389 flags = dsc->flags;
390 if ((flags & MACH_MSG_GUARD_FLAGS_UNGUARDED_ON_SEND) == 0) {
391 /* Need to unguard before destroying the port */
392 mach_port_unguard(mach_task_self_, dsc->name, (uint64_t)dsc->context);
393 }
394 mach_msg_destroy_port(dsc->name, dsc->disposition);
395 daddr = (mach_msg_descriptor_t *)(dsc + 1);
396 break;
397 }
398 }
399 }
400 }
401 }
402
403 static inline boolean_t
mach_msg_server_is_recoverable_send_error(kern_return_t kr)404 mach_msg_server_is_recoverable_send_error(kern_return_t kr)
405 {
406 switch (kr) {
407 case MACH_SEND_INVALID_DEST:
408 case MACH_SEND_TIMED_OUT:
409 case MACH_SEND_INTERRUPTED:
410 return TRUE;
411 default:
412 /*
413 * Other errors mean that the message may have been partially destroyed
414 * by the kernel, and these can't be recovered and may leak resources.
415 */
416 return FALSE;
417 }
418 }
419
420 static kern_return_t
mach_msg_server_mig_return_code(mig_reply_error_t * reply)421 mach_msg_server_mig_return_code(mig_reply_error_t *reply)
422 {
423 /*
424 * If the message is complex, it is assumed that the reply was successful,
425 * as the RetCode is where the count of out of line descriptors is.
426 *
427 * If not, we read RetCode.
428 */
429 if (reply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) {
430 return KERN_SUCCESS;
431 }
432 return reply->RetCode;
433 }
434
435 static void
mach_msg_server_consume_unsent_message(mach_msg_header_t * hdr)436 mach_msg_server_consume_unsent_message(mach_msg_header_t *hdr)
437 {
438 /* mach_msg_destroy doesn't handle the local port */
439 mach_port_t port = hdr->msgh_local_port;
440 if (MACH_PORT_VALID(port)) {
441 switch (MACH_MSGH_BITS_LOCAL(hdr->msgh_bits)) {
442 case MACH_MSG_TYPE_MOVE_SEND:
443 case MACH_MSG_TYPE_MOVE_SEND_ONCE:
444 /* destroy the send/send-once right */
445 (void) mach_port_deallocate(mach_task_self_, port);
446 hdr->msgh_local_port = MACH_PORT_NULL;
447 break;
448 }
449 }
450 mach_msg_destroy(hdr);
451 }
452
453 /*
454 * Routine: mach_msg_server_once
455 * Purpose:
456 * A simple generic server function. It allows more flexibility
457 * than mach_msg_server by processing only one message request
458 * and then returning to the user. Note that more in the way
459 * of error codes are returned to the user; specifically, any
460 * failing error from mach_msg calls will be returned
461 * (though errors from the demux routine or the routine it
462 * calls will not be).
463 */
464 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)465 mach_msg_server_once(
466 boolean_t (*demux)(mach_msg_header_t *, mach_msg_header_t *),
467 mach_msg_size_t max_size,
468 mach_port_t rcv_name,
469 mach_msg_options_t options)
470 {
471 mig_reply_error_t *bufRequest, *bufReply;
472 mach_msg_size_t request_size;
473 mach_msg_size_t request_alloc;
474 mach_msg_size_t trailer_alloc;
475 mach_msg_size_t reply_alloc;
476 mach_msg_return_t mr;
477 kern_return_t kr;
478 mach_port_t self = mach_task_self_;
479 voucher_mach_msg_state_t old_state = VOUCHER_MACH_MSG_STATE_UNCHANGED;
480
481 options &= ~(MACH_SEND_MSG | MACH_RCV_MSG | MACH_RCV_VOUCHER);
482
483 trailer_alloc = REQUESTED_TRAILER_SIZE(options);
484 request_alloc = (mach_msg_size_t)round_page(max_size + trailer_alloc);
485
486 request_size = (options & MACH_RCV_LARGE) ?
487 request_alloc : max_size + trailer_alloc;
488
489 reply_alloc = (mach_msg_size_t)round_page((options & MACH_SEND_TRAILER) ?
490 (max_size + MAX_TRAILER_SIZE) :
491 max_size);
492
493 kr = vm_allocate(self,
494 (vm_address_t *)&bufReply,
495 reply_alloc,
496 VM_MAKE_TAG(VM_MEMORY_MACH_MSG) | TRUE);
497 if (kr != KERN_SUCCESS) {
498 return kr;
499 }
500
501 for (;;) {
502 mach_msg_size_t new_request_alloc;
503
504 kr = vm_allocate(self,
505 (vm_address_t *)&bufRequest,
506 request_alloc,
507 VM_MAKE_TAG(VM_MEMORY_MACH_MSG) | TRUE);
508 if (kr != KERN_SUCCESS) {
509 vm_deallocate(self,
510 (vm_address_t)bufReply,
511 reply_alloc);
512 return kr;
513 }
514
515 mr = mach_msg(&bufRequest->Head, MACH_RCV_MSG | MACH_RCV_VOUCHER | options,
516 0, request_size, rcv_name,
517 MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
518
519 if (!((mr == MACH_RCV_TOO_LARGE) && (options & MACH_RCV_LARGE))) {
520 break;
521 }
522
523 new_request_alloc = (mach_msg_size_t)round_page(bufRequest->Head.msgh_size +
524 trailer_alloc);
525 vm_deallocate(self,
526 (vm_address_t) bufRequest,
527 request_alloc);
528 request_size = request_alloc = new_request_alloc;
529 }
530
531 if (mr == MACH_MSG_SUCCESS) {
532 /* we have a request message */
533
534 old_state = voucher_mach_msg_adopt(&bufRequest->Head);
535
536 (void) (*demux)(&bufRequest->Head, &bufReply->Head);
537
538 switch (mach_msg_server_mig_return_code(bufReply)) {
539 case KERN_SUCCESS:
540 break;
541 case MIG_NO_REPLY:
542 bufReply->Head.msgh_remote_port = MACH_PORT_NULL;
543 break;
544 default:
545 /*
546 * destroy the request - but not the reply port
547 * (MIG moved it into the bufReply).
548 */
549 bufRequest->Head.msgh_remote_port = MACH_PORT_NULL;
550 mach_msg_destroy(&bufRequest->Head);
551 }
552
553 /*
554 * We don't want to block indefinitely because the client
555 * isn't receiving messages from the reply port.
556 * If we have a send-once right for the reply port, then
557 * this isn't a concern because the send won't block.
558 * If we have a send right, we need to use MACH_SEND_TIMEOUT.
559 * To avoid falling off the kernel's fast RPC path unnecessarily,
560 * we only supply MACH_SEND_TIMEOUT when absolutely necessary.
561 */
562 if (bufReply->Head.msgh_remote_port != MACH_PORT_NULL) {
563 mr = mach_msg(&bufReply->Head,
564 (MACH_MSGH_BITS_REMOTE(bufReply->Head.msgh_bits) ==
565 MACH_MSG_TYPE_MOVE_SEND_ONCE) ?
566 MACH_SEND_MSG | options :
567 MACH_SEND_MSG | MACH_SEND_TIMEOUT | options,
568 bufReply->Head.msgh_size, 0, MACH_PORT_NULL,
569 MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
570
571 if (mach_msg_server_is_recoverable_send_error(mr)) {
572 mach_msg_server_consume_unsent_message(&bufReply->Head);
573 mr = MACH_MSG_SUCCESS;
574 }
575 }
576 }
577
578 voucher_mach_msg_revert(old_state);
579
580 (void)vm_deallocate(self,
581 (vm_address_t) bufRequest,
582 request_alloc);
583 (void)vm_deallocate(self,
584 (vm_address_t) bufReply,
585 reply_alloc);
586 return mr;
587 }
588
589 /*
590 * Routine: mach_msg_server
591 * Purpose:
592 * A simple generic server function. Note that changes here
593 * should be considered for duplication above.
594 */
595 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)596 mach_msg_server(
597 boolean_t (*demux)(mach_msg_header_t *, mach_msg_header_t *),
598 mach_msg_size_t max_size,
599 mach_port_t rcv_name,
600 mach_msg_options_t options)
601 {
602 mig_reply_error_t *bufRequest, *bufReply;
603 mach_msg_size_t request_size;
604 mach_msg_size_t new_request_alloc;
605 mach_msg_size_t request_alloc;
606 mach_msg_size_t trailer_alloc;
607 mach_msg_size_t reply_alloc;
608 mach_msg_return_t mr;
609 kern_return_t kr;
610 mach_port_t self = mach_task_self_;
611 voucher_mach_msg_state_t old_state = VOUCHER_MACH_MSG_STATE_UNCHANGED;
612 boolean_t buffers_swapped = FALSE;
613
614 options &= ~(MACH_SEND_MSG | MACH_RCV_MSG | MACH_RCV_VOUCHER);
615
616 reply_alloc = (mach_msg_size_t)round_page((options & MACH_SEND_TRAILER) ?
617 (max_size + MAX_TRAILER_SIZE) : max_size);
618
619 kr = vm_allocate(self,
620 (vm_address_t *)&bufReply,
621 reply_alloc,
622 VM_MAKE_TAG(VM_MEMORY_MACH_MSG) | TRUE);
623 if (kr != KERN_SUCCESS) {
624 return kr;
625 }
626
627 request_alloc = 0;
628 trailer_alloc = REQUESTED_TRAILER_SIZE(options);
629 new_request_alloc = (mach_msg_size_t)round_page(max_size + trailer_alloc);
630
631 request_size = (options & MACH_RCV_LARGE) ?
632 new_request_alloc : max_size + trailer_alloc;
633
634 for (;;) {
635 if (request_alloc < new_request_alloc) {
636 request_alloc = new_request_alloc;
637 kr = vm_allocate(self,
638 (vm_address_t *)&bufRequest,
639 request_alloc,
640 VM_MAKE_TAG(VM_MEMORY_MACH_MSG) | TRUE);
641 if (kr != KERN_SUCCESS) {
642 vm_deallocate(self,
643 (vm_address_t)bufReply,
644 reply_alloc);
645 return kr;
646 }
647 }
648
649 mr = mach_msg(&bufRequest->Head, MACH_RCV_MSG | MACH_RCV_VOUCHER | options,
650 0, request_size, rcv_name,
651 MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
652
653 while (mr == MACH_MSG_SUCCESS) {
654 /* we have another request message */
655
656 buffers_swapped = FALSE;
657 old_state = voucher_mach_msg_adopt(&bufRequest->Head);
658 bufReply->Head = (mach_msg_header_t){};
659
660 (void) (*demux)(&bufRequest->Head, &bufReply->Head);
661
662 switch (mach_msg_server_mig_return_code(bufReply)) {
663 case KERN_SUCCESS:
664 break;
665 case MIG_NO_REPLY:
666 bufReply->Head.msgh_remote_port = MACH_PORT_NULL;
667 break;
668 default:
669 /*
670 * destroy the request - but not the reply port
671 * (MIG moved it into the bufReply).
672 */
673 bufRequest->Head.msgh_remote_port = MACH_PORT_NULL;
674 mach_msg_destroy(&bufRequest->Head);
675 }
676
677 /*
678 * We don't want to block indefinitely because the client
679 * isn't receiving messages from the reply port.
680 * If we have a send-once right for the reply port, then
681 * this isn't a concern because the send won't block.
682 * If we have a send right, we need to use MACH_SEND_TIMEOUT.
683 * To avoid falling off the kernel's fast RPC path,
684 * we only supply MACH_SEND_TIMEOUT when absolutely necessary.
685 */
686 if (bufReply->Head.msgh_remote_port != MACH_PORT_NULL) {
687 if (request_alloc == reply_alloc) {
688 mig_reply_error_t *bufTemp;
689
690 mr = mach_msg(
691 &bufReply->Head,
692 (MACH_MSGH_BITS_REMOTE(bufReply->Head.msgh_bits) ==
693 MACH_MSG_TYPE_MOVE_SEND_ONCE) ?
694 MACH_SEND_MSG | MACH_RCV_MSG | MACH_RCV_TIMEOUT | MACH_RCV_VOUCHER | options :
695 MACH_SEND_MSG | MACH_RCV_MSG | MACH_SEND_TIMEOUT | MACH_RCV_TIMEOUT | MACH_RCV_VOUCHER | options,
696 bufReply->Head.msgh_size, request_size, rcv_name,
697 MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
698
699 /* swap request and reply */
700 bufTemp = bufRequest;
701 bufRequest = bufReply;
702 bufReply = bufTemp;
703 buffers_swapped = TRUE;
704 } else {
705 mr = mach_msg_overwrite(
706 &bufReply->Head,
707 (MACH_MSGH_BITS_REMOTE(bufReply->Head.msgh_bits) ==
708 MACH_MSG_TYPE_MOVE_SEND_ONCE) ?
709 MACH_SEND_MSG | MACH_RCV_MSG | MACH_RCV_TIMEOUT | MACH_RCV_VOUCHER | options :
710 MACH_SEND_MSG | MACH_RCV_MSG | MACH_SEND_TIMEOUT | MACH_RCV_TIMEOUT | MACH_RCV_VOUCHER | options,
711 bufReply->Head.msgh_size, request_size, rcv_name,
712 MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL,
713 &bufRequest->Head, 0);
714 }
715
716 /*
717 * Need to destroy the reply msg in case if there was a send timeout or
718 * invalid destination. The reply msg would be swapped with request msg
719 * if buffers_swapped is true, thus destroy request msg instead of
720 * reply msg in such cases.
721 */
722 if (mach_msg_server_is_recoverable_send_error(mr)) {
723 if (buffers_swapped) {
724 mach_msg_server_consume_unsent_message(&bufRequest->Head);
725 } else {
726 mach_msg_server_consume_unsent_message(&bufReply->Head);
727 }
728 } else if (mr != MACH_RCV_TIMED_OUT) {
729 voucher_mach_msg_revert(old_state);
730 old_state = VOUCHER_MACH_MSG_STATE_UNCHANGED;
731
732 continue;
733 }
734 }
735 voucher_mach_msg_revert(old_state);
736 old_state = VOUCHER_MACH_MSG_STATE_UNCHANGED;
737
738 mr = mach_msg(&bufRequest->Head, MACH_RCV_MSG | MACH_RCV_VOUCHER | options,
739 0, request_size, rcv_name,
740 MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
741 } /* while (mr == MACH_MSG_SUCCESS) */
742
743 if ((mr == MACH_RCV_TOO_LARGE) && (options & MACH_RCV_LARGE)) {
744 new_request_alloc = (mach_msg_size_t)round_page(bufRequest->Head.msgh_size +
745 trailer_alloc);
746 request_size = new_request_alloc;
747 vm_deallocate(self,
748 (vm_address_t) bufRequest,
749 request_alloc);
750 continue;
751 }
752
753 break;
754 } /* for(;;) */
755
756 (void)vm_deallocate(self,
757 (vm_address_t) bufRequest,
758 request_alloc);
759 (void)vm_deallocate(self,
760 (vm_address_t) bufReply,
761 reply_alloc);
762 return mr;
763 }
764
765 /*
766 * Routine: mach_msg_server_importance
767 * Purpose:
768 * A simple generic server function which handles importance
769 * promotion assertions for adaptive daemons.
770 */
771 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)772 mach_msg_server_importance(
773 boolean_t (*demux)(mach_msg_header_t *, mach_msg_header_t *),
774 mach_msg_size_t max_size,
775 mach_port_t rcv_name,
776 mach_msg_options_t options)
777 {
778 return mach_msg_server(demux, max_size, rcv_name, options);
779 }
780
781 kern_return_t
mach_voucher_deallocate(mach_voucher_t voucher)782 mach_voucher_deallocate(
783 mach_voucher_t voucher)
784 {
785 return mach_port_deallocate(mach_task_self(), voucher);
786 }
787
788 #undef mach_msg_priority_is_pthread_priority
789 int
mach_msg_priority_is_pthread_priority(mach_msg_priority_t pri)790 mach_msg_priority_is_pthread_priority(mach_msg_priority_t pri)
791 {
792 return mach_msg_priority_is_pthread_priority_inline(pri);
793 }
794
795 #undef mach_msg_priority_encode
796 mach_msg_priority_t
mach_msg_priority_encode(mach_msg_qos_t override_qos,mach_msg_qos_t qos,int relpri)797 mach_msg_priority_encode(mach_msg_qos_t override_qos, mach_msg_qos_t qos, int relpri)
798 {
799 return mach_msg_priority_encode_inline(override_qos, qos, relpri);
800 }
801
802 #undef mach_msg_priority_overide_qos
803 mach_msg_qos_t
mach_msg_priority_overide_qos(mach_msg_priority_t pri)804 mach_msg_priority_overide_qos(mach_msg_priority_t pri)
805 {
806 return mach_msg_priority_overide_qos_inline(pri);
807 }
808
809 #undef mach_msg_priority_qos
810 mach_msg_qos_t
mach_msg_priority_qos(mach_msg_priority_t pri)811 mach_msg_priority_qos(mach_msg_priority_t pri)
812 {
813 return mach_msg_priority_qos_inline(pri);
814 }
815
816 #undef mach_msg_priority_relpri
817 int
mach_msg_priority_relpri(mach_msg_priority_t pri)818 mach_msg_priority_relpri(mach_msg_priority_t pri)
819 {
820 return mach_msg_priority_relpri_inline(pri);
821 }
822