xref: /NextBSD/sys/compat/mach/kern/ipc_tt.c (revision 54bf82fa47b244585d60a1e89027c9dd6658dda9)
1 /*
2  * Copyright 1991-1998 by Open Software Foundation, Inc.
3  *              All Rights Reserved
4  *
5  * Permission to use, copy, modify, and distribute this software and
6  * its documentation for any purpose and without fee is hereby granted,
7  * provided that the above copyright notice appears in all copies and
8  * that both the copyright notice and this permission notice appear in
9  * supporting documentation.
10  *
11  * OSF DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
12  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
13  * FOR A PARTICULAR PURPOSE.
14  *
15  * IN NO EVENT SHALL OSF BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
16  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
17  * LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
18  * NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
19  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20  */
21 /*
22  * MkLinux
23  */
24 /* CMU_HIST */
25 /*
26  * Revision 2.12  91/06/25  10:28:49  rpd
27  * 	Changed the convert_foo_to_bar functions
28  * 	to use ipc_port_t instead of mach_port_t.
29  * 	[91/05/27            rpd]
30  *
31  * Revision 2.11  91/06/17  15:47:09  jsb
32  * 	Renamed NORMA conditionals. Moved norma code to norma/kern_task.c.
33  * 	[91/06/17  10:50:57  jsb]
34  *
35  * Revision 2.10  91/06/06  17:07:15  jsb
36  * 	NORMA_TASK support.
37  * 	[91/05/14  09:17:09  jsb]
38  *
39  * Revision 2.9  91/05/14  16:42:54  mrt
40  * 	Correcting copyright
41  *
42  * Revision 2.8  91/03/16  14:50:24  rpd
43  * 	Removed ith_saved.
44  * 	[91/02/16            rpd]
45  *
46  * Revision 2.7  91/02/05  17:27:08  mrt
47  * 	Changed to new Mach copyright
48  * 	[91/02/01  16:13:42  mrt]
49  *
50  * Revision 2.6  91/01/08  15:16:05  rpd
51  * 	Added retrieve_task_self_fast, retrieve_thread_self_fast.
52  * 	[90/12/27            rpd]
53  *
54  * Revision 2.5  90/11/05  14:31:08  rpd
55  * 	Changed ip_reference to ipc_port_reference.
56  * 	Use new ip_reference and ip_release.
57  * 	[90/10/29            rpd]
58  *
59  * Revision 2.4  90/06/02  14:54:33  rpd
60  * 	Converted to new IPC.
61  * 	[90/03/26  22:05:07  rpd]
62  *
63  *
64  * Condensed history:
65  *	Modified for pure kernel (dbg).
66  *	Support thread_exception_abort (dlb).
67  *	Added kernel monitor support (tfl).
68  *	Added task/thread kernel port interposing (rpd).
69  *	Improvements/fixes for task_secure (rpd).
70  * 	New translation cache (rpd).
71  *	Move old stuff under MACH_IPC_XXXHACK (rpd).
72  *	Created from mach_ipc.c (rpd).
73  */
74 /* CMU_ENDHIST */
75 /*
76  * Mach Operating System
77  * Copyright (c) 1991,1990,1989,1988,1987 Carnegie Mellon University
78  * All Rights Reserved.
79  *
80  * Permission to use, copy, modify and distribute this software and its
81  * documentation is hereby granted, provided that both the copyright
82  * notice and this permission notice appear in all copies of the
83  * software, derivative works or modified versions, and any portions
84  * thereof, and that both notices appear in supporting documentation.
85  *
86  * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
87  * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
88  * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
89  *
90  * Carnegie Mellon requests users of this software to return to
91  *
92  *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
93  *  School of Computer Science
94  *  Carnegie Mellon University
95  *  Pittsburgh PA 15213-3890
96  *
97  * any improvements or extensions that they make and grant Carnegie Mellon
98  * the rights to redistribute these changes.
99  */
100 /*
101  */
102 
103 /*
104  * File:	ipc_tt.c
105  * Purpose:
106  *	Task and thread related IPC functions.
107  */
108 
109 #include <sys/cdefs.h>
110 #include <sys/types.h>
111 #include <sys/syslog.h>
112 #include <sys/mach/mach_types.h>
113 #include <sys/mach/kern_return.h>
114 #include <sys/mach/mach_param.h>
115 #include <sys/mach/task_special_ports.h>
116 #include <sys/mach/thread_special_ports.h>
117 #include <sys/mach/thread_status.h>
118 #include <sys/mach/exception.h>
119 #if 0
120 #include <sys/mach/mach_traps.h>
121 #include <sys/mach/mach_server.h>
122 #endif
123 #include <sys/mach/mach_host_server.h>
124 #include <sys/mach/ipc_tt.h>
125 #if 0
126 #include <kern/thread_act.h>
127 #include <kern/misc_protos.h>
128 #endif
129 #include <sys/mach/host_special_ports.h>
130 #include <sys/mach/host.h>
131 #include <sys/mach/thread.h>
132 #include <sys/mach/mach_init.h>
133 #include <sys/mach/task_server.h>
134 #include <sys/mach/host_priv_server.h>
135 
136 #define THR_ACT_NULL NULL
137 
138 
139 
140 /*
141  *	Routine:	ipc_task_init
142  *	Purpose:
143  *		Initialize a task's IPC state.
144  *
145  *		If non-null, some state will be inherited from the parent.
146  *		The parent must be appropriately initialized.
147  *	Conditions:
148  *		Nothing locked.
149  */
150 
151 void
ipc_task_create(task_t task)152 ipc_task_create(
153 	task_t		task)
154 {
155 	ipc_space_t space;
156 	kern_return_t kr;
157 	ipc_port_t kport;
158 
159 	kr = ipc_space_create(&ipc_table_entries[0], &space);
160 	if (kr != KERN_SUCCESS)
161 		panic("ipc_task_init");
162 
163 	kport = ipc_port_alloc_kernel();
164 	if (kport == IP_NULL)
165 		panic("ipc_task_init");
166 
167 	task->itk_self = kport;
168 	task->itk_sself = ipc_port_make_send(kport);
169 	task->itk_space = space;
170 	space->is_task = task;
171 }
172 
173 void
ipc_task_init(task_t task,task_t parent)174 ipc_task_init(
175 	task_t		task,
176 	task_t		parent)
177 {
178 	int i;
179 
180 	if (parent == TASK_NULL) {
181 		for (i = FIRST_EXCEPTION; i < EXC_TYPES_COUNT; i++) {
182 			task->exc_actions[i].port = IP_NULL;
183 		}/* for */
184 		task->exc_actions[EXC_MACH_SYSCALL].port =
185 			ipc_port_make_send(realhost.host_self);
186 		task->itk_bootstrap = IP_NULL;
187 		for (i = 0; i < TASK_PORT_REGISTER_MAX; i++)
188 			task->itk_registered[i] = IP_NULL;
189 	} else {
190 		itk_lock(parent);
191 		assert(parent->itk_self != IP_NULL);
192 
193 		/* inherit registered ports */
194 
195 		for (i = 0; i < TASK_PORT_REGISTER_MAX; i++)
196 			task->itk_registered[i] =
197 				ipc_port_copy_send(parent->itk_registered[i]);
198 
199 		/* inherit exception and bootstrap ports */
200 
201 		for (i = FIRST_EXCEPTION; i < EXC_TYPES_COUNT; i++) {
202 		    task->exc_actions[i].port =
203 		  		ipc_port_copy_send(parent->exc_actions[i].port);
204 		    task->exc_actions[i].flavor =
205 				parent->exc_actions[i].flavor;
206 		    task->exc_actions[i].behavior =
207 				parent->exc_actions[i].behavior;
208 		}/* for */
209 		task->itk_bootstrap =
210 			ipc_port_copy_send(parent->itk_bootstrap);
211 
212 		itk_unlock(parent);
213 	}
214 }
215 
216 /*
217  *	Routine:	ipc_task_enable
218  *	Purpose:
219  *		Enable a task for IPC access.
220  *	Conditions:
221  *		Nothing locked.
222  */
223 
224 void
ipc_task_enable(task_t task)225 ipc_task_enable(
226 	task_t		task)
227 {
228 	ipc_port_t kport;
229 
230 	itk_lock(task);
231 	kport = task->itk_self;
232 	if (kport != IP_NULL)
233 		ipc_kobject_set(kport, (ipc_kobject_t) task, IKOT_TASK);
234 	itk_unlock(task);
235 }
236 
237 /*
238  *	Routine:	ipc_task_disable
239  *	Purpose:
240  *		Disable IPC access to a task.
241  *	Conditions:
242  *		Nothing locked.
243  */
244 
245 void
ipc_task_disable(task_t task)246 ipc_task_disable(
247 	task_t		task)
248 {
249 	ipc_port_t kport;
250 
251 	itk_lock(task);
252 	kport = task->itk_self;
253 	if (kport != IP_NULL)
254 		ipc_kobject_set(kport, IKO_NULL, IKOT_NONE);
255 	itk_unlock(task);
256 }
257 
258 /*
259  *	Routine:	ipc_task_terminate
260  *	Purpose:
261  *		Clean up and destroy a task's IPC state.
262  *	Conditions:
263  *		Nothing locked.  The task must be suspended.
264  *		(Or the current thread must be in the task.)
265  */
266 
267 void
ipc_task_terminate(task_t task)268 ipc_task_terminate(
269 	task_t		task)
270 {
271 	ipc_port_t kport;
272 	int i;
273 
274 	itk_lock(task);
275 	kport = task->itk_self;
276 
277 	if (kport == IP_NULL) {
278 		/* the task is already terminated (can this happen?) */
279 		itk_unlock(task);
280 		return;
281 	}
282 
283 	task->itk_self = IP_NULL;
284 	itk_unlock(task);
285 
286 	/* release the naked send rights */
287 
288 	if (IP_VALID(task->itk_sself))
289 		ipc_port_release_send(task->itk_sself);
290 
291 	for (i = FIRST_EXCEPTION; i < EXC_TYPES_COUNT; i++) {
292 		if (IP_VALID(task->exc_actions[i].port)) {
293 			ipc_port_release_send(task->exc_actions[i].port);
294 		}
295 	}/* for */
296 	if (IP_VALID(task->itk_bootstrap))
297 		ipc_port_release_send(task->itk_bootstrap);
298 
299 	for (i = 0; i < TASK_PORT_REGISTER_MAX; i++)
300 		if (IP_VALID(task->itk_registered[i]))
301 			ipc_port_release_send(task->itk_registered[i]);
302 
303 	/* destroy the space, leaving just a reference for it */
304 
305 	if (!task->kernel_loaded)
306 		ipc_space_destroy(task->itk_space);
307 
308 	/* destroy the kernel port */
309 
310 	ipc_port_dealloc_kernel(kport);
311 }
312 
313 /*
314  *	Routine:	ipc_thread_init
315  *	Purpose:
316  *		Initialize a thread's IPC state.
317  *	Conditions:
318  *		Nothing locked.
319  */
320 
321 void
ipc_thread_init(thread_t thread)322 ipc_thread_init(
323 	thread_t	thread)
324 {
325 	ipc_thread_links_init(thread);
326 	ipc_kmsg_queue_init(&thread->ith_messages);
327 	thread->ith_mig_reply = MACH_PORT_NULL;
328 	thread->ith_rpc_reply = IP_NULL;
329 }
330 
331 /*
332  *	Routine:	ipc_thread_terminate
333  *	Purpose:
334  *		Clean up and destroy a thread's IPC state.
335  *	Conditions:
336  *		Nothing locked.  The thread must be suspended.
337  *		(Or be the current thread.)
338  */
339 
340 void
ipc_thread_terminate(thread_t thread)341 ipc_thread_terminate(
342 	thread_t	thread)
343 {
344 	assert(ipc_kmsg_queue_empty(&thread->ith_messages));
345 
346         if (thread->ith_rpc_reply != IP_NULL)
347             ipc_port_dealloc_reply(thread->ith_rpc_reply);
348 	thread->ith_rpc_reply = IP_NULL;
349 }
350 
351 /*
352  *	Routine:	ipc_thr_act_init
353  *	Purpose:
354  *		Initialize an thr_act's IPC state.
355  *	Conditions:
356  *		Nothing locked.
357  */
358 
359 void
ipc_thr_act_init(thread_act_t thr_act)360 ipc_thr_act_init(thread_act_t thr_act)
361 {
362 	ipc_port_t kport; int i;
363 
364 	kport = ipc_port_alloc_kernel();
365 	if (kport == IP_NULL)
366 		panic("ipc_thr_act_init");
367 
368 	thr_act->ith_self = kport;
369 	thr_act->ith_sself = ipc_port_make_send(kport);
370 
371 	for (i = FIRST_EXCEPTION; i < EXC_TYPES_COUNT; i++)
372 		thr_act->exc_actions[i].port = IP_NULL;
373 
374 	thr_act->exc_actions[EXC_MACH_SYSCALL].port =
375 					ipc_port_make_send(realhost.host_self);
376 
377 	ipc_kobject_set(kport, (ipc_kobject_t) thr_act, IKOT_ACT);
378 }
379 
380 #if 0
381 
382 void
383 ipc_thr_act_disable(thread_act_t thr_act)
384 {
385 	int i;
386 	ipc_port_t kport;
387 
388 	act_lock(thr_act);
389 	kport = thr_act->ith_self;
390 
391 	if (kport != IP_NULL)
392 		ipc_kobject_set(kport, IKO_NULL, IKOT_NONE);
393 	act_unlock(thr_act);
394 }
395 
396 
397 void
398 ipc_thr_act_disable_act_locked(thread_act_t thr_act)
399 {
400 	int i;
401 	ipc_port_t kport;
402 
403 	kport = thr_act->ith_self;
404 
405 	if (kport != IP_NULL)
406 		ipc_kobject_set(kport, IKO_NULL, IKOT_NONE);
407 }
408 #endif
409 
410 void
ipc_thr_act_terminate(thread_act_t thr_act)411 ipc_thr_act_terminate(thread_act_t thr_act)
412 {
413 	ipc_port_t kport; int i;
414 
415 	act_lock(thr_act);
416 	kport = thr_act->ith_self;
417 
418 	if (kport == IP_NULL) {
419 		/* the thread is already terminated (can this happen?) */
420 		act_unlock(thr_act);
421 		return;
422 	}
423 
424 	thr_act->ith_self = IP_NULL;
425 	act_unlock(thr_act);
426 
427 	/* release the naked send rights */
428 
429 	if (IP_VALID(thr_act->ith_sself))
430 		ipc_port_release_send(thr_act->ith_sself);
431 	for (i = FIRST_EXCEPTION; i < EXC_TYPES_COUNT; i++) {
432 	    if (IP_VALID(thr_act->exc_actions[i].port))
433 		ipc_port_release_send(thr_act->exc_actions[i].port);
434 	}
435 
436 	/* destroy the kernel port */
437 	ipc_port_dealloc_kernel(kport);
438 }
439 
440 
441 /*
442  *	Routine:	retrieve_task_self_fast
443  *	Purpose:
444  *		Optimized version of retrieve_task_self,
445  *		that only works for the current task.
446  *
447  *		Return a send right (possibly null/dead)
448  *		for the task's user-visible self port.
449  *	Conditions:
450  *		Nothing locked.
451  */
452 
453 ipc_port_t
retrieve_task_self_fast(register task_t task)454 retrieve_task_self_fast(
455 	register task_t		task)
456 {
457 	register ipc_port_t port;
458 
459 	assert(task == current_task());
460 
461 	itk_lock(task);
462 	assert(task->itk_self != IP_NULL);
463 
464 	if ((port = task->itk_sself) == task->itk_self) {
465 		/* no interposing */
466 		itk_unlock(task);
467 		ip_lock(port);
468 		assert(ip_active(port));
469 		ip_reference(port);
470 		port->ip_srights++;
471 		ip_unlock(port);
472 	} else {
473 		itk_unlock(task);
474 		port = ipc_port_copy_send(port);
475 	}
476 
477 	return (port);
478 }
479 
480 /*
481  *	Routine:	retrieve_act_self_fast
482  *	Purpose:
483  *		Optimized version of retrieve_thread_self,
484  *		that only works for the current thread.
485  *
486  *		Return a send right (possibly null/dead)
487  *		for the thread's user-visible self port.
488  *	Conditions:
489  *		Nothing locked.
490  */
491 
492 ipc_port_t
retrieve_thread_self_fast(thread_t thr_act)493 retrieve_thread_self_fast(thread_t thr_act)
494 {
495 	register ipc_port_t port;
496 
497 	assert(thr_act == current_act());
498 	act_lock(thr_act);
499 	assert(thr_act->ith_self != IP_NULL);
500 
501 	if ((port = thr_act->ith_sself) == thr_act->ith_self) {
502 		/* no interposing */
503 
504 		ip_lock(port);
505 		assert(ip_active(port));
506 		ip_reference(port);
507 		port->ip_srights++;
508 		ip_unlock(port);
509 	} else
510 		port = ipc_port_copy_send(port);
511 	act_unlock(thr_act);
512 
513 	return port;
514 }
515 
516 /*
517  *	Routine:	mach_task_self [mach trap]
518  *	Purpose:
519  *		Give the caller send rights for his own task port.
520  *	Conditions:
521  *		Nothing locked.
522  *	Returns:
523  *		MACH_PORT_NULL if there are any resource failures
524  *		or other errors.
525  */
526 
527 mach_port_name_t
mach_task_self(void)528 mach_task_self(void)
529 {
530 	task_t task = current_task();
531 	ipc_space_t space = current_space();
532 	ipc_port_t sright;
533 
534 	sright = retrieve_task_self_fast(task);
535 	return ipc_port_copyout_send(sright, space);
536 }
537 
538 /*
539  *	Routine:	mach_thread_self [mach trap]
540  *	Purpose:
541  *		Give the caller send rights for his own thread port.
542  *	Conditions:
543  *		Nothing locked.
544  *	Returns:
545  *		MACH_PORT_NULL if there are any resource failures
546  *		or other errors.
547  */
548 
549 mach_port_name_t
mach_thread_self(void)550 mach_thread_self(void)
551 {
552 	thread_t thr_act = current_thread();
553 	ipc_space_t space = current_space();
554 	ipc_port_t sright;
555 
556 	sright = retrieve_thread_self_fast(thr_act);
557 	return ipc_port_copyout_send(sright, space);
558 }
559 
560 /*
561  *	Routine:	mach_reply_port [mach trap]
562  *	Purpose:
563  *		Allocate a port for the caller.
564  *	Conditions:
565  *		Nothing locked.
566  *	Returns:
567  *		MACH_PORT_NULL if there are any resource failures
568  *		or other errors.
569  */
570 
571 mach_port_name_t
mach_reply_port(void)572 mach_reply_port(void)
573 {
574 	ipc_port_t port;
575 	mach_port_name_t name;
576 	kern_return_t kr;
577 
578 	kr = ipc_port_alloc(current_task()->itk_space, &name, &port);
579 	if (kr == KERN_SUCCESS)
580 		ip_unlock(port);
581 	else
582 		name = MACH_PORT_NAME_NULL;
583 
584 	return name;
585 }
586 
587 /*
588  *	Routine:	task_get_special_port [kernel call]
589  *	Purpose:
590  *		Clones a send right for one of the task's
591  *		special ports.
592  *	Conditions:
593  *		Nothing locked.
594  *	Returns:
595  *		KERN_SUCCESS		Extracted a send right.
596  *		KERN_INVALID_ARGUMENT	The task is null.
597  *		KERN_FAILURE		The task/space is dead.
598  *		KERN_INVALID_ARGUMENT	Invalid special port.
599  */
600 
601 kern_return_t
task_get_special_port(task_t task,int which,ipc_port_t * portp)602 task_get_special_port(
603 	task_t		task,
604 	int		which,
605 	ipc_port_t	*portp)
606 {
607 	ipc_port_t port;
608 
609 	if (task == TASK_NULL)
610 		return KERN_INVALID_ARGUMENT;
611 
612 	switch (which) {
613 	case TASK_KERNEL_PORT:
614 		port = ipc_port_copy_send(task->itk_sself);
615 		break;
616 
617 	    case TASK_NAME_PORT:
618 		port = ipc_port_make_send(task->itk_nself);
619 		break;
620 
621 	    case TASK_HOST_PORT:
622 		port = ipc_port_copy_send(task->itk_host);
623 		break;
624 
625 	    case TASK_BOOTSTRAP_PORT:
626 			port = ipc_port_copy_send(task->itk_bootstrap);
627 		break;
628 
629 	    case TASK_SEATBELT_PORT:
630 		port = ipc_port_copy_send(task->itk_seatbelt);
631 		break;
632 
633 	    case TASK_ACCESS_PORT:
634 		port = ipc_port_copy_send(task->itk_task_access);
635 		break;
636 
637 		case TASK_DEBUG_CONTROL_PORT:
638 		port = ipc_port_copy_send(task->itk_debug_control);
639 		break;
640 	    default:
641 		return KERN_INVALID_ARGUMENT;
642 	}
643 
644 	*portp = port;
645 	return KERN_SUCCESS;
646 }
647 
648 /*
649  *	Routine:	task_set_special_port [kernel call]
650  *	Purpose:
651  *		Changes one of the task's special ports,
652  *		setting it to the supplied send right.
653  *	Conditions:
654  *		Nothing locked.  If successful, consumes
655  *		the supplied send right.
656  *	Returns:
657  *		KERN_SUCCESS		Changed the special port.
658  *		KERN_INVALID_ARGUMENT	The task is null.
659  *		KERN_FAILURE		The task/space is dead.
660  *		KERN_INVALID_ARGUMENT	Invalid special port.
661  */
662 
663 kern_return_t
task_set_special_port(task_t task,int which,ipc_port_t port)664 task_set_special_port(
665 	task_t		task,
666 	int		which,
667 	ipc_port_t	port)
668 {
669 	ipc_port_t *whichp;
670 	ipc_port_t old;
671 
672 #if VERBOSE_DEBUGGING
673 	printf("task_set_special_port(task=%p, which=%d, port=%p)\n",task, which, port);
674 #endif
675 	/* we only support the current task */
676 	if (task == TASK_NULL)
677 		return KERN_INVALID_ARGUMENT;
678 
679 	switch (which) {
680 	case TASK_KERNEL_PORT:
681 		whichp = &task->itk_sself;
682 		break;
683 	case TASK_HOST_PORT:
684 		whichp = &task->itk_host;
685 		break;
686 	case TASK_BOOTSTRAP_PORT:
687 		whichp = &task->itk_bootstrap;
688 		break;
689 	case TASK_SEATBELT_PORT:
690 		whichp = &task->itk_seatbelt;
691 		break;
692 	case TASK_ACCESS_PORT:
693 		whichp = &task->itk_task_access;
694 		break;
695 	case TASK_DEBUG_CONTROL_PORT:
696 		whichp = &task->itk_debug_control;
697 		break;
698 	default:
699 		return KERN_INVALID_ARGUMENT;
700 	}/* switch */
701 
702 	if ((TASK_SEATBELT_PORT == which  || TASK_ACCESS_PORT == which)
703 		&& IP_VALID(*whichp)) {
704 			itk_unlock(task);
705 			return KERN_NO_ACCESS;
706 	}
707 	itk_lock(task);
708 	if (task->itk_self == IP_NULL) {
709 		itk_unlock(task);
710 		return KERN_FAILURE;
711 	}
712 
713 	old = *whichp;
714 	*whichp = port;
715 	itk_unlock(task);
716 
717 	if (IP_VALID(old))
718 		ipc_port_release_send(old);
719 	return KERN_SUCCESS;
720 }
721 
722 
723 /*
724  *	Routine:	mach_ports_register [kernel call]
725  *	Purpose:
726  *		Stash a handful of port send rights in the task.
727  *		Child tasks will inherit these rights, but they
728  *		must use mach_ports_lookup to acquire them.
729  *
730  *		The rights are supplied in a (wired) kalloc'd segment.
731  *		Rights which aren't supplied are assumed to be null.
732  *	Conditions:
733  *		Nothing locked.  If successful, consumes
734  *		the supplied rights and memory.
735  *	Returns:
736  *		KERN_SUCCESS		Stashed the port rights.
737  *		KERN_INVALID_ARGUMENT	The task is null.
738  *		KERN_INVALID_ARGUMENT	The task is dead.
739  *		KERN_INVALID_ARGUMENT	Too many port rights supplied.
740  */
741 
742 kern_return_t
mach_ports_register(task_t task,mach_port_array_t memory,mach_msg_type_number_t portsCnt)743 mach_ports_register(
744 	task_t			task,
745 	mach_port_array_t	memory,
746 	mach_msg_type_number_t	portsCnt)
747 {
748 	ipc_port_t ports[TASK_PORT_REGISTER_MAX];
749 	int i;
750 
751 	if ((task == TASK_NULL) ||
752 	    (portsCnt > TASK_PORT_REGISTER_MAX))
753 		return KERN_INVALID_ARGUMENT;
754 
755 	/*
756 	 *	Pad the port rights with nulls.
757 	 */
758 
759 	for (i = 0; i < portsCnt; i++)
760 		ports[i] = (ipc_port_t) memory[i];
761 	for (; i < TASK_PORT_REGISTER_MAX; i++)
762 		ports[i] = IP_NULL;
763 
764 	itk_lock(task);
765 	if (task->itk_self == IP_NULL) {
766 		itk_unlock(task);
767 		return KERN_INVALID_ARGUMENT;
768 	}
769 
770 	/*
771 	 *	Replace the old send rights with the new.
772 	 *	Release the old rights after unlocking.
773 	 */
774 
775 	for (i = 0; i < TASK_PORT_REGISTER_MAX; i++) {
776 		ipc_port_t old;
777 
778 		old = task->itk_registered[i];
779 		task->itk_registered[i] = ports[i];
780 		ports[i] = old;
781 	}
782 
783 	itk_unlock(task);
784 
785 	for (i = 0; i < TASK_PORT_REGISTER_MAX; i++)
786 		if (IP_VALID(ports[i]))
787 			ipc_port_release_send(ports[i]);
788 
789 	/*
790 	 *	Now that the operation is known to be successful,
791 	 *	we can free the memory.
792 	 */
793 
794 	if (portsCnt != 0)
795 		kfree((vm_offset_t) memory,
796 		      (vm_size_t) (portsCnt * sizeof(mach_port_t)));
797 
798 	return KERN_SUCCESS;
799 }
800 
801 /*
802  *	Routine:	mach_ports_lookup [kernel call]
803  *	Purpose:
804  *		Retrieves (clones) the stashed port send rights.
805  *	Conditions:
806  *		Nothing locked.  If successful, the caller gets
807  *		rights and memory.
808  *	Returns:
809  *		KERN_SUCCESS		Retrieved the send rights.
810  *		KERN_INVALID_ARGUMENT	The task is null.
811  *		KERN_INVALID_ARGUMENT	The task is dead.
812  *		KERN_RESOURCE_SHORTAGE	Couldn't allocate memory.
813  */
814 
815 kern_return_t
mach_ports_lookup(task_t task,mach_port_array_t * portsp,mach_msg_type_number_t * portsCnt)816 mach_ports_lookup(
817 	task_t			task,
818 	mach_port_array_t	*portsp,
819 	mach_msg_type_number_t	*portsCnt)
820 {
821 	vm_offset_t memory;
822 	vm_size_t size;
823 	ipc_port_t *ports;
824 	int i;
825 
826 	if (task == TASK_NULL)
827 		return KERN_INVALID_ARGUMENT;
828 
829 	size = (vm_size_t) (TASK_PORT_REGISTER_MAX * sizeof(ipc_port_t));
830 
831 	memory = KALLOC(size, rt);
832 	if (memory == 0)
833 		return KERN_RESOURCE_SHORTAGE;
834 
835 	itk_lock(task);
836 	if (task->itk_self == IP_NULL) {
837 		itk_unlock(task);
838 
839 		KFREE(memory, size, rt);
840 		return KERN_INVALID_ARGUMENT;
841 	}
842 
843 	ports = (ipc_port_t *) memory;
844 
845 	/*
846 	 *	Clone port rights.  Because kalloc'd memory
847 	 *	is wired, we won't fault while holding the task lock.
848 	 */
849 
850 	for (i = 0; i < TASK_PORT_REGISTER_MAX; i++)
851 		ports[i] = ipc_port_copy_send(task->itk_registered[i]);
852 
853 	itk_unlock(task);
854 
855 	*portsp = (mach_port_array_t) ports;
856 	*portsCnt = TASK_PORT_REGISTER_MAX;
857 	return KERN_SUCCESS;
858 }
859 
860 /*
861  *	Routine:	convert_port_to_task
862  *	Purpose:
863  *		Convert from a port to a task.
864  *		Doesn't consume the port ref; produces a task ref,
865  *		which may be null.
866  *	Conditions:
867  *		Nothing locked.
868  */
869 
870 task_t
convert_port_to_task(ipc_port_t port)871 convert_port_to_task(
872 	ipc_port_t	port)
873 {
874 	boolean_t r;
875 	task_t task = TASK_NULL;
876 
877 	/* currently only handle current task */
878 	return (current_task());
879 
880 	r = FALSE;
881 	while (!r && IP_VALID(port)) {
882 		ip_lock(port);
883 		r = ref_task_port_locked(port, &task);
884 		/* port unlocked */
885 	}
886 
887 	return task;
888 }
889 
890 boolean_t
ref_task_port_locked(ipc_port_t port,task_t * ptask)891 ref_task_port_locked( ipc_port_t port, task_t *ptask )
892 {
893 	task_t task;
894 
895 	task = TASK_NULL;
896 	if (ip_active(port) &&
897 		(ip_kotype(port) == IKOT_TASK)) {
898 		task = (task_t) port->ip_kobject;
899 		assert(task != TASK_NULL);
900 
901 		/*
902 		 * Normal lock ordering puts task_lock() before ip_lock().
903 		 * Allow out-of-order locking here, inlining
904 		 * task_reference() to accomodate it.
905 		 */
906 		if (!task_lock_try(task)) {
907 			ip_unlock(port);
908 			return (FALSE);
909 		}
910 		task->ref_count++;
911 		task_unlock(task);
912 	}
913 	*ptask = task;
914 	ip_unlock(port);
915 	return (TRUE);
916 }
917 
918 /*
919  *	Routine:	convert_port_to_space
920  *	Purpose:
921  *		Convert from a port to a space.
922  *		Doesn't consume the port ref; produces a space ref,
923  *		which may be null.
924  *	Conditions:
925  *		Nothing locked.
926  */
927 
928 ipc_space_t
convert_port_to_space(ipc_port_t port)929 convert_port_to_space(
930 	ipc_port_t	port)
931 {
932 	boolean_t r;
933 	ipc_space_t space = IS_NULL;
934 
935 	r = FALSE;
936 	while (!r && IP_VALID(port)) {
937 		ip_lock(port);
938 		r = ref_space_port_locked(port, &space);
939 		/* port unlocked */
940 	}
941 	return space;
942 }
943 
944 boolean_t
ref_space_port_locked(ipc_port_t port,ipc_space_t * pspace)945 ref_space_port_locked( ipc_port_t port, ipc_space_t *pspace )
946 {
947 	ipc_space_t space;
948 
949 	space = IS_NULL;
950 	if (ip_active(port) &&
951 		(ip_kotype(port) == IKOT_TASK)) {
952 		space = ((task_t) port->ip_kobject)->itk_space;
953 
954 		/*
955 		 * Normal lock ordering puts ipc_space lock before
956 		 * ip_lock(). Allow out-of-order locking here, inlining
957 		 * is_reference() to accomodate it.
958 		 */
959 		if (!mtx_trylock(&space->is_ref_lock_data)) {
960 			ip_unlock(port);
961 			return (FALSE);
962 		}
963 		space->is_references++;
964 		mtx_unlock(&space->is_ref_lock_data);
965 	}
966 	*pspace = space;
967 	ip_unlock(port);
968 	return (TRUE);
969 }
970 
971 /*
972  *	Routine:	convert_port_to_map
973  *	Purpose:
974  *		Convert from a port to a map.
975  *		Doesn't consume the port ref; produces a map ref,
976  *		which may be null.
977  *	Conditions:
978  *		Nothing locked.
979  */
980 
981 vm_map_t
convert_port_to_map(ipc_port_t port)982 convert_port_to_map(
983 	ipc_port_t	port)
984 {
985 	task_t task;
986 	vm_map_t map;
987 
988 	task = convert_port_to_task(port);
989 
990 	if (task == TASK_NULL)
991 		return VM_MAP_NULL;
992 
993 	map = &task->itk_p->p_vmspace->vm_map;
994 	task_deallocate(task);
995 	return (map);
996 }
997 
998 static boolean_t
ref_act_port_locked(ipc_port_t port,thread_act_t * pthr_act)999 ref_act_port_locked( ipc_port_t port, thread_act_t *pthr_act )
1000 {
1001 	thread_act_t thr_act;
1002 
1003 	thr_act = 0;
1004 	if (ip_active(port) &&
1005 		(ip_kotype(port) == IKOT_ACT)) {
1006 		thr_act = (thread_act_t) port->ip_kobject;
1007 		assert(thr_act != THR_ACT_NULL);
1008 
1009 		/*
1010 		 * Normal lock ordering is act_lock(), then ip_lock().
1011 		 * Allow out-of-order locking here, using
1012 		 * act_reference_act_locked() to accomodate it.
1013 		 */
1014 		if (!act_lock_try(thr_act)) {
1015 			ip_unlock(port);
1016 			return (FALSE);
1017 		}
1018 		act_locked_act_reference(thr_act);
1019 		act_unlock(thr_act);
1020 	}
1021 	*pthr_act = thr_act;
1022 	ip_unlock(port);
1023 	return (TRUE);
1024 }
1025 
1026 /*
1027  *	Routine:	convert_port_to_act
1028  *	Purpose:
1029  *		Convert from a port to a thr_act.
1030  *		Doesn't consume the port ref; produces an thr_act ref,
1031  *		which may be null.
1032  *	Conditions:
1033  *		Nothing locked.
1034  */
1035 
1036 thread_act_t
convert_port_to_thread(ipc_port_t port)1037 convert_port_to_thread( ipc_port_t port )
1038 {
1039 	boolean_t r;
1040 	thread_act_t thr_act = 0;
1041 
1042 	r = FALSE;
1043 	while (!r && IP_VALID(port)) {
1044 		ip_lock(port);
1045 		r = ref_act_port_locked(port, &thr_act);
1046 		/* port unlocked */
1047 	}
1048 	return (thr_act);
1049 }
1050 
1051 /*
1052  *	Routine:	convert_task_to_port
1053  *	Purpose:
1054  *		Convert from a task to a port.
1055  *		Consumes a task ref; produces a naked send right
1056  *		which may be invalid.
1057  *	Conditions:
1058  *		Nothing locked.
1059  */
1060 
1061 ipc_port_t
convert_task_to_port(task_t task)1062 convert_task_to_port(
1063 	task_t		task)
1064 {
1065 	ipc_port_t port;
1066 
1067 	itk_lock(task);
1068 	if (task->itk_self != IP_NULL)
1069 		port = ipc_port_make_send(task->itk_self);
1070 	else
1071 		port = IP_NULL;
1072 	itk_unlock(task);
1073 
1074 	task_deallocate(task);
1075 	return port;
1076 }
1077 
1078 /*
1079  *	Routine:	convert_act_to_port
1080  *	Purpose:
1081  *		Convert from a thr_act to a port.
1082  *		Consumes an thr_act ref; produces a naked send right
1083  *		which may be invalid.
1084  *	Conditions:
1085  *		Nothing locked.
1086  */
1087 
1088 ipc_port_t
convert_thread_to_port(thread_t thr_act)1089 convert_thread_to_port(thread_t thr_act)
1090 {
1091 	ipc_port_t port;
1092 
1093 	act_lock(thr_act);
1094 	if (thr_act->ith_self != IP_NULL)
1095 		port = ipc_port_make_send(thr_act->ith_self);
1096 	else
1097 		port = IP_NULL;
1098 	act_unlock(thr_act);
1099 
1100 	act_deallocate(thr_act);
1101 	return port;
1102 }
1103 
1104 /*
1105  *	Routine:	space_deallocate
1106  *	Purpose:
1107  *		Deallocate a space ref produced by convert_port_to_space.
1108  *	Conditions:
1109  *		Nothing locked.
1110  */
1111 
1112 void
space_deallocate(ipc_space_t space)1113 space_deallocate(
1114 	ipc_space_t	space)
1115 {
1116 	if (space != IS_NULL)
1117 		is_release(space);
1118 }
1119 
1120 /*
1121  *	Routine:	host/task_set_exception_ports [kernel call]
1122  *	Purpose:
1123  *			Sets the host/task exception port, flavor and
1124  *			behavior for the exception types specified by the mask.
1125  *			There will be one send right per exception per valid
1126  *			port.
1127  *	Conditions:
1128  *		Nothing locked.  If successful, consumes
1129  *		the supplied send right.
1130  *	Returns:
1131  *		KERN_SUCCESS		Changed the special port.
1132  *		KERN_INVALID_ARGUMENT	The thread is null,
1133  *					Illegal mask bit set.
1134  *					Illegal exception behavior
1135  *		KERN_FAILURE		The thread is dead.
1136  */
1137 
1138 kern_return_t
host_set_exception_ports(host_t host,exception_mask_t exception_mask,ipc_port_t new_port,exception_behavior_t new_behavior,thread_state_flavor_t new_flavor)1139 host_set_exception_ports(
1140 	host_t		 	host,
1141 	exception_mask_t		exception_mask,
1142 	ipc_port_t			new_port,
1143 	exception_behavior_t		new_behavior,
1144 	thread_state_flavor_t		new_flavor)
1145 {
1146 	register int	i;
1147 	ipc_port_t	old_port[EXC_TYPES_COUNT];
1148 
1149 	if (!host)
1150 		return KERN_INVALID_ARGUMENT;
1151 
1152 	if (exception_mask & ~EXC_MASK_ALL)
1153 		return KERN_INVALID_ARGUMENT;
1154 
1155 	if (IP_VALID(new_port)) {
1156 		switch (new_behavior) {
1157 		case EXCEPTION_DEFAULT:
1158 		case EXCEPTION_STATE:
1159 		case EXCEPTION_STATE_IDENTITY:
1160 			break;
1161 		default:
1162 			return KERN_INVALID_ARGUMENT;
1163 		}
1164 	}
1165 	/* Cannot easily check "flavor", but that just means that the flavor
1166 	 * in the generated exception message might be garbage. GIGO */
1167 
1168 	host_lock(host);
1169 
1170 	for (i = FIRST_EXCEPTION; i < EXC_TYPES_COUNT; i++) {
1171 		if (exception_mask & (1 << i)) {
1172 			old_port[i] = host->exc_actions[i].port;
1173 			host->exc_actions[i].port =
1174 				ipc_port_copy_send(new_port);
1175 			host->exc_actions[i].behavior = new_behavior;
1176 			host->exc_actions[i].flavor = new_flavor;
1177 		} else
1178 			old_port[i] = IP_NULL;
1179 	}/* for */
1180 	/*
1181 	 * Consume send rights without any lock held.
1182 	 */
1183 	host_unlock(host);
1184 	for (i = FIRST_EXCEPTION; i < EXC_TYPES_COUNT; i++)
1185 		if (IP_VALID(old_port[i]))
1186 			ipc_port_release_send(old_port[i]);
1187 	if (IP_VALID(new_port))		 /* consume send right */
1188 		ipc_port_release_send(new_port);
1189 
1190         return KERN_SUCCESS;
1191 }/* thread_set_exception_port */
1192 
1193 kern_return_t
task_set_exception_ports(task_t task,exception_mask_t exception_mask,ipc_port_t new_port,exception_behavior_t new_behavior,thread_state_flavor_t new_flavor)1194 task_set_exception_ports(
1195 	task_t				task,
1196 	exception_mask_t		exception_mask,
1197 	ipc_port_t			new_port,
1198 	exception_behavior_t		new_behavior,
1199 	thread_state_flavor_t		new_flavor)
1200 {
1201 	register int	i;
1202 	ipc_port_t	old_port[EXC_TYPES_COUNT];
1203 
1204 	if (task == TASK_NULL) {
1205 		return KERN_INVALID_ARGUMENT;
1206 	}
1207 
1208 	if (exception_mask & ~EXC_MASK_ALL) {
1209 		return KERN_INVALID_ARGUMENT;
1210 	}
1211 
1212 	if (IP_VALID(new_port)) {
1213 		switch (new_behavior) {
1214 		case EXCEPTION_DEFAULT:
1215 		case EXCEPTION_STATE:
1216 		case EXCEPTION_STATE_IDENTITY:
1217 			break;
1218 		default:
1219 			return KERN_INVALID_ARGUMENT;
1220 		}
1221 	}
1222 	/* Cannot easily check "new_flavor", but that just means that
1223 	 * the flavor in the generated exception message might be garbage:
1224 	 * GIGO */
1225 
1226         itk_lock(task);
1227         if (task->itk_self == IP_NULL) {
1228                 itk_unlock(task);
1229                 return KERN_FAILURE;
1230         }
1231 
1232 	for (i = FIRST_EXCEPTION; i < EXC_TYPES_COUNT; i++) {
1233 		if (exception_mask & (1 << i)) {
1234 			old_port[i] = task->exc_actions[i].port;
1235 			task->exc_actions[i].port =
1236 				ipc_port_copy_send(new_port);
1237 			task->exc_actions[i].behavior = new_behavior;
1238 			task->exc_actions[i].flavor = new_flavor;
1239 		} else
1240 			old_port[i] = IP_NULL;
1241 	}/* for */
1242 
1243 	/*
1244 	 * Consume send rights without any lock held.
1245 	 */
1246         itk_unlock(task);
1247 	for (i = FIRST_EXCEPTION; i < EXC_TYPES_COUNT; i++)
1248 		if (IP_VALID(old_port[i]))
1249 			ipc_port_release_send(old_port[i]);
1250 	if (IP_VALID(new_port))		 /* consume send right */
1251 		ipc_port_release_send(new_port);
1252 
1253         return KERN_SUCCESS;
1254 }/* task_set_exception_port */
1255 
1256 /*
1257  *	Routine:	host/task_swap_exception_ports [kernel call]
1258  *	Purpose:
1259  *			Sets the thread/task exception port, flavor and
1260  *			behavior for the exception types specified by the
1261  *			mask.
1262  *
1263  *			The old ports, behavior and flavors are returned
1264  *			Count specifies the array sizes on input and
1265  *			the number of returned ports etc. on output.  The
1266  *			arrays must be large enough to hold all the returned
1267  *			data, MIG returnes an error otherwise.  The masks
1268  *			array specifies the corresponding exception type(s).
1269  *
1270  *	Conditions:
1271  *		Nothing locked.  If successful, consumes
1272  *		the supplied send right.
1273  *
1274  *		Returns upto [in} CountCnt elements.
1275  *	Returns:
1276  *		KERN_SUCCESS		Changed the special port.
1277  *		KERN_INVALID_ARGUMENT	The thread is null,
1278  *					Illegal mask bit set.
1279  *					Illegal exception behavior
1280  *		KERN_FAILURE		The thread is dead.
1281  */
1282 
1283 kern_return_t
host_swap_exception_ports(host_t host,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)1284 host_swap_exception_ports(
1285 	host_t			host,
1286 	exception_mask_t		exception_mask,
1287 	ipc_port_t			new_port,
1288 	exception_behavior_t		new_behavior,
1289 	thread_state_flavor_t		new_flavor,
1290 	exception_mask_array_t		masks,
1291 	mach_msg_type_number_t		* CountCnt,
1292 	exception_port_array_t		ports,
1293 	exception_behavior_array_t      behaviors,
1294 	thread_state_flavor_array_t     flavors	)
1295 {
1296 	register int	i,
1297 			j,
1298 			count;
1299 	ipc_port_t	old_port[EXC_TYPES_COUNT];
1300 
1301 	if (!host)
1302 		return KERN_INVALID_ARGUMENT;
1303 
1304 	if (exception_mask & ~EXC_MASK_ALL) {
1305 		return KERN_INVALID_ARGUMENT;
1306 	}
1307 
1308 	if (IP_VALID(new_port)) {
1309 		switch (new_behavior) {
1310 		case EXCEPTION_DEFAULT:
1311 		case EXCEPTION_STATE:
1312 		case EXCEPTION_STATE_IDENTITY:
1313 			break;
1314 		default:
1315 			return KERN_INVALID_ARGUMENT;
1316 		}
1317 	}
1318 	/* Cannot easily check "new_flavor", but that just means that
1319 	 * the flavor in the generated exception message might be garbage:
1320 	 * GIGO */
1321 
1322 	host_lock(host);
1323 	for (count = 0, i = FIRST_EXCEPTION; i < EXC_TYPES_COUNT; i++) {
1324 		if (exception_mask & (1 << i)) {
1325 			for (j = 0; j < count; j++) {
1326 /*
1327  *				search for an identical entry, if found
1328  *				set corresponding mask for this exception.
1329  */
1330 				if (host->exc_actions[i].port == ports[j] &&
1331 				  host->exc_actions[i].behavior ==behaviors[j]
1332 				  && host->exc_actions[i].flavor ==flavors[j])
1333 				{
1334 					masks[j] |= (1 << i);
1335 					break;
1336 				}
1337 			}/* for */
1338 			if (j == count) {
1339 				masks[j] = (1 << i);
1340 				ports[j] =
1341 				ipc_port_copy_send(host->exc_actions[i].port);
1342 
1343 				behaviors[j] = host->exc_actions[i].behavior;
1344 				flavors[j] = host->exc_actions[i].flavor;
1345 				count++;
1346 			}
1347 
1348 			old_port[i] = host->exc_actions[i].port;
1349 			host->exc_actions[i].port =
1350 				ipc_port_copy_send(new_port);
1351 			host->exc_actions[i].behavior = new_behavior;
1352 			host->exc_actions[i].flavor = new_flavor;
1353 			if (count > *CountCnt) {
1354 				break;
1355 			}
1356 		} else
1357 			old_port[i] = IP_NULL;
1358 	}/* for */
1359 
1360 	/*
1361 	 * Consume send rights without any lock held.
1362 	 */
1363 	host_unlock(host);
1364 	for (i = FIRST_EXCEPTION; i < EXC_TYPES_COUNT; i++)
1365 		if (IP_VALID(old_port[i]))
1366 			ipc_port_release_send(old_port[i]);
1367 	if (IP_VALID(new_port))		 /* consume send right */
1368 		ipc_port_release_send(new_port);
1369 	*CountCnt = count;
1370 	return KERN_SUCCESS;
1371 }/* thread_swap_exception_ports */
1372 
1373 kern_return_t
task_swap_exception_ports(task_t task,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)1374 task_swap_exception_ports(
1375 	task_t				task,
1376 	exception_mask_t		exception_mask,
1377 	ipc_port_t			new_port,
1378 	exception_behavior_t		new_behavior,
1379 	thread_state_flavor_t		new_flavor,
1380 	exception_mask_array_t		masks,
1381 	mach_msg_type_number_t		* CountCnt,
1382 	exception_port_array_t		ports,
1383 	exception_behavior_array_t      behaviors,
1384 	thread_state_flavor_array_t     flavors		)
1385 {
1386 	register int	i,
1387 			j,
1388 			count;
1389 	ipc_port_t	old_port[EXC_TYPES_COUNT];
1390 
1391 	if (task == TASK_NULL)
1392 		return KERN_INVALID_ARGUMENT;
1393 
1394 	if (exception_mask & ~EXC_MASK_ALL) {
1395 		return KERN_INVALID_ARGUMENT;
1396 	}
1397 
1398 	if (IP_VALID(new_port)) {
1399 		switch (new_behavior) {
1400 		case EXCEPTION_DEFAULT:
1401 		case EXCEPTION_STATE:
1402 		case EXCEPTION_STATE_IDENTITY:
1403 			break;
1404 		default:
1405 			return KERN_INVALID_ARGUMENT;
1406 		}
1407 	}
1408 	/* Cannot easily check "new_flavor", but that just means that
1409 	 * the flavor in the generated exception message might be garbage:
1410 	 * GIGO */
1411 
1412 	itk_lock(task);
1413 	if (task->itk_self == IP_NULL) {
1414 		itk_unlock(task);
1415 		return KERN_FAILURE;
1416 	}
1417 
1418 	count = 0;
1419 
1420 	for (i = FIRST_EXCEPTION; i < EXC_TYPES_COUNT; i++) {
1421 		if (exception_mask & (1 << i)) {
1422 			for (j = 0; j < count; j++) {
1423 /*
1424  *				search for an identical entry, if found
1425  *				set corresponding mask for this exception.
1426  */
1427 				if (task->exc_actions[i].port == ports[j] &&
1428 				  task->exc_actions[i].behavior == behaviors[j]
1429 				  && task->exc_actions[i].flavor == flavors[j])
1430 				{
1431 					masks[j] |= (1 << i);
1432 					break;
1433 				}
1434 			}/* for */
1435 			if (j == count) {
1436 				masks[j] = (1 << i);
1437 				ports[j] =
1438 				ipc_port_copy_send(task->exc_actions[i].port);
1439 				behaviors[j] = task->exc_actions[i].behavior;
1440 				flavors[j] = task->exc_actions[i].flavor;
1441 				count++;
1442 			}
1443 			old_port[i] = task->exc_actions[i].port;
1444 			task->exc_actions[i].port =
1445 				ipc_port_copy_send(new_port);
1446 			task->exc_actions[i].behavior = new_behavior;
1447 			task->exc_actions[i].flavor = new_flavor;
1448 			if (count > *CountCnt) {
1449 				break;
1450 			}
1451 		} else
1452 			old_port[i] = IP_NULL;
1453 	}/* for */
1454 
1455 
1456 	/*
1457 	 * Consume send rights without any lock held.
1458 	 */
1459 	itk_unlock(task);
1460 	for (i = FIRST_EXCEPTION; i < EXC_TYPES_COUNT; i++)
1461 		if (IP_VALID(old_port[i]))
1462 			ipc_port_release_send(old_port[i]);
1463 	if (IP_VALID(new_port))		 /* consume send right */
1464 		ipc_port_release_send(new_port);
1465 	*CountCnt = count;
1466 
1467 	return KERN_SUCCESS;
1468 }/* task_swap_exception_ports */
1469 
1470 
1471 /*
1472  *	Routine:	host/task_get_exception_ports [kernel call]
1473  *	Purpose:
1474  *		Clones a send right for each of the thread/task's exception
1475  *		ports specified in the mask and returns the behaviour
1476  *		and flavor of said port.
1477  *
1478  *		Returns upto [in} CountCnt elements.
1479  *
1480  *	Conditions:
1481  *		Nothing locked.
1482  *	Returns:
1483  *		KERN_SUCCESS		Extracted a send right.
1484  *		KERN_INVALID_ARGUMENT	The thread is null,
1485  *					Invalid special port,
1486  *					Illegal mask bit set.
1487  *		KERN_FAILURE		The thread is dead.
1488  */
1489 
1490 kern_return_t
host_get_exception_ports(host_t host,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)1491 host_get_exception_ports(
1492 	host_t			host,
1493 	exception_mask_t                exception_mask,
1494 	exception_mask_array_t		masks,
1495 	mach_msg_type_number_t		* CountCnt,
1496 	exception_port_array_t		ports,
1497 	exception_behavior_array_t      behaviors,
1498 	thread_state_flavor_array_t     flavors		)
1499 {
1500 	register int	i,
1501 			j,
1502 			count;
1503 
1504 	if (!host)
1505 		return KERN_INVALID_ARGUMENT;
1506 
1507 	if (exception_mask & ~EXC_MASK_ALL) {
1508 		return KERN_INVALID_ARGUMENT;
1509 	}
1510 
1511 	host_lock(host);
1512 	for (count = 0, i = FIRST_EXCEPTION; i < EXC_TYPES_COUNT; i++) {
1513 		if (exception_mask & (1 << i)) {
1514 			for (j = 0; j < count; j++) {
1515 /*
1516  *				search for an identical entry, if found
1517  *				set corresponding mask for this exception.
1518  */
1519 				if (host->exc_actions[i].port == ports[j] &&
1520 				  host->exc_actions[i].behavior ==behaviors[j]
1521 				  && host->exc_actions[i].flavor == flavors[j])
1522 				{
1523 					masks[j] |= (1 << i);
1524 					break;
1525 				}
1526 			}/* for */
1527 			if (j == count) {
1528 				masks[j] = (1 << i);
1529 				ports[j] =
1530 				ipc_port_copy_send(host->exc_actions[i].port);
1531 				behaviors[j] = host->exc_actions[i].behavior;
1532 				flavors[j] = host->exc_actions[i].flavor;
1533 				count++;
1534 				if (count >= *CountCnt) {
1535 					break;
1536 				}
1537 			}
1538 		}
1539 	}/* for */
1540 
1541 	host_unlock(host);
1542 
1543 	*CountCnt = count;
1544 	return KERN_SUCCESS;
1545 }/* thread_get_exception_ports */
1546 
1547 kern_return_t
task_get_exception_ports(task_t task,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)1548 task_get_exception_ports(
1549 	task_t				task,
1550 	exception_mask_t                exception_mask,
1551 	exception_mask_array_t		masks,
1552 	mach_msg_type_number_t		* CountCnt,
1553 	exception_port_array_t		ports,
1554 	exception_behavior_array_t      behaviors,
1555 	thread_state_flavor_array_t     flavors		)
1556 {
1557 	register int	i,
1558 			j,
1559 			count;
1560 
1561 	if (task == TASK_NULL)
1562 		return KERN_INVALID_ARGUMENT;
1563 
1564 	if (exception_mask & ~EXC_MASK_ALL) {
1565 		return KERN_INVALID_ARGUMENT;
1566 	}
1567 
1568 	itk_lock(task);
1569 	if (task->itk_self == IP_NULL) {
1570 		itk_unlock(task);
1571 		return KERN_FAILURE;
1572 	}
1573 
1574 	count = 0;
1575 
1576 	for (i = FIRST_EXCEPTION; i < EXC_TYPES_COUNT; i++) {
1577 		if (exception_mask & (1 << i)) {
1578 			for (j = 0; j < count; j++) {
1579 /*
1580  *				search for an identical entry, if found
1581  *				set corresponding mask for this exception.
1582  */
1583 				if (task->exc_actions[i].port == ports[j] &&
1584 				  task->exc_actions[i].behavior == behaviors[j]
1585 				  && task->exc_actions[i].flavor == flavors[j])
1586 				{
1587 					masks[j] |= (1 << i);
1588 					break;
1589 				}
1590 			}/* for */
1591 			if (j == count) {
1592 				masks[j] = (1 << i);
1593 				ports[j] =
1594 				  ipc_port_copy_send(task->exc_actions[i].port);
1595 				behaviors[j] = task->exc_actions[i].behavior;
1596 				flavors[j] = task->exc_actions[i].flavor;
1597 				count++;
1598 				if (count > *CountCnt) {
1599 					break;
1600 				}
1601 			}
1602 		}
1603 	}/* for */
1604 
1605 	itk_unlock(task);
1606 
1607 	*CountCnt = count;
1608 	return KERN_SUCCESS;
1609 }/* task_get_exception_ports */
1610