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