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