xref: /xnu-10002.1.13/osfmk/kern/ipc_host.c (revision 1031c584a5e37aff177559b9f69dbd3c8c3fd30a)
1 /*
2  * Copyright (c) 2000-2009 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  * @OSF_COPYRIGHT@
30  */
31 /*
32  * Mach Operating System
33  * Copyright (c) 1991,1990,1989,1988 Carnegie Mellon University
34  * All Rights Reserved.
35  *
36  * Permission to use, copy, modify and distribute this software and its
37  * documentation is hereby granted, provided that both the copyright
38  * notice and this permission notice appear in all copies of the
39  * software, derivative works or modified versions, and any portions
40  * thereof, and that both notices appear in supporting documentation.
41  *
42  * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
43  * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
44  * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
45  *
46  * Carnegie Mellon requests users of this software to return to
47  *
48  *  Software Distribution Coordinator  or  [email protected]
49  *  School of Computer Science
50  *  Carnegie Mellon University
51  *  Pittsburgh PA 15213-3890
52  *
53  * any improvements or extensions that they make and grant Carnegie Mellon
54  * the rights to redistribute these changes.
55  */
56 /*
57  */
58 
59 /*
60  *	kern/ipc_host.c
61  *
62  *	Routines to implement host ports.
63  */
64 #include <mach/message.h>
65 #include <mach/mach_traps.h>
66 #include <mach/mach_host_server.h>
67 #include <mach/host_priv_server.h>
68 #include <kern/host.h>
69 #include <kern/processor.h>
70 #include <kern/task.h>
71 #include <kern/thread.h>
72 #include <kern/ipc_host.h>
73 #include <kern/ipc_kobject.h>
74 #include <kern/ux_handler.h>
75 #include <kern/misc_protos.h>
76 #include <kern/spl.h>
77 #include <ipc/ipc_port.h>
78 #include <ipc/ipc_space.h>
79 
80 #if CONFIG_CSR
81 #include <sys/csr.h>
82 #endif
83 
84 #if CONFIG_MACF
85 #include <security/mac_mach_internal.h>
86 #endif
87 
88 /*
89  *	ipc_host_init: set up various things.
90  */
91 
92 extern lck_grp_t                host_notify_lock_grp;
93 
94 IPC_KOBJECT_DEFINE(IKOT_HOST,
95     .iko_op_stable    = true,
96     .iko_op_permanent = true);
97 IPC_KOBJECT_DEFINE(IKOT_HOST_PRIV,
98     .iko_op_stable    = true,
99     .iko_op_permanent = true);
100 
101 IPC_KOBJECT_DEFINE(IKOT_PROCESSOR,
102     .iko_op_stable    = true,
103     .iko_op_permanent = true);
104 IPC_KOBJECT_DEFINE(IKOT_PSET,
105     .iko_op_stable    = true,
106     .iko_op_permanent = true);
107 IPC_KOBJECT_DEFINE(IKOT_PSET_NAME,
108     .iko_op_stable    = true,
109     .iko_op_permanent = true);
110 
111 void
ipc_host_init(void)112 ipc_host_init(void)
113 {
114 	ipc_port_t      port;
115 	int i;
116 
117 	lck_mtx_init(&realhost.lock, &host_notify_lock_grp, LCK_ATTR_NULL);
118 
119 	/*
120 	 *	Allocate and set up the two host ports.
121 	 */
122 	port = ipc_kobject_alloc_port((ipc_kobject_t) &realhost, IKOT_HOST,
123 	    IPC_KOBJECT_ALLOC_MAKE_SEND);
124 	kernel_set_special_port(&realhost, HOST_PORT, port);
125 
126 	port = ipc_kobject_alloc_port((ipc_kobject_t) &realhost, IKOT_HOST_PRIV,
127 	    IPC_KOBJECT_ALLOC_MAKE_SEND);
128 	kernel_set_special_port(&realhost, HOST_PRIV_PORT, port);
129 
130 	/* the rest of the special ports will be set up later */
131 
132 	bzero(&realhost.exc_actions[0], sizeof(realhost.exc_actions[0]));
133 	for (i = FIRST_EXCEPTION; i < EXC_TYPES_COUNT; i++) {
134 		realhost.exc_actions[i].port = IP_NULL;
135 		/* The mac framework is not yet initialized, so we defer
136 		 * initializing the labels to later, when they are set
137 		 * for the first time. */
138 		realhost.exc_actions[i].label = NULL;
139 		/* initialize the entire exception action struct */
140 		realhost.exc_actions[i].behavior = 0;
141 		realhost.exc_actions[i].flavor = 0;
142 		realhost.exc_actions[i].privileged = FALSE;
143 	} /* for */
144 
145 	/*
146 	 *	Set up ipc for default processor set.
147 	 */
148 	ipc_pset_init(&pset0);
149 
150 	/*
151 	 *	And for master processor
152 	 */
153 	ipc_processor_init(master_processor);
154 }
155 
156 /*
157  *	Routine:	host_self_trap [mach trap]
158  *	Purpose:
159  *		Give the caller send rights for his own host port.
160  *	Conditions:
161  *		Nothing locked.
162  *	Returns:
163  *		MACH_PORT_NULL if there are any resource failures
164  *		or other errors.
165  */
166 
167 mach_port_name_t
host_self_trap(__unused struct host_self_trap_args * args)168 host_self_trap(
169 	__unused struct host_self_trap_args *args)
170 {
171 	task_t self = current_task();
172 	ipc_port_t sright;
173 	mach_port_name_t name;
174 
175 	itk_lock(self);
176 	sright = host_port_copy_send(self->itk_host);
177 	itk_unlock(self);
178 	name = ipc_port_copyout_send(sright, current_space());
179 	return name;
180 }
181 
182 /*
183  *	ipc_processor_init:
184  *
185  *	Initialize ipc access to processor by allocating port.
186  */
187 
188 void
ipc_processor_init(processor_t processor)189 ipc_processor_init(
190 	processor_t     processor)
191 {
192 	processor->processor_self = ipc_kobject_alloc_port(processor,
193 	    IKOT_PROCESSOR, IPC_KOBJECT_ALLOC_NONE);
194 }
195 
196 /*
197  *	ipc_pset_init:
198  *
199  *	Initialize ipc control of a processor set by allocating its ports.
200  */
201 
202 void
ipc_pset_init(processor_set_t pset)203 ipc_pset_init(
204 	processor_set_t         pset)
205 {
206 	pset->pset_self = ipc_kobject_alloc_port(pset,
207 	    IKOT_PSET, IPC_KOBJECT_ALLOC_NONE);
208 	pset->pset_name_self = ipc_kobject_alloc_port(pset,
209 	    IKOT_PSET_NAME, IPC_KOBJECT_ALLOC_NONE);
210 }
211 
212 /*
213  *	processor_set_default:
214  *
215  *	Return ports for manipulating default_processor set.
216  */
217 kern_return_t
processor_set_default(host_t host,processor_set_t * pset)218 processor_set_default(
219 	host_t                  host,
220 	processor_set_t         *pset)
221 {
222 	if (host == HOST_NULL) {
223 		return KERN_INVALID_ARGUMENT;
224 	}
225 
226 	*pset = &pset0;
227 
228 	return KERN_SUCCESS;
229 }
230 
231 /*
232  *	Routine:	convert_port_to_host
233  *	Purpose:
234  *		Convert from a port to a host.
235  *		Doesn't consume the port ref; the host produced may be null.
236  *	Conditions:
237  *		Nothing locked.
238  */
239 
240 host_t
convert_port_to_host(ipc_port_t port)241 convert_port_to_host(
242 	ipc_port_t      port)
243 {
244 	host_t host = HOST_NULL;
245 	ipc_kobject_type_t type;
246 
247 	if (IP_VALID(port)) {
248 		type = ip_kotype(port);
249 		if (type == IKOT_HOST || type == IKOT_HOST_PRIV) {
250 			host = (host_t)ipc_kobject_get_stable(port, type);
251 			if (host && host != &realhost) {
252 				panic("unexpected host object: %p", host);
253 			}
254 		}
255 	}
256 	return host;
257 }
258 
259 /*
260  *	Routine:	convert_port_to_host_priv
261  *	Purpose:
262  *		Convert from a port to a host.
263  *		Doesn't consume the port ref; the host produced may be null.
264  *	Conditions:
265  *		Nothing locked.
266  */
267 
268 host_t
convert_port_to_host_priv(ipc_port_t port)269 convert_port_to_host_priv(
270 	ipc_port_t      port)
271 {
272 	host_t host = HOST_NULL;
273 
274 	/* reject translation if itk_host is not host_priv */
275 	if (port != current_task()->itk_host) {
276 		return HOST_NULL;
277 	}
278 
279 	if (IP_VALID(port)) {
280 		host = ipc_kobject_get_stable(port, IKOT_HOST_PRIV);
281 		if (host && host != &realhost) {
282 			panic("unexpected host object: %p", host);
283 		}
284 	}
285 
286 	return host;
287 }
288 
289 /*
290  *	Routine:	convert_port_to_processor
291  *	Purpose:
292  *		Convert from a port to a processor.
293  *		Doesn't consume the port ref;
294  *		the processor produced may be null.
295  *	Conditions:
296  *		Nothing locked.
297  */
298 
299 processor_t
convert_port_to_processor(ipc_port_t port)300 convert_port_to_processor(
301 	ipc_port_t      port)
302 {
303 	processor_t processor = PROCESSOR_NULL;
304 
305 	if (IP_VALID(port)) {
306 		processor = ipc_kobject_get_stable(port, IKOT_PROCESSOR);
307 	}
308 
309 	return processor;
310 }
311 
312 /*
313  *	Routine:	convert_port_to_pset
314  *	Purpose:
315  *		Convert from a port to a pset.
316  *		Doesn't consume the port ref
317  *		which may be null.
318  *	Conditions:
319  *		Nothing locked.
320  */
321 
322 processor_set_t
convert_port_to_pset(ipc_port_t port)323 convert_port_to_pset(
324 	ipc_port_t      port)
325 {
326 	processor_set_t pset = PROCESSOR_SET_NULL;
327 
328 	if (IP_VALID(port)) {
329 		pset = ipc_kobject_get_stable(port, IKOT_PSET);
330 	}
331 
332 	return pset;
333 }
334 
335 /*
336  *	Routine:	convert_port_to_pset_name
337  *	Purpose:
338  *		Convert from a port to a pset.
339  *		Doesn't consume the port ref
340  *		which may be null.
341  *	Conditions:
342  *		Nothing locked.
343  */
344 
345 processor_set_name_t
convert_port_to_pset_name(ipc_port_t port)346 convert_port_to_pset_name(
347 	ipc_port_t      port)
348 {
349 	processor_set_t pset = PROCESSOR_SET_NULL;
350 	ipc_kobject_type_t type;
351 
352 	if (IP_VALID(port)) {
353 		type = ip_kotype(port);
354 		if (type == IKOT_PSET || type == IKOT_PSET_NAME) {
355 			pset = ipc_kobject_get_stable(port, type);
356 		}
357 	}
358 	return pset;
359 }
360 
361 /*
362  *	Routine:	host_port_copy_send
363  *	Purpose:
364  *		Copies a send right for a host port (priv or not)
365  *	Conditions:
366  *		Nothing locked.
367  */
368 
369 ipc_port_t
host_port_copy_send(ipc_port_t port)370 host_port_copy_send(ipc_port_t port)
371 {
372 	if (IP_VALID(port)) {
373 		ipc_kobject_type_t kotype = ip_kotype(port);
374 
375 		if (kotype == IKOT_HOST) {
376 			port = ipc_kobject_copy_send(port,
377 			    host_self(), IKOT_HOST);
378 		} else if (kotype == IKOT_HOST_PRIV) {
379 			port = ipc_kobject_copy_send(port,
380 			    host_priv_self(), IKOT_HOST_PRIV);
381 #if CONFIG_CSR
382 		} else if (kotype == IKOT_NONE &&
383 		    (csr_check(CSR_ALLOW_KERNEL_DEBUGGER) == 0)) {
384 			port = ipc_port_copy_send_mqueue(port);
385 #endif
386 		} else {
387 			panic("port %p is an invalid host port", port);
388 		}
389 	}
390 
391 	return port;
392 }
393 
394 /*
395  *	Routine:	convert_host_to_port
396  *	Purpose:
397  *		Convert from a host to a port.
398  *		Produces a naked send right which may be invalid.
399  *	Conditions:
400  *		Nothing locked.
401  */
402 
403 ipc_port_t
convert_host_to_port(host_t host)404 convert_host_to_port(
405 	host_t          host)
406 {
407 	ipc_port_t port = IP_NULL;
408 	__assert_only kern_return_t kr;
409 
410 	kr = host_get_host_port(host, &port);
411 	assert(kr == KERN_SUCCESS);
412 	return port;
413 }
414 
415 /*
416  *	Routine:	convert_processor_to_port
417  *	Purpose:
418  *		Convert from a processor to a port.
419  *		Produces a naked send right which may be invalid.
420  *		Processors are not reference counted, so nothing to release.
421  *	Conditions:
422  *		Nothing locked.
423  */
424 
425 ipc_port_t
convert_processor_to_port(processor_t processor)426 convert_processor_to_port(
427 	processor_t             processor)
428 {
429 	ipc_port_t port = processor->processor_self;
430 
431 	if (port != IP_NULL) {
432 		port = ipc_kobject_make_send(port, processor, IKOT_PROCESSOR);
433 	}
434 	return port;
435 }
436 
437 /*
438  *	Routine:	convert_pset_to_port
439  *	Purpose:
440  *		Convert from a pset to a port.
441  *		Produces a naked send right which may be invalid.
442  *		Processor sets are not reference counted, so nothing to release.
443  *	Conditions:
444  *		Nothing locked.
445  */
446 
447 ipc_port_t
convert_pset_to_port(processor_set_t pset)448 convert_pset_to_port(
449 	processor_set_t         pset)
450 {
451 	return ipc_kobject_make_send(pset->pset_self, pset, IKOT_PSET);
452 }
453 
454 /*
455  *	Routine:	convert_pset_name_to_port
456  *	Purpose:
457  *		Convert from a pset to a port.
458  *		Produces a naked send right which may be invalid.
459  *		Processor sets are not reference counted, so nothing to release.
460  *	Conditions:
461  *		Nothing locked.
462  */
463 
464 ipc_port_t
convert_pset_name_to_port(processor_set_name_t pset)465 convert_pset_name_to_port(
466 	processor_set_name_t            pset)
467 {
468 	return ipc_kobject_make_send(pset->pset_name_self, pset, IKOT_PSET_NAME);
469 }
470 
471 /*
472  *	Routine:	host_set_exception_ports [kernel call]
473  *	Purpose:
474  *			Sets the host exception port, flavor and
475  *			behavior for the exception types specified by the mask.
476  *			There will be one send right per exception per valid
477  *			port.
478  *	Conditions:
479  *		Nothing locked.  If successful, consumes
480  *		the supplied send right.
481  *	Returns:
482  *		KERN_SUCCESS		Changed the special port.
483  *		KERN_INVALID_ARGUMENT	The host_priv is not valid,
484  *					Illegal mask bit set.
485  *					Illegal exception behavior
486  *		KERN_NO_ACCESS		Restricted access to set port
487  */
488 kern_return_t
host_set_exception_ports(host_priv_t host_priv,exception_mask_t exception_mask,ipc_port_t new_port,exception_behavior_t new_behavior,thread_state_flavor_t new_flavor)489 host_set_exception_ports(
490 	host_priv_t                     host_priv,
491 	exception_mask_t                exception_mask,
492 	ipc_port_t                      new_port,
493 	exception_behavior_t            new_behavior,
494 	thread_state_flavor_t           new_flavor)
495 {
496 	int     i;
497 	ipc_port_t      old_port[EXC_TYPES_COUNT];
498 
499 #if CONFIG_MACF
500 	struct label *deferred_labels[EXC_TYPES_COUNT];
501 	struct label *new_label;
502 #endif
503 
504 	if (host_priv == HOST_PRIV_NULL) {
505 		return KERN_INVALID_ARGUMENT;
506 	}
507 
508 	if (exception_mask & ~EXC_MASK_VALID) {
509 		return KERN_INVALID_ARGUMENT;
510 	}
511 
512 	if (IP_VALID(new_port)) {
513 		switch (new_behavior & ~MACH_EXCEPTION_MASK) {
514 		case EXCEPTION_DEFAULT:
515 		case EXCEPTION_STATE:
516 		case EXCEPTION_STATE_IDENTITY:
517 		case EXCEPTION_IDENTITY_PROTECTED:
518 			break;
519 		default:
520 			return KERN_INVALID_ARGUMENT;
521 		}
522 	}
523 
524 	/*
525 	 * Check the validity of the thread_state_flavor by calling the
526 	 * VALID_THREAD_STATE_FLAVOR architecture dependent macro defined in
527 	 * osfmk/mach/ARCHITECTURE/thread_status.h
528 	 */
529 	if (new_flavor != 0 && !VALID_THREAD_STATE_FLAVOR(new_flavor)) {
530 		return KERN_INVALID_ARGUMENT;
531 	}
532 
533 	if (((new_behavior & ~MACH_EXCEPTION_MASK) == EXCEPTION_IDENTITY_PROTECTED ||
534 	    (new_behavior & MACH_EXCEPTION_BACKTRACE_PREFERRED))
535 	    && !(new_behavior & MACH_EXCEPTION_CODES)) {
536 		return KERN_INVALID_ARGUMENT;
537 	}
538 
539 	if (!set_exception_behavior_allowed(new_port, new_behavior, NULL, exception_mask, "host")) {
540 		return KERN_NO_ACCESS;
541 	}
542 
543 #if CONFIG_MACF
544 	if (mac_task_check_set_host_exception_ports(current_task(), exception_mask) != 0) {
545 		return KERN_NO_ACCESS;
546 	}
547 
548 	new_label = mac_exc_create_label_for_current_proc();
549 
550 	for (i = FIRST_EXCEPTION; i < EXC_TYPES_COUNT; i++) {
551 		if (mac_exc_label(&host_priv->exc_actions[i]) == NULL) {
552 			deferred_labels[i] = mac_exc_create_label(&host_priv->exc_actions[i]);
553 		} else {
554 			deferred_labels[i] = NULL;
555 		}
556 	}
557 #endif
558 
559 	host_lock(host_priv);
560 
561 	for (i = FIRST_EXCEPTION; i < EXC_TYPES_COUNT; i++) {
562 #if CONFIG_MACF
563 		if (mac_exc_label(&host_priv->exc_actions[i]) == NULL) {
564 			// Lazy initialization (see ipc_port_init).
565 			mac_exc_associate_action_label(&host_priv->exc_actions[i], deferred_labels[i]);
566 			deferred_labels[i] = NULL; // Label is used, do not free.
567 		}
568 #endif
569 
570 		if ((exception_mask & (1 << i))
571 #if CONFIG_MACF
572 		    && mac_exc_update_action_label(&host_priv->exc_actions[i], new_label) == 0
573 #endif
574 		    ) {
575 			old_port[i] = host_priv->exc_actions[i].port;
576 
577 			host_priv->exc_actions[i].port =
578 			    exception_port_copy_send(new_port);
579 			host_priv->exc_actions[i].behavior = new_behavior;
580 			host_priv->exc_actions[i].flavor = new_flavor;
581 		} else {
582 			old_port[i] = IP_NULL;
583 		}
584 	}/* for */
585 
586 	/*
587 	 * Consume send rights without any lock held.
588 	 */
589 	host_unlock(host_priv);
590 
591 #if CONFIG_MACF
592 	mac_exc_free_label(new_label);
593 #endif
594 
595 	for (i = FIRST_EXCEPTION; i < EXC_TYPES_COUNT; i++) {
596 		if (IP_VALID(old_port[i])) {
597 			ipc_port_release_send(old_port[i]);
598 		}
599 #if CONFIG_MACF
600 		if (deferred_labels[i] != NULL) {
601 			/* Deferred label went unused: Another thread has completed the lazy initialization. */
602 			mac_exc_free_label(deferred_labels[i]);
603 		}
604 #endif
605 	}
606 	if (IP_VALID(new_port)) {        /* consume send right */
607 		ipc_port_release_send(new_port);
608 	}
609 
610 	return KERN_SUCCESS;
611 }
612 
613 /*
614  *	Routine:	host_get_exception_ports [kernel call]
615  *	Purpose:
616  *		Clones a send right for each of the host's exception
617  *		ports specified in the mask and returns the behaviour
618  *		and flavor of said port.
619  *
620  *		Returns upto [in} CountCnt elements.
621  *
622  *	Conditions:
623  *		Nothing locked.
624  *	Returns:
625  *		KERN_SUCCESS		Extracted a send right.
626  *		KERN_INVALID_ARGUMENT	Invalid host_priv specified,
627  *					Invalid special port,
628  *					Illegal mask bit set.
629  *		KERN_FAILURE		The thread is dead.
630  */
631 kern_return_t
host_get_exception_ports(host_priv_t host_priv,exception_mask_t exception_mask,exception_mask_array_t masks,mach_msg_type_number_t * CountCnt,exception_port_array_t ports,exception_behavior_array_t behaviors,thread_state_flavor_array_t flavors)632 host_get_exception_ports(
633 	host_priv_t                     host_priv,
634 	exception_mask_t                exception_mask,
635 	exception_mask_array_t          masks,
636 	mach_msg_type_number_t          * CountCnt,
637 	exception_port_array_t          ports,
638 	exception_behavior_array_t      behaviors,
639 	thread_state_flavor_array_t     flavors         )
640 {
641 	unsigned int    i, j, count;
642 
643 	if (host_priv == HOST_PRIV_NULL) {
644 		return KERN_INVALID_ARGUMENT;
645 	}
646 
647 	if (exception_mask & ~EXC_MASK_VALID) {
648 		return KERN_INVALID_ARGUMENT;
649 	}
650 
651 	host_lock(host_priv);
652 
653 	count = 0;
654 
655 	for (i = FIRST_EXCEPTION; i < EXC_TYPES_COUNT; i++) {
656 		if (exception_mask & (1 << i)) {
657 			for (j = 0; j < count; j++) {
658 /*
659  *				search for an identical entry, if found
660  *				set corresponding mask for this exception.
661  */
662 				if (host_priv->exc_actions[i].port == ports[j] &&
663 				    host_priv->exc_actions[i].behavior == behaviors[j]
664 				    && host_priv->exc_actions[i].flavor == flavors[j]) {
665 					masks[j] |= (1 << i);
666 					break;
667 				}
668 			}/* for */
669 			if (j == count && count < *CountCnt) {
670 				masks[j] = (1 << i);
671 				ports[j] =
672 				    exception_port_copy_send(host_priv->exc_actions[i].port);
673 				behaviors[j] = host_priv->exc_actions[i].behavior;
674 				flavors[j] = host_priv->exc_actions[i].flavor;
675 				count++;
676 			}
677 		}
678 	}/* for */
679 	host_unlock(host_priv);
680 
681 	*CountCnt = count;
682 	return KERN_SUCCESS;
683 }
684 
685 kern_return_t
host_swap_exception_ports(host_priv_t host_priv,exception_mask_t exception_mask,ipc_port_t new_port,exception_behavior_t new_behavior,thread_state_flavor_t new_flavor,exception_mask_array_t masks,mach_msg_type_number_t * CountCnt,exception_port_array_t ports,exception_behavior_array_t behaviors,thread_state_flavor_array_t flavors)686 host_swap_exception_ports(
687 	host_priv_t                     host_priv,
688 	exception_mask_t                exception_mask,
689 	ipc_port_t                      new_port,
690 	exception_behavior_t            new_behavior,
691 	thread_state_flavor_t           new_flavor,
692 	exception_mask_array_t          masks,
693 	mach_msg_type_number_t          * CountCnt,
694 	exception_port_array_t          ports,
695 	exception_behavior_array_t      behaviors,
696 	thread_state_flavor_array_t     flavors         )
697 {
698 	unsigned int    i,
699 	    j,
700 	    count;
701 	ipc_port_t      old_port[EXC_TYPES_COUNT];
702 
703 #if CONFIG_MACF
704 	struct label *deferred_labels[EXC_TYPES_COUNT];
705 	struct label *new_label;
706 #endif
707 
708 	if (host_priv == HOST_PRIV_NULL) {
709 		return KERN_INVALID_ARGUMENT;
710 	}
711 
712 	if (exception_mask & ~EXC_MASK_VALID) {
713 		return KERN_INVALID_ARGUMENT;
714 	}
715 
716 	if (IP_VALID(new_port)) {
717 		switch (new_behavior & ~MACH_EXCEPTION_MASK) {
718 		case EXCEPTION_DEFAULT:
719 		case EXCEPTION_STATE:
720 		case EXCEPTION_STATE_IDENTITY:
721 		case EXCEPTION_IDENTITY_PROTECTED:
722 			break;
723 		default:
724 			return KERN_INVALID_ARGUMENT;
725 		}
726 	}
727 
728 	if (new_flavor != 0 && !VALID_THREAD_STATE_FLAVOR(new_flavor)) {
729 		return KERN_INVALID_ARGUMENT;
730 	}
731 
732 	if (((new_behavior & ~MACH_EXCEPTION_MASK) == EXCEPTION_IDENTITY_PROTECTED ||
733 	    (new_behavior & MACH_EXCEPTION_BACKTRACE_PREFERRED))
734 	    && !(new_behavior & MACH_EXCEPTION_CODES)) {
735 		return KERN_INVALID_ARGUMENT;
736 	}
737 
738 	if (!set_exception_behavior_allowed(new_port, new_behavior, NULL, exception_mask, "host")) {
739 		return KERN_NO_ACCESS;
740 	}
741 
742 #if CONFIG_MACF
743 	if (mac_task_check_set_host_exception_ports(current_task(), exception_mask) != 0) {
744 		return KERN_NO_ACCESS;
745 	}
746 
747 	new_label = mac_exc_create_label_for_current_proc();
748 
749 	for (i = FIRST_EXCEPTION; i < EXC_TYPES_COUNT; i++) {
750 		if (mac_exc_label(&host_priv->exc_actions[i]) == NULL) {
751 			deferred_labels[i] = mac_exc_create_label(&host_priv->exc_actions[i]);
752 		} else {
753 			deferred_labels[i] = NULL;
754 		}
755 	}
756 #endif /* CONFIG_MACF */
757 
758 	host_lock(host_priv);
759 
760 	assert(EXC_TYPES_COUNT > FIRST_EXCEPTION);
761 	for (count = 0, i = FIRST_EXCEPTION; i < EXC_TYPES_COUNT && count < *CountCnt; i++) {
762 #if CONFIG_MACF
763 		if (mac_exc_label(&host_priv->exc_actions[i]) == NULL) {
764 			// Lazy initialization (see ipc_port_init).
765 			mac_exc_associate_action_label(&host_priv->exc_actions[i], deferred_labels[i]);
766 			deferred_labels[i] = NULL; // Label is used, do not free.
767 		}
768 #endif
769 
770 		if ((exception_mask & (1 << i))
771 #if CONFIG_MACF
772 		    && mac_exc_update_action_label(&host_priv->exc_actions[i], new_label) == 0
773 #endif
774 		    ) {
775 			for (j = 0; j < count; j++) {
776 /*
777  *				search for an identical entry, if found
778  *				set corresponding mask for this exception.
779  */
780 				if (host_priv->exc_actions[i].port == ports[j] &&
781 				    host_priv->exc_actions[i].behavior == behaviors[j]
782 				    && host_priv->exc_actions[i].flavor == flavors[j]) {
783 					masks[j] |= (1 << i);
784 					break;
785 				}
786 			}/* for */
787 			if (j == count) {
788 				masks[j] = (1 << i);
789 				ports[j] =
790 				    exception_port_copy_send(host_priv->exc_actions[i].port);
791 				behaviors[j] = host_priv->exc_actions[i].behavior;
792 				flavors[j] = host_priv->exc_actions[i].flavor;
793 				count++;
794 			}
795 			old_port[i] = host_priv->exc_actions[i].port;
796 			host_priv->exc_actions[i].port =
797 			    exception_port_copy_send(new_port);
798 			host_priv->exc_actions[i].behavior = new_behavior;
799 			host_priv->exc_actions[i].flavor = new_flavor;
800 		} else {
801 			old_port[i] = IP_NULL;
802 		}
803 	}/* for */
804 	host_unlock(host_priv);
805 
806 #if CONFIG_MACF
807 	mac_exc_free_label(new_label);
808 #endif
809 
810 	/*
811 	 * Consume send rights without any lock held.
812 	 */
813 	while (--i >= FIRST_EXCEPTION) {
814 		if (IP_VALID(old_port[i])) {
815 			ipc_port_release_send(old_port[i]);
816 		}
817 #if CONFIG_MACF
818 		if (deferred_labels[i] != NULL) {
819 			mac_exc_free_label(deferred_labels[i]); // Label unused.
820 		}
821 #endif
822 	}
823 
824 	if (IP_VALID(new_port)) {        /* consume send right */
825 		ipc_port_release_send(new_port);
826 	}
827 	*CountCnt = count;
828 
829 	return KERN_SUCCESS;
830 }
831