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