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