xref: /xnu-8020.101.4/osfmk/kern/exception.c (revision e7776783b89a353188416a9a346c6cdb4928faad)
1 /*
2  * Copyright (c) 2000-2020 Apple Computer, 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,1987 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 #include <mach/mach_types.h>
60 #include <mach/boolean.h>
61 #include <mach/kern_return.h>
62 #include <mach/message.h>
63 #include <mach/port.h>
64 #include <mach/mig_errors.h>
65 #include <mach/task.h>
66 #include <mach/thread_status.h>
67 #include <mach/exception_types.h>
68 #include <mach/exc.h>
69 #include <mach/mach_exc.h>
70 
71 #include <ipc/port.h>
72 #include <ipc/ipc_entry.h>
73 #include <ipc/ipc_object.h>
74 #include <ipc/ipc_notify.h>
75 #include <ipc/ipc_space.h>
76 #include <ipc/ipc_pset.h>
77 #include <ipc/ipc_machdep.h>
78 
79 #include <kern/ipc_tt.h>
80 #include <kern/task.h>
81 #include <kern/thread.h>
82 #include <kern/processor.h>
83 #include <kern/sched.h>
84 #include <kern/sched_prim.h>
85 #include <kern/host.h>
86 #include <kern/misc_protos.h>
87 #include <kern/ux_handler.h>
88 #include <kern/task_ident.h>
89 
90 #include <vm/vm_map.h>
91 
92 #include <security/mac_mach_internal.h>
93 #include <string.h>
94 
95 #include <pexpert/pexpert.h>
96 
97 #include <os/log.h>
98 
99 #include <libkern/coreanalytics/coreanalytics.h>
100 
101 bool panic_on_exception_triage = false;
102 
103 unsigned long c_thr_exc_raise = 0;
104 unsigned long c_thr_exc_raise_identity_token = 0;
105 unsigned long c_thr_exc_raise_state = 0;
106 unsigned long c_thr_exc_raise_state_id = 0;
107 
108 /* forward declarations */
109 kern_return_t exception_deliver(
110 	thread_t                thread,
111 	exception_type_t        exception,
112 	mach_exception_data_t   code,
113 	mach_msg_type_number_t  codeCnt,
114 	struct exception_action *excp,
115 	lck_mtx_t                       *mutex);
116 
117 #ifdef MACH_BSD
118 kern_return_t bsd_exception(
119 	exception_type_t        exception,
120 	mach_exception_data_t   code,
121 	mach_msg_type_number_t  codeCnt);
122 #endif /* MACH_BSD */
123 
124 #if __has_feature(ptrauth_calls)
125 extern int exit_with_pac_exception(
126 	void *proc,
127 	exception_type_t         exception,
128 	mach_exception_code_t    code,
129 	mach_exception_subcode_t subcode);
130 #endif /* __has_feature(ptrauth_calls) */
131 
132 #ifdef MACH_BSD
133 extern bool proc_is_traced(void *p);
134 extern int      proc_selfpid(void);
135 extern char     *proc_name_address(struct proc *p);
136 #endif /* MACH_BSD */
137 
138 #if (DEVELOPMENT || DEBUG)
139 TUNABLE_WRITEABLE(unsigned int, exception_log_max_pid, "exception_log_max_pid", 0);
140 #endif /* (DEVELOPMENT || DEBUG) */
141 
142 /*
143  * Routine: exception_init
144  * Purpose:
145  *   Global initialization of state for exceptions.
146  * Conditions:
147  *   None.
148  */
149 void
exception_init(void)150 exception_init(void)
151 {
152 	int tmp = 0;
153 
154 	if (PE_parse_boot_argn("-panic_on_exception_triage", &tmp, sizeof(tmp))) {
155 		panic_on_exception_triage = true;
156 	}
157 
158 #if (DEVELOPMENT || DEBUG)
159 	if (exception_log_max_pid) {
160 		printf("Logging all exceptions where pid < exception_log_max_pid (%d)\n", exception_log_max_pid);
161 	}
162 #endif /* (DEVELOPMENT || DEBUG) */
163 }
164 
165 static TUNABLE(bool, pac_replace_ptrs_user, "-pac_replace_ptrs_user", false);
166 static TUNABLE(bool, pac_replace_ts_user, "-pac_replace_ts_user", false);
167 
168 /*
169  *	Routine:	exception_deliver
170  *	Purpose:
171  *		Make an upcall to the exception server provided.
172  *	Conditions:
173  *		Nothing locked and no resources held.
174  *		Called from an exception context, so
175  *		thread_exception_return and thread_kdb_return
176  *		are possible.
177  *	Returns:
178  *		KERN_SUCCESS if the exception was handled
179  */
180 kern_return_t
exception_deliver(thread_t thread,exception_type_t exception,mach_exception_data_t code,mach_msg_type_number_t codeCnt,struct exception_action * excp,lck_mtx_t * mutex)181 exception_deliver(
182 	thread_t                thread,
183 	exception_type_t        exception,
184 	mach_exception_data_t   code,
185 	mach_msg_type_number_t  codeCnt,
186 	struct exception_action *excp,
187 	lck_mtx_t                       *mutex)
188 {
189 	ipc_port_t              exc_port = IPC_PORT_NULL;
190 	exception_data_type_t   small_code[EXCEPTION_CODE_MAX];
191 	thread_state_t          new_state = NULL;
192 	int                     code64;
193 	int                     behavior;
194 	int                     flavor;
195 	kern_return_t           kr;
196 	task_t task;
197 	task_id_token_t task_token;
198 	ipc_port_t thread_port = IPC_PORT_NULL,
199 	    task_port = IPC_PORT_NULL,
200 	    task_token_port = IPC_PORT_NULL;
201 
202 	/*
203 	 *  Save work if we are terminating.
204 	 *  Just go back to our AST handler.
205 	 */
206 	if (!thread->active && !thread->inspection) {
207 		return KERN_SUCCESS;
208 	}
209 
210 	/*
211 	 * If there are no exception actions defined for this entity,
212 	 * we can't deliver here.
213 	 */
214 	if (excp == NULL) {
215 		return KERN_FAILURE;
216 	}
217 
218 	assert(exception < EXC_TYPES_COUNT);
219 	if (exception >= EXC_TYPES_COUNT) {
220 		return KERN_FAILURE;
221 	}
222 
223 	excp = &excp[exception];
224 
225 	/*
226 	 * Snapshot the exception action data under lock for consistency.
227 	 * Hold a reference to the port over the exception_raise_* calls
228 	 * so it can't be destroyed.  This seems like overkill, but keeps
229 	 * the port from disappearing between now and when
230 	 * ipc_object_copyin_from_kernel is finally called.
231 	 */
232 	lck_mtx_lock(mutex);
233 	exc_port = excp->port;
234 	if (!IP_VALID(exc_port)) {
235 		lck_mtx_unlock(mutex);
236 		return KERN_FAILURE;
237 	}
238 	ip_mq_lock(exc_port);
239 	if (!ip_active(exc_port)) {
240 		ip_mq_unlock(exc_port);
241 		lck_mtx_unlock(mutex);
242 		return KERN_FAILURE;
243 	}
244 	ip_reference(exc_port);
245 	exc_port->ip_srights++;
246 	ip_mq_unlock(exc_port);
247 
248 	flavor = excp->flavor;
249 	behavior = excp->behavior;
250 	lck_mtx_unlock(mutex);
251 
252 	code64 = (behavior & MACH_EXCEPTION_CODES);
253 	behavior &= ~MACH_EXCEPTION_MASK;
254 
255 	if (!code64) {
256 		small_code[0] = CAST_DOWN_EXPLICIT(exception_data_type_t, code[0]);
257 		small_code[1] = CAST_DOWN_EXPLICIT(exception_data_type_t, code[1]);
258 	}
259 
260 	task = get_threadtask(thread);
261 
262 #if CONFIG_MACF
263 	/* Now is a reasonably good time to check if the exception action is
264 	 * permitted for this process, because after this point we will send
265 	 * the message out almost certainly.
266 	 * As with other failures, exception_triage_thread will go on
267 	 * to the next level.
268 	 */
269 
270 	/* The global exception-to-signal translation port is safe to be an exception handler. */
271 	if (is_ux_handler_port(exc_port) == FALSE &&
272 	    mac_exc_action_check_exception_send(task, excp) != 0) {
273 		kr = KERN_FAILURE;
274 		goto out_release_right;
275 	}
276 #endif
277 
278 	if ((behavior != EXCEPTION_STATE) && (behavior != EXCEPTION_IDENTITY_PROTECTED)) {
279 		task_reference(task);
280 		task_port = convert_task_to_port(task);
281 		/* task ref consumed */
282 		thread_reference(thread);
283 		thread_port = convert_thread_to_port(thread);
284 		/* thread ref consumed */
285 	}
286 
287 	if (behavior == EXCEPTION_IDENTITY_PROTECTED) {
288 		kr = task_create_identity_token(task, &task_token);
289 		/* task_token now represents a task, or corpse */
290 		assert(kr == KERN_SUCCESS);
291 		task_token_port = convert_task_id_token_to_port(task_token);
292 		/* task token ref consumed */
293 	}
294 
295 	switch (behavior) {
296 	case EXCEPTION_STATE: {
297 		mach_msg_type_number_t old_state_cnt, new_state_cnt;
298 		thread_state_data_t old_state;
299 		thread_set_status_flags_t flags = TSSF_CHECK_USER_FLAGS;
300 
301 		if (pac_replace_ptrs_user) {
302 			flags |= TSSF_ALLOW_ONLY_USER_PTRS;
303 		}
304 		if (pac_replace_ts_user) {
305 			flags |= TSSF_ALLOW_ONLY_USER_STATE;
306 		}
307 
308 		c_thr_exc_raise_state++;
309 		old_state_cnt = _MachineStateCount[flavor];
310 		kr = thread_getstatus_to_user(thread, flavor,
311 		    (thread_state_t)old_state,
312 		    &old_state_cnt);
313 		new_state_cnt = old_state_cnt;
314 		if (kr == KERN_SUCCESS) {
315 			new_state = (thread_state_t)kalloc_data(sizeof(thread_state_data_t), Z_WAITOK | Z_ZERO);
316 			if (new_state == NULL) {
317 				kr = KERN_RESOURCE_SHORTAGE;
318 				goto out_release_right;
319 			}
320 			if (code64) {
321 				kr = mach_exception_raise_state(exc_port,
322 				    exception,
323 				    code,
324 				    codeCnt,
325 				    &flavor,
326 				    old_state, old_state_cnt,
327 				    new_state, &new_state_cnt);
328 			} else {
329 				kr = exception_raise_state(exc_port, exception,
330 				    small_code,
331 				    codeCnt,
332 				    &flavor,
333 				    old_state, old_state_cnt,
334 				    new_state, &new_state_cnt);
335 			}
336 			if (kr == KERN_SUCCESS) {
337 				if (exception != EXC_CORPSE_NOTIFY) {
338 					kr = thread_setstatus_from_user(thread, flavor,
339 					    (thread_state_t)new_state, new_state_cnt,
340 					    (thread_state_t)old_state, old_state_cnt,
341 					    flags);
342 				}
343 				goto out_release_right;
344 			}
345 		}
346 
347 		goto out_release_right;
348 	}
349 
350 	case EXCEPTION_DEFAULT: {
351 		c_thr_exc_raise++;
352 		if (code64) {
353 			kr = mach_exception_raise(exc_port,
354 			    thread_port,
355 			    task_port,
356 			    exception,
357 			    code,
358 			    codeCnt);
359 		} else {
360 			kr = exception_raise(exc_port,
361 			    thread_port,
362 			    task_port,
363 			    exception,
364 			    small_code,
365 			    codeCnt);
366 		}
367 
368 		goto out_release_right;
369 	}
370 
371 	case EXCEPTION_IDENTITY_PROTECTED: {
372 		c_thr_exc_raise_identity_token++;
373 		if (code64) {
374 			kr = mach_exception_raise_identity_protected(exc_port,
375 			    thread->thread_id,
376 			    task_token_port,
377 			    exception,
378 			    code,
379 			    codeCnt);
380 		} else {
381 			panic("mach_exception_raise_identity_protected() must be code64");
382 		}
383 
384 		goto out_release_right;
385 	}
386 
387 	case EXCEPTION_STATE_IDENTITY: {
388 		mach_msg_type_number_t old_state_cnt, new_state_cnt;
389 		thread_state_data_t old_state;
390 		thread_set_status_flags_t flags = TSSF_CHECK_USER_FLAGS;
391 
392 		if (pac_replace_ptrs_user) {
393 			flags |= TSSF_ALLOW_ONLY_USER_PTRS;
394 		}
395 
396 		if (pac_replace_ts_user) {
397 			flags |= TSSF_ALLOW_ONLY_USER_STATE;
398 		}
399 
400 		c_thr_exc_raise_state_id++;
401 		old_state_cnt = _MachineStateCount[flavor];
402 		kr = thread_getstatus_to_user(thread, flavor,
403 		    (thread_state_t)old_state,
404 		    &old_state_cnt);
405 		new_state_cnt = old_state_cnt;
406 		if (kr == KERN_SUCCESS) {
407 			new_state = (thread_state_t)kalloc_data(sizeof(thread_state_data_t), Z_WAITOK | Z_ZERO);
408 			if (new_state == NULL) {
409 				kr = KERN_RESOURCE_SHORTAGE;
410 				goto out_release_right;
411 			}
412 			if (code64) {
413 				kr = mach_exception_raise_state_identity(
414 					exc_port,
415 					thread_port,
416 					task_port,
417 					exception,
418 					code,
419 					codeCnt,
420 					&flavor,
421 					old_state, old_state_cnt,
422 					new_state, &new_state_cnt);
423 			} else {
424 				kr = exception_raise_state_identity(exc_port,
425 				    thread_port,
426 				    task_port,
427 				    exception,
428 				    small_code,
429 				    codeCnt,
430 				    &flavor,
431 				    old_state, old_state_cnt,
432 				    new_state, &new_state_cnt);
433 			}
434 
435 			if (kr == KERN_SUCCESS) {
436 				if (exception != EXC_CORPSE_NOTIFY) {
437 					kr = thread_setstatus_from_user(thread, flavor,
438 					    (thread_state_t)new_state, new_state_cnt,
439 					    (thread_state_t)old_state, old_state_cnt, flags);
440 				}
441 				goto out_release_right;
442 			}
443 		}
444 
445 		goto out_release_right;
446 	}
447 
448 	default:
449 		panic("bad exception behavior!");
450 		return KERN_FAILURE;
451 	}/* switch */
452 
453 out_release_right:
454 
455 	if (task_port) {
456 		ipc_port_release_send(task_port);
457 	}
458 
459 	if (thread_port) {
460 		ipc_port_release_send(thread_port);
461 	}
462 
463 	if (exc_port) {
464 		ipc_port_release_send(exc_port);
465 	}
466 
467 	if (task_token_port) {
468 		ipc_port_release_send(task_token_port);
469 	}
470 
471 	if (new_state) {
472 		kfree_data(new_state, sizeof(thread_state_data_t));
473 	}
474 
475 	return kr;
476 }
477 
478 /*
479  * Routine: check_exc_receiver_dependency
480  * Purpose:
481  *      Verify that the port destined for receiving this exception is not
482  *      on the current task. This would cause hang in kernel for
483  *      EXC_CRASH primarily. Note: If port is transferred
484  *      between check and delivery then deadlock may happen.
485  *
486  * Conditions:
487  *		Nothing locked and no resources held.
488  *		Called from an exception context.
489  * Returns:
490  *      KERN_SUCCESS if its ok to send exception message.
491  */
492 static kern_return_t
check_exc_receiver_dependency(exception_type_t exception,struct exception_action * excp,lck_mtx_t * mutex)493 check_exc_receiver_dependency(
494 	exception_type_t exception,
495 	struct exception_action *excp,
496 	lck_mtx_t *mutex)
497 {
498 	kern_return_t retval = KERN_SUCCESS;
499 
500 	if (excp == NULL || exception != EXC_CRASH) {
501 		return retval;
502 	}
503 
504 	task_t task = current_task();
505 	lck_mtx_lock(mutex);
506 	ipc_port_t xport = excp[exception].port;
507 	if (IP_VALID(xport) && ip_in_space_noauth(xport, task->itk_space)) {
508 		retval = KERN_FAILURE;
509 	}
510 	lck_mtx_unlock(mutex);
511 	return retval;
512 }
513 
514 
515 /*
516  *	Routine:	exception_triage_thread
517  *	Purpose:
518  *		The thread caught an exception.
519  *		We make an up-call to the thread's exception server.
520  *	Conditions:
521  *		Nothing locked and no resources held.
522  *		Called from an exception context, so
523  *		thread_exception_return and thread_kdb_return
524  *		are possible.
525  *	Returns:
526  *		KERN_SUCCESS if exception is handled by any of the handlers.
527  */
528 kern_return_t
exception_triage_thread(exception_type_t exception,mach_exception_data_t code,mach_msg_type_number_t codeCnt,thread_t thread)529 exception_triage_thread(
530 	exception_type_t        exception,
531 	mach_exception_data_t   code,
532 	mach_msg_type_number_t  codeCnt,
533 	thread_t                thread)
534 {
535 	task_t                  task;
536 	thread_ro_t             tro;
537 	host_priv_t             host_priv;
538 	lck_mtx_t               *mutex;
539 	struct exception_action *actions;
540 	kern_return_t   kr = KERN_FAILURE;
541 
542 	assert(exception != EXC_RPC_ALERT);
543 
544 	/*
545 	 * If this behavior has been requested by the the kernel
546 	 * (due to the boot environment), we should panic if we
547 	 * enter this function.  This is intended as a debugging
548 	 * aid; it should allow us to debug why we caught an
549 	 * exception in environments where debugging is especially
550 	 * difficult.
551 	 */
552 	if (panic_on_exception_triage) {
553 		panic("called exception_triage when it was forbidden by the boot environment");
554 	}
555 
556 	/*
557 	 * Try to raise the exception at the activation level.
558 	 */
559 	mutex   = &thread->mutex;
560 	tro     = get_thread_ro(thread);
561 	actions = tro->tro_exc_actions;
562 	if (KERN_SUCCESS == check_exc_receiver_dependency(exception, actions, mutex)) {
563 		kr = exception_deliver(thread, exception, code, codeCnt, actions, mutex);
564 		if (kr == KERN_SUCCESS || kr == MACH_RCV_PORT_DIED) {
565 			goto out;
566 		}
567 	}
568 
569 	/*
570 	 * Maybe the task level will handle it.
571 	 */
572 	task    = tro->tro_task;
573 	mutex   = &task->itk_lock_data;
574 	actions = task->exc_actions;
575 	if (KERN_SUCCESS == check_exc_receiver_dependency(exception, actions, mutex)) {
576 		kr = exception_deliver(thread, exception, code, codeCnt, actions, mutex);
577 		if (kr == KERN_SUCCESS || kr == MACH_RCV_PORT_DIED) {
578 			goto out;
579 		}
580 	}
581 
582 	/*
583 	 * How about at the host level?
584 	 */
585 	host_priv = host_priv_self();
586 	mutex     = &host_priv->lock;
587 	actions   = host_priv->exc_actions;
588 	if (KERN_SUCCESS == check_exc_receiver_dependency(exception, actions, mutex)) {
589 		kr = exception_deliver(thread, exception, code, codeCnt, actions, mutex);
590 		if (kr == KERN_SUCCESS || kr == MACH_RCV_PORT_DIED) {
591 			goto out;
592 		}
593 	}
594 
595 out:
596 	if ((exception != EXC_CRASH) && (exception != EXC_RESOURCE) &&
597 	    (exception != EXC_GUARD) && (exception != EXC_CORPSE_NOTIFY)) {
598 		thread_exception_return();
599 	}
600 	return kr;
601 }
602 
603 #if __has_feature(ptrauth_calls)
604 
605 CA_EVENT(pac_exception_event,
606     CA_INT, exception,
607     CA_INT, exception_code_0,
608     CA_INT, exception_code_1,
609     CA_STATIC_STRING(CA_PROCNAME_LEN), proc_name);
610 
611 static void
pac_exception_triage(exception_type_t exception,mach_exception_data_t code)612 pac_exception_triage(
613 	exception_type_t        exception,
614 	mach_exception_data_t   code)
615 {
616 	boolean_t traced_flag = FALSE;
617 	task_t task = current_task();
618 	void *proc = task->bsd_info;
619 	char *proc_name = (char *) "unknown";
620 	int pid = 0;
621 
622 #ifdef MACH_BSD
623 	pid = proc_selfpid();
624 	if (proc) {
625 		traced_flag = proc_is_traced(proc);
626 		/* Should only be called on current proc */
627 		proc_name = proc_name_address(proc);
628 
629 		/*
630 		 * For a ptrauth violation, check if process isn't being ptraced and
631 		 * the task has the TF_PAC_EXC_FATAL flag set. If both conditions are true,
632 		 * terminate the task via exit_with_reason
633 		 */
634 		if (!traced_flag) {
635 			ca_event_t ca_event = CA_EVENT_ALLOCATE(pac_exception_event);
636 			CA_EVENT_TYPE(pac_exception_event) * pexc_event = ca_event->data;
637 			pexc_event->exception = exception;
638 			pexc_event->exception_code_0 = code[0];
639 			pexc_event->exception_code_1 = code[1];
640 			strlcpy(pexc_event->proc_name, proc_name, CA_PROCNAME_LEN);
641 			CA_EVENT_SEND(ca_event);
642 			if (task_is_pac_exception_fatal(task)) {
643 				os_log_error(OS_LOG_DEFAULT, "%s: process %s[%d] hit a pac violation\n", __func__, proc_name, pid);
644 				exit_with_pac_exception(proc, exception, code[0], code[1]);
645 				thread_exception_return();
646 				/* NOT_REACHABLE */
647 			}
648 		}
649 	}
650 #endif /* MACH_BSD */
651 }
652 #endif /* __has_feature(ptrauth_calls) */
653 
654 /*
655  *	Routine:	exception_triage
656  *	Purpose:
657  *		The current thread caught an exception.
658  *		We make an up-call to the thread's exception server.
659  *	Conditions:
660  *		Nothing locked and no resources held.
661  *		Called from an exception context, so
662  *		thread_exception_return and thread_kdb_return
663  *		are possible.
664  *	Returns:
665  *		KERN_SUCCESS if exception is handled by any of the handlers.
666  */
667 int debug4k_panic_on_exception = 0;
668 kern_return_t
exception_triage(exception_type_t exception,mach_exception_data_t code,mach_msg_type_number_t codeCnt)669 exception_triage(
670 	exception_type_t        exception,
671 	mach_exception_data_t   code,
672 	mach_msg_type_number_t  codeCnt)
673 {
674 	thread_t thread = current_thread();
675 	task_t   task   = current_task();
676 
677 	assert(codeCnt > 0);
678 
679 	if (VM_MAP_PAGE_SIZE(task->map) < PAGE_SIZE) {
680 		DEBUG4K_EXC("thread %p task %p map %p exception %d codes 0x%llx 0x%llx\n",
681 		    thread, task, task->map, exception, code[0], codeCnt > 1 ? code[1] : 0);
682 		if (debug4k_panic_on_exception) {
683 			panic("DEBUG4K thread %p task %p map %p exception %d codes 0x%llx 0x%llx",
684 			    thread, task, task->map, exception, code[0], codeCnt > 1 ? code[1] : 0);
685 		}
686 	}
687 
688 #if (DEVELOPMENT || DEBUG)
689 #ifdef MACH_BSD
690 	if (proc_pid(task->bsd_info) <= exception_log_max_pid) {
691 		printf("exception_log_max_pid: pid %d (%s): sending exception %d (0x%llx 0x%llx)\n",
692 		    proc_pid(task->bsd_info), proc_name_address(task->bsd_info),
693 		    exception, code[0], codeCnt > 1 ? code[1] : 0);
694 	}
695 #endif /* MACH_BSD */
696 #endif /* DEVELOPMENT || DEBUG */
697 
698 #if __has_feature(ptrauth_calls)
699 	if (exception & EXC_PTRAUTH_BIT) {
700 		exception &= ~EXC_PTRAUTH_BIT;
701 		assert(codeCnt == 2);
702 		pac_exception_triage(exception, code);
703 	}
704 #endif /* __has_feature(ptrauth_calls) */
705 	return exception_triage_thread(exception, code, codeCnt, thread);
706 }
707 
708 kern_return_t
bsd_exception(exception_type_t exception,mach_exception_data_t code,mach_msg_type_number_t codeCnt)709 bsd_exception(
710 	exception_type_t        exception,
711 	mach_exception_data_t   code,
712 	mach_msg_type_number_t  codeCnt)
713 {
714 	task_t                  task;
715 	lck_mtx_t               *mutex;
716 	thread_t                self = current_thread();
717 	kern_return_t           kr;
718 
719 	/*
720 	 * Maybe the task level will handle it.
721 	 */
722 	task = current_task();
723 	mutex = &task->itk_lock_data;
724 
725 	kr = exception_deliver(self, exception, code, codeCnt, task->exc_actions, mutex);
726 
727 	if (kr == KERN_SUCCESS || kr == MACH_RCV_PORT_DIED) {
728 		return KERN_SUCCESS;
729 	}
730 	return KERN_FAILURE;
731 }
732 
733 
734 /*
735  * Raise an exception on a task.
736  * This should tell launchd to launch Crash Reporter for this task.
737  */
738 kern_return_t
task_exception_notify(exception_type_t exception,mach_exception_data_type_t exccode,mach_exception_data_type_t excsubcode)739 task_exception_notify(exception_type_t exception,
740     mach_exception_data_type_t exccode, mach_exception_data_type_t excsubcode)
741 {
742 	mach_exception_data_type_t      code[EXCEPTION_CODE_MAX];
743 	wait_interrupt_t                wsave;
744 	kern_return_t kr = KERN_SUCCESS;
745 
746 	code[0] = exccode;
747 	code[1] = excsubcode;
748 
749 	wsave = thread_interrupt_level(THREAD_UNINT);
750 	kr = exception_triage(exception, code, EXCEPTION_CODE_MAX);
751 	(void) thread_interrupt_level(wsave);
752 	return kr;
753 }
754 
755 
756 /*
757  *	Handle interface for special performance monitoring
758  *	This is a special case of the host exception handler
759  */
760 kern_return_t
sys_perf_notify(thread_t thread,int pid)761 sys_perf_notify(thread_t thread, int pid)
762 {
763 	host_priv_t             hostp;
764 	ipc_port_t              xport;
765 	wait_interrupt_t        wsave;
766 	kern_return_t           ret;
767 
768 	hostp = host_priv_self();       /* Get the host privileged ports */
769 	mach_exception_data_type_t      code[EXCEPTION_CODE_MAX];
770 	code[0] = 0xFF000001;           /* Set terminate code */
771 	code[1] = pid;          /* Pass out the pid */
772 
773 	lck_mtx_lock(&hostp->lock);
774 	xport = hostp->exc_actions[EXC_RPC_ALERT].port;
775 
776 	/* Make sure we're not catching our own exception */
777 	if (!IP_VALID(xport) ||
778 	    !ip_active(xport) ||
779 	    ip_in_space_noauth(xport, get_threadtask(thread)->itk_space)) {
780 		lck_mtx_unlock(&hostp->lock);
781 		return KERN_FAILURE;
782 	}
783 
784 	lck_mtx_unlock(&hostp->lock);
785 
786 	wsave = thread_interrupt_level(THREAD_UNINT);
787 	ret = exception_deliver(
788 		thread,
789 		EXC_RPC_ALERT,
790 		code,
791 		2,
792 		hostp->exc_actions,
793 		&hostp->lock);
794 	(void)thread_interrupt_level(wsave);
795 
796 	return ret;
797 }
798