xref: /xnu-8020.140.41/libsyscall/mach/mach_msg.c (revision 27b03b360a988dfd3dfdf34262bb0042026747cc)
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