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