xref: /xnu-11215.1.10/osfmk/kern/ipc_host.c (revision 8d741a5de7ff4191bf97d57b9f54c2f6d4a15585)
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 	kern_return_t kr = set_exception_ports_validation(NULL, exception_mask, new_port, new_behavior, new_flavor, false);
509 	if (kr != KERN_SUCCESS) {
510 		return kr;
511 	}
512 
513 #if CONFIG_MACF
514 	if (mac_task_check_set_host_exception_ports(current_task(), exception_mask) != 0) {
515 		return KERN_NO_ACCESS;
516 	}
517 
518 	new_label = mac_exc_create_label_for_current_proc();
519 
520 	for (i = FIRST_EXCEPTION; i < EXC_TYPES_COUNT; i++) {
521 		if (mac_exc_label(&host_priv->exc_actions[i]) == NULL) {
522 			deferred_labels[i] = mac_exc_create_label(&host_priv->exc_actions[i]);
523 		} else {
524 			deferred_labels[i] = NULL;
525 		}
526 	}
527 #endif
528 
529 	host_lock(host_priv);
530 
531 	for (i = FIRST_EXCEPTION; i < EXC_TYPES_COUNT; i++) {
532 #if CONFIG_MACF
533 		if (mac_exc_label(&host_priv->exc_actions[i]) == NULL) {
534 			// Lazy initialization (see ipc_port_init).
535 			mac_exc_associate_action_label(&host_priv->exc_actions[i], deferred_labels[i]);
536 			deferred_labels[i] = NULL; // Label is used, do not free.
537 		}
538 #endif
539 
540 		if ((exception_mask & (1 << i))
541 #if CONFIG_MACF
542 		    && mac_exc_update_action_label(&host_priv->exc_actions[i], new_label) == 0
543 #endif
544 		    ) {
545 			old_port[i] = host_priv->exc_actions[i].port;
546 
547 			host_priv->exc_actions[i].port =
548 			    exception_port_copy_send(new_port);
549 			host_priv->exc_actions[i].behavior = new_behavior;
550 			host_priv->exc_actions[i].flavor = new_flavor;
551 		} else {
552 			old_port[i] = IP_NULL;
553 		}
554 	}/* for */
555 
556 	/*
557 	 * Consume send rights without any lock held.
558 	 */
559 	host_unlock(host_priv);
560 
561 #if CONFIG_MACF
562 	mac_exc_free_label(new_label);
563 #endif
564 
565 	for (i = FIRST_EXCEPTION; i < EXC_TYPES_COUNT; i++) {
566 		if (IP_VALID(old_port[i])) {
567 			ipc_port_release_send(old_port[i]);
568 		}
569 #if CONFIG_MACF
570 		if (deferred_labels[i] != NULL) {
571 			/* Deferred label went unused: Another thread has completed the lazy initialization. */
572 			mac_exc_free_label(deferred_labels[i]);
573 		}
574 #endif
575 	}
576 	if (IP_VALID(new_port)) {        /* consume send right */
577 		ipc_port_release_send(new_port);
578 	}
579 
580 	return KERN_SUCCESS;
581 }
582 
583 /*
584  *	Routine:	host_get_exception_ports [kernel call]
585  *	Purpose:
586  *		Clones a send right for each of the host's exception
587  *		ports specified in the mask and returns the behaviour
588  *		and flavor of said port.
589  *
590  *		Returns upto [in} CountCnt elements.
591  *
592  *	Conditions:
593  *		Nothing locked.
594  *	Returns:
595  *		KERN_SUCCESS		Extracted a send right.
596  *		KERN_INVALID_ARGUMENT	Invalid host_priv specified,
597  *					Invalid special port,
598  *					Illegal mask bit set.
599  *		KERN_FAILURE		The thread is dead.
600  */
601 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)602 host_get_exception_ports(
603 	host_priv_t                     host_priv,
604 	exception_mask_t                exception_mask,
605 	exception_mask_array_t          masks,
606 	mach_msg_type_number_t          * CountCnt,
607 	exception_port_array_t          ports,
608 	exception_behavior_array_t      behaviors,
609 	thread_state_flavor_array_t     flavors         )
610 {
611 	unsigned int    i, j, count;
612 
613 	if (host_priv == HOST_PRIV_NULL) {
614 		return KERN_INVALID_ARGUMENT;
615 	}
616 
617 	if (exception_mask & ~EXC_MASK_VALID) {
618 		return KERN_INVALID_ARGUMENT;
619 	}
620 
621 	host_lock(host_priv);
622 
623 	count = 0;
624 
625 	for (i = FIRST_EXCEPTION; i < EXC_TYPES_COUNT; i++) {
626 		if (exception_mask & (1 << i)) {
627 			for (j = 0; j < count; j++) {
628 /*
629  *				search for an identical entry, if found
630  *				set corresponding mask for this exception.
631  */
632 				if (host_priv->exc_actions[i].port == ports[j] &&
633 				    host_priv->exc_actions[i].behavior == behaviors[j]
634 				    && host_priv->exc_actions[i].flavor == flavors[j]) {
635 					masks[j] |= (1 << i);
636 					break;
637 				}
638 			}/* for */
639 			if (j == count && count < *CountCnt) {
640 				masks[j] = (1 << i);
641 				ports[j] =
642 				    exception_port_copy_send(host_priv->exc_actions[i].port);
643 				behaviors[j] = host_priv->exc_actions[i].behavior;
644 				flavors[j] = host_priv->exc_actions[i].flavor;
645 				count++;
646 			}
647 		}
648 	}/* for */
649 	host_unlock(host_priv);
650 
651 	*CountCnt = count;
652 	return KERN_SUCCESS;
653 }
654 
655 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)656 host_swap_exception_ports(
657 	host_priv_t                     host_priv,
658 	exception_mask_t                exception_mask,
659 	ipc_port_t                      new_port,
660 	exception_behavior_t            new_behavior,
661 	thread_state_flavor_t           new_flavor,
662 	exception_mask_array_t          masks,
663 	mach_msg_type_number_t          * CountCnt,
664 	exception_port_array_t          ports,
665 	exception_behavior_array_t      behaviors,
666 	thread_state_flavor_array_t     flavors         )
667 {
668 	unsigned int    i,
669 	    j,
670 	    count;
671 	ipc_port_t      old_port[EXC_TYPES_COUNT];
672 
673 #if CONFIG_MACF
674 	struct label *deferred_labels[EXC_TYPES_COUNT];
675 	struct label *new_label;
676 #endif
677 
678 	if (host_priv == HOST_PRIV_NULL) {
679 		return KERN_INVALID_ARGUMENT;
680 	}
681 
682 	kern_return_t kr = set_exception_ports_validation(NULL, exception_mask, new_port, new_behavior, new_flavor, false);
683 	if (kr != KERN_SUCCESS) {
684 		return kr;
685 	}
686 
687 #if CONFIG_MACF
688 	if (mac_task_check_set_host_exception_ports(current_task(), exception_mask) != 0) {
689 		return KERN_NO_ACCESS;
690 	}
691 
692 	new_label = mac_exc_create_label_for_current_proc();
693 
694 	for (i = FIRST_EXCEPTION; i < EXC_TYPES_COUNT; i++) {
695 		if (mac_exc_label(&host_priv->exc_actions[i]) == NULL) {
696 			deferred_labels[i] = mac_exc_create_label(&host_priv->exc_actions[i]);
697 		} else {
698 			deferred_labels[i] = NULL;
699 		}
700 	}
701 #endif /* CONFIG_MACF */
702 
703 	host_lock(host_priv);
704 
705 	assert(EXC_TYPES_COUNT > FIRST_EXCEPTION);
706 	for (count = 0, i = FIRST_EXCEPTION; i < EXC_TYPES_COUNT && count < *CountCnt; i++) {
707 #if CONFIG_MACF
708 		if (mac_exc_label(&host_priv->exc_actions[i]) == NULL) {
709 			// Lazy initialization (see ipc_port_init).
710 			mac_exc_associate_action_label(&host_priv->exc_actions[i], deferred_labels[i]);
711 			deferred_labels[i] = NULL; // Label is used, do not free.
712 		}
713 #endif
714 
715 		if ((exception_mask & (1 << i))
716 #if CONFIG_MACF
717 		    && mac_exc_update_action_label(&host_priv->exc_actions[i], new_label) == 0
718 #endif
719 		    ) {
720 			for (j = 0; j < count; j++) {
721 /*
722  *				search for an identical entry, if found
723  *				set corresponding mask for this exception.
724  */
725 				if (host_priv->exc_actions[i].port == ports[j] &&
726 				    host_priv->exc_actions[i].behavior == behaviors[j]
727 				    && host_priv->exc_actions[i].flavor == flavors[j]) {
728 					masks[j] |= (1 << i);
729 					break;
730 				}
731 			}/* for */
732 			if (j == count) {
733 				masks[j] = (1 << i);
734 				ports[j] =
735 				    exception_port_copy_send(host_priv->exc_actions[i].port);
736 				behaviors[j] = host_priv->exc_actions[i].behavior;
737 				flavors[j] = host_priv->exc_actions[i].flavor;
738 				count++;
739 			}
740 			old_port[i] = host_priv->exc_actions[i].port;
741 			host_priv->exc_actions[i].port =
742 			    exception_port_copy_send(new_port);
743 			host_priv->exc_actions[i].behavior = new_behavior;
744 			host_priv->exc_actions[i].flavor = new_flavor;
745 		} else {
746 			old_port[i] = IP_NULL;
747 		}
748 	}/* for */
749 	host_unlock(host_priv);
750 
751 #if CONFIG_MACF
752 	mac_exc_free_label(new_label);
753 #endif
754 
755 	/*
756 	 * Consume send rights without any lock held.
757 	 */
758 	while (--i >= FIRST_EXCEPTION) {
759 		if (IP_VALID(old_port[i])) {
760 			ipc_port_release_send(old_port[i]);
761 		}
762 #if CONFIG_MACF
763 		if (deferred_labels[i] != NULL) {
764 			mac_exc_free_label(deferred_labels[i]); // Label unused.
765 		}
766 #endif
767 	}
768 
769 	if (IP_VALID(new_port)) {        /* consume send right */
770 		ipc_port_release_send(new_port);
771 	}
772 	*CountCnt = count;
773 
774 	return KERN_SUCCESS;
775 }
776