2 * Copyright (c) 2000-2020 Apple Computer, Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
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.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
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.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
32 * Mach Operating System
33 * Copyright (c) 1991,1990,1989,1988,1987 Carnegie Mellon University
34 * All Rights Reserved.
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.
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.
46 * Carnegie Mellon requests users of this software to return to
48 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
49 * School of Computer Science
50 * Carnegie Mellon University
51 * Pittsburgh PA 15213-3890
53 * any improvements or extensions that they make and grant Carnegie Mellon
54 * the rights to redistribute these changes.
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>
69 #include <mach/mach_exc.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>
79 #include <kern/counters.h>
80 #include <kern/ipc_tt.h>
81 #include <kern/task.h>
82 #include <kern/thread.h>
83 #include <kern/processor.h>
84 #include <kern/sched.h>
85 #include <kern/sched_prim.h>
86 #include <kern/host.h>
87 #include <kern/misc_protos.h>
88 #include <kern/ux_handler.h>
90 #include <vm/vm_map.h>
92 #include <security/mac_mach_internal.h>
95 #include <pexpert/pexpert.h>
97 bool panic_on_exception_triage
= false;
99 unsigned long c_thr_exc_raise
= 0;
100 unsigned long c_thr_exc_raise_state
= 0;
101 unsigned long c_thr_exc_raise_state_id
= 0;
102 unsigned long c_tsk_exc_raise
= 0;
103 unsigned long c_tsk_exc_raise_state
= 0;
104 unsigned long c_tsk_exc_raise_state_id
= 0;
106 /* forward declarations */
107 kern_return_t
exception_deliver(
109 exception_type_t exception
,
110 mach_exception_data_t code
,
111 mach_msg_type_number_t codeCnt
,
112 struct exception_action
*excp
,
116 check_exc_receiver_dependency(
117 exception_type_t exception
,
118 struct exception_action
*excp
,
122 kern_return_t
bsd_exception(
123 exception_type_t exception
,
124 mach_exception_data_t code
,
125 mach_msg_type_number_t codeCnt
);
126 #endif /* MACH_BSD */
128 #if __has_feature(ptrauth_calls)
129 extern int exit_with_pac_exception(
131 exception_type_t exception
,
132 mach_exception_code_t code
,
133 mach_exception_subcode_t subcode
);
135 extern bool proc_is_traced(void *p
);
136 #endif /* __has_feature(ptrauth_calls) */
139 * Routine: exception_init
141 * Global initialization of state for exceptions.
150 if (PE_parse_boot_argn("-panic_on_exception_triage", &tmp
, sizeof(tmp
))) {
151 panic_on_exception_triage
= true;
156 * Routine: exception_deliver
158 * Make an upcall to the exception server provided.
160 * Nothing locked and no resources held.
161 * Called from an exception context, so
162 * thread_exception_return and thread_kdb_return
165 * KERN_SUCCESS if the exception was handled
170 exception_type_t exception
,
171 mach_exception_data_t code
,
172 mach_msg_type_number_t codeCnt
,
173 struct exception_action
*excp
,
176 ipc_port_t exc_port
= IPC_PORT_NULL
;
177 exception_data_type_t small_code
[EXCEPTION_CODE_MAX
];
183 ipc_port_t thread_port
= IPC_PORT_NULL
, task_port
= IPC_PORT_NULL
;
186 * Save work if we are terminating.
187 * Just go back to our AST handler.
189 if (!thread
->active
&& !thread
->inspection
) {
194 * If there are no exception actions defined for this entity,
195 * we can't deliver here.
201 assert(exception
< EXC_TYPES_COUNT
);
202 if (exception
>= EXC_TYPES_COUNT
) {
206 excp
= &excp
[exception
];
209 * Snapshot the exception action data under lock for consistency.
210 * Hold a reference to the port over the exception_raise_* calls
211 * so it can't be destroyed. This seems like overkill, but keeps
212 * the port from disappearing between now and when
213 * ipc_object_copyin_from_kernel is finally called.
216 exc_port
= excp
->port
;
217 if (!IP_VALID(exc_port
)) {
218 lck_mtx_unlock(mutex
);
222 if (!ip_active(exc_port
)) {
224 lck_mtx_unlock(mutex
);
227 ip_reference(exc_port
);
228 exc_port
->ip_srights
++;
231 flavor
= excp
->flavor
;
232 behavior
= excp
->behavior
;
233 lck_mtx_unlock(mutex
);
235 code64
= (behavior
& MACH_EXCEPTION_CODES
);
236 behavior
&= ~MACH_EXCEPTION_MASK
;
239 small_code
[0] = CAST_DOWN_EXPLICIT(exception_data_type_t
, code
[0]);
240 small_code
[1] = CAST_DOWN_EXPLICIT(exception_data_type_t
, code
[1]);
246 /* Now is a reasonably good time to check if the exception action is
247 * permitted for this process, because after this point we will send
248 * the message out almost certainly.
249 * As with other failures, exception_triage_thread will go on
253 /* The global exception-to-signal translation port is safe to be an exception handler. */
254 if (is_ux_handler_port(exc_port
) == FALSE
&&
255 mac_exc_action_check_exception_send(task
, excp
) != 0) {
257 goto out_release_right
;
261 if (behavior
!= EXCEPTION_STATE
) {
262 task_reference(task
);
263 task_port
= convert_task_to_port(task
);
264 /* task ref consumed */
265 thread_reference(thread
);
266 thread_port
= convert_thread_to_port(thread
);
267 /* thread ref consumed */
271 case EXCEPTION_STATE
: {
272 mach_msg_type_number_t state_cnt
;
273 thread_state_data_t state
;
275 c_thr_exc_raise_state
++;
276 state_cnt
= _MachineStateCount
[flavor
];
277 kr
= thread_getstatus_to_user(thread
, flavor
,
278 (thread_state_t
)state
,
280 if (kr
== KERN_SUCCESS
) {
282 kr
= mach_exception_raise_state(exc_port
,
290 kr
= exception_raise_state(exc_port
, exception
,
297 if (kr
== KERN_SUCCESS
) {
298 if (exception
!= EXC_CORPSE_NOTIFY
) {
299 kr
= thread_setstatus_from_user(thread
, flavor
,
300 (thread_state_t
)state
,
303 goto out_release_right
;
307 goto out_release_right
;
310 case EXCEPTION_DEFAULT
:
313 kr
= mach_exception_raise(exc_port
,
320 kr
= exception_raise(exc_port
,
328 goto out_release_right
;
330 case EXCEPTION_STATE_IDENTITY
: {
331 mach_msg_type_number_t state_cnt
;
332 thread_state_data_t state
;
334 c_thr_exc_raise_state_id
++;
335 state_cnt
= _MachineStateCount
[flavor
];
336 kr
= thread_getstatus_to_user(thread
, flavor
,
337 (thread_state_t
)state
,
339 if (kr
== KERN_SUCCESS
) {
341 kr
= mach_exception_raise_state_identity(
352 kr
= exception_raise_state_identity(exc_port
,
363 if (kr
== KERN_SUCCESS
) {
364 if (exception
!= EXC_CORPSE_NOTIFY
) {
365 kr
= thread_setstatus_from_user(thread
, flavor
,
366 (thread_state_t
)state
,
369 goto out_release_right
;
373 goto out_release_right
;
377 panic("bad exception behavior!");
384 ipc_port_release_send(task_port
);
388 ipc_port_release_send(thread_port
);
392 ipc_port_release_send(exc_port
);
399 * Routine: check_exc_receiver_dependency
401 * Verify that the port destined for receiving this exception is not
402 * on the current task. This would cause hang in kernel for
403 * EXC_CRASH primarily. Note: If port is transferred
404 * between check and delivery then deadlock may happen.
407 * Nothing locked and no resources held.
408 * Called from an exception context.
410 * KERN_SUCCESS if its ok to send exception message.
413 check_exc_receiver_dependency(
414 exception_type_t exception
,
415 struct exception_action
*excp
,
418 kern_return_t retval
= KERN_SUCCESS
;
420 if (excp
== NULL
|| exception
!= EXC_CRASH
) {
424 task_t task
= current_task();
426 ipc_port_t xport
= excp
[exception
].port
;
429 && task
->itk_space
== xport
->ip_receiver
) {
430 retval
= KERN_FAILURE
;
432 lck_mtx_unlock(mutex
);
438 * Routine: exception_triage_thread
440 * The thread caught an exception.
441 * We make an up-call to the thread's exception server.
443 * Nothing locked and no resources held.
444 * Called from an exception context, so
445 * thread_exception_return and thread_kdb_return
448 * KERN_SUCCESS if exception is handled by any of the handlers.
451 exception_triage_thread(
452 exception_type_t exception
,
453 mach_exception_data_t code
,
454 mach_msg_type_number_t codeCnt
,
458 host_priv_t host_priv
;
460 kern_return_t kr
= KERN_FAILURE
;
463 assert(exception
!= EXC_RPC_ALERT
);
466 * If this behavior has been requested by the the kernel
467 * (due to the boot environment), we should panic if we
468 * enter this function. This is intended as a debugging
469 * aid; it should allow us to debug why we caught an
470 * exception in environments where debugging is especially
473 if (panic_on_exception_triage
) {
474 panic("called exception_triage when it was forbidden by the boot environment");
478 * Try to raise the exception at the activation level.
480 mutex
= &thread
->mutex
;
481 if (KERN_SUCCESS
== check_exc_receiver_dependency(exception
, thread
->exc_actions
, mutex
)) {
482 kr
= exception_deliver(thread
, exception
, code
, codeCnt
, thread
->exc_actions
, mutex
);
483 if (kr
== KERN_SUCCESS
|| kr
== MACH_RCV_PORT_DIED
) {
489 * Maybe the task level will handle it.
492 mutex
= &task
->itk_lock_data
;
493 if (KERN_SUCCESS
== check_exc_receiver_dependency(exception
, task
->exc_actions
, mutex
)) {
494 kr
= exception_deliver(thread
, exception
, code
, codeCnt
, task
->exc_actions
, mutex
);
495 if (kr
== KERN_SUCCESS
|| kr
== MACH_RCV_PORT_DIED
) {
501 * How about at the host level?
503 host_priv
= host_priv_self();
504 mutex
= &host_priv
->lock
;
506 if (KERN_SUCCESS
== check_exc_receiver_dependency(exception
, host_priv
->exc_actions
, mutex
)) {
507 kr
= exception_deliver(thread
, exception
, code
, codeCnt
, host_priv
->exc_actions
, mutex
);
508 if (kr
== KERN_SUCCESS
|| kr
== MACH_RCV_PORT_DIED
) {
514 if ((exception
!= EXC_CRASH
) && (exception
!= EXC_RESOURCE
) &&
515 (exception
!= EXC_GUARD
) && (exception
!= EXC_CORPSE_NOTIFY
)) {
516 thread_exception_return();
522 * Routine: exception_triage
524 * The current thread caught an exception.
525 * We make an up-call to the thread's exception server.
527 * Nothing locked and no resources held.
528 * Called from an exception context, so
529 * thread_exception_return and thread_kdb_return
532 * KERN_SUCCESS if exception is handled by any of the handlers.
534 int debug4k_panic_on_exception
= 0;
537 exception_type_t exception
,
538 mach_exception_data_t code
,
539 mach_msg_type_number_t codeCnt
)
541 thread_t thread
= current_thread();
542 if (VM_MAP_PAGE_SIZE(thread
->task
->map
) < PAGE_SIZE
) {
543 DEBUG4K_EXC("thread %p task %p map %p exception %d codes 0x%llx 0x%llx \n", thread
, thread
->task
, thread
->task
->map
, exception
, code
[0], code
[1]);
544 if (debug4k_panic_on_exception
) {
545 panic("DEBUG4K %s:%d thread %p task %p map %p exception %d codes 0x%llx 0x%llx \n", __FUNCTION__
, __LINE__
, thread
, thread
->task
, thread
->task
->map
, exception
, code
[0], code
[1]);
548 #if __has_feature(ptrauth_calls)
550 * If it is a ptrauth violation, then check if the task has the TF_PAC_EXC_FATAL
551 * flag set and isn't being ptraced. If so, terminate the task via exit_with_reason
553 if (exception
& EXC_PTRAUTH_BIT
) {
554 exception
&= ~EXC_PTRAUTH_BIT
;
556 boolean_t traced_flag
= FALSE
;
557 task_t task
= thread
->task
;
558 void *proc
= task
->bsd_info
;
560 if (task
->bsd_info
) {
561 traced_flag
= proc_is_traced(proc
);
564 if (task_is_pac_exception_fatal(current_task()) && !traced_flag
) {
565 exit_with_pac_exception(proc
, exception
, code
[0], code
[1]);
566 thread_exception_return();
570 #endif /* __has_feature(ptrauth_calls) */
571 return exception_triage_thread(exception
, code
, codeCnt
, thread
);
576 exception_type_t exception
,
577 mach_exception_data_t code
,
578 mach_msg_type_number_t codeCnt
)
582 thread_t self
= current_thread();
586 * Maybe the task level will handle it.
588 task
= current_task();
589 mutex
= &task
->itk_lock_data
;
591 kr
= exception_deliver(self
, exception
, code
, codeCnt
, task
->exc_actions
, mutex
);
593 if (kr
== KERN_SUCCESS
|| kr
== MACH_RCV_PORT_DIED
) {
601 * Raise an exception on a task.
602 * This should tell launchd to launch Crash Reporter for this task.
605 task_exception_notify(exception_type_t exception
,
606 mach_exception_data_type_t exccode
, mach_exception_data_type_t excsubcode
)
608 mach_exception_data_type_t code
[EXCEPTION_CODE_MAX
];
609 wait_interrupt_t wsave
;
610 kern_return_t kr
= KERN_SUCCESS
;
613 code
[1] = excsubcode
;
615 wsave
= thread_interrupt_level(THREAD_UNINT
);
616 kr
= exception_triage(exception
, code
, EXCEPTION_CODE_MAX
);
617 (void) thread_interrupt_level(wsave
);
623 * Handle interface for special performance monitoring
624 * This is a special case of the host exception handler
627 sys_perf_notify(thread_t thread
, int pid
)
631 wait_interrupt_t wsave
;
634 hostp
= host_priv_self(); /* Get the host privileged ports */
635 mach_exception_data_type_t code
[EXCEPTION_CODE_MAX
];
636 code
[0] = 0xFF000001; /* Set terminate code */
637 code
[1] = pid
; /* Pass out the pid */
639 struct task
*task
= thread
->task
;
640 xport
= hostp
->exc_actions
[EXC_RPC_ALERT
].port
;
642 /* Make sure we're not catching our own exception */
643 if (!IP_VALID(xport
) ||
645 task
->itk_space
== xport
->data
.receiver
) {
649 wsave
= thread_interrupt_level(THREAD_UNINT
);
650 ret
= exception_deliver(
657 (void)thread_interrupt_level(wsave
);