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