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