Mach exception delivery
Machine-independent exception delivery. exception_triage searches the thread, task and host exception ports in turn and delivers the exception; unclaimed exceptions fall back to a BSD signal.
exception_init source
Routine: exception_init
Purpose:
Global initialization of state for exceptions.
Conditions:
None.
void exception_init(void) { int tmp = 0; if (PE_parse_boot_argn("-panic_on_exception_triage", &tmp, sizeof(tmp))) { panic_on_exception_triage = true; } #if (DEVELOPMENT || DEBUG) if (exception_log_max_pid) { printf("Logging all exceptions where pid < exception_log_max_pid (%d)\n", exception_log_max_pid); } #endif /* (DEVELOPMENT || DEBUG) */ }
exception_port_copy_send source
ipc_port_t exception_port_copy_send(ipc_port_t port) { if (IP_VALID(port)) { if (is_ux_handler_port(port)) { /* is_ux_handler_port() compares against __DATA_CONST */ port = ipc_port_copy_send_any(port); } else { port = ipc_port_copy_send_mqueue(port); } } return port; }
exception_deliver source
Routine: exception_deliver
Purpose:
Make an upcall to the exception server provided.
Conditions:
Nothing locked and no resources held.
Called from an exception context, so
thread_exception_return and thread_kdb_return
are possible.
Returns:
KERN_SUCCESS if the exception was handled
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) { ipc_port_t exc_port = IPC_PORT_NULL; exception_data_type_t small_code[EXCEPTION_CODE_MAX]; thread_state_t new_state = NULL; int code64; int behavior; int flavor; kern_return_t kr = KERN_FAILURE; task_t task; task_id_token_t task_token; ipc_port_t thread_port = IPC_PORT_NULL, task_port = IPC_PORT_NULL, task_token_port = IPC_PORT_NULL; thread_set_status_flags_t get_flags = TSSF_TRANSLATE_TO_USER; thread_set_status_flags_t set_flags = TSSF_CHECK_USER_FLAGS; /* * Save work if we are terminating. * Just go back to our AST handler. */ if (!thread->active && !thread->inspection) { return KERN_SUCCESS; … more in source
exception_deliver_backtrace source
Routine: exception_deliver_backtrace
Purpose:
Attempt exception delivery with backtrace info to exception ports
in exc_ports in order.
Conditions:
Caller has a reference on bt_object, and send rights on exc_ports.
Does not consume any passed references or rights
void exception_deliver_backtrace( kcdata_object_t bt_object, ipc_port_t exc_ports[static BT_EXC_PORTS_COUNT], exception_type_t exception) { kern_return_t kr; mach_exception_data_type_t code[EXCEPTION_CODE_MAX]; ipc_port_t target_port, bt_obj_port; assert(exception == EXC_GUARD); code[0] = exception; code[1] = 0; kcdata_object_reference(bt_object); bt_obj_port = convert_kcdata_object_to_port(bt_object); /* backtrace object ref consumed, no-senders is armed */ if (!IP_VALID(bt_obj_port)) { return; } /* * We are guaranteed at task_enqueue_exception_with_corpse() time * that the exception port prefers backtrace delivery. */ for (unsigned int i = 0; i < BT_EXC_PORTS_COUNT; i++) { target_port = exc_ports[i]; … more in source
check_exc_receiver_dependency source
Routine: check_exc_receiver_dependency
Purpose:
Verify that the port destined for receiving this exception is not
on the current task. This would cause hang in kernel for
EXC_CRASH primarily. Note: If port is transferred
between check and delivery then deadlock may happen.
Conditions:
Nothing locked and no resources held.
Called from an exception context.
Returns:
KERN_SUCCESS if its ok to send exception message.
static kern_return_t check_exc_receiver_dependency( exception_type_t exception, struct exception_action *excp, lck_mtx_t *mutex) { kern_return_t retval = KERN_SUCCESS; if (excp == NULL || exception != EXC_CRASH) { return retval; } task_t task = current_task(); lck_mtx_lock(mutex); ipc_port_t xport = excp[exception].port; if (IP_VALID(xport) && ip_in_space_noauth(xport, task->itk_space)) { retval = KERN_FAILURE; } lck_mtx_unlock(mutex); return retval; }
exception_triage_thread source
Routine: exception_triage_thread
Purpose:
The thread caught an exception.
We make an up-call to the thread's exception server.
Conditions:
Nothing locked and no resources held.
Called from an exception context, so
thread_exception_return and thread_kdb_return
are possible.
Returns:
KERN_SUCCESS if exception is handled by any of the handlers.
kern_return_t exception_triage_thread( exception_type_t exception, mach_exception_data_t code, mach_msg_type_number_t codeCnt, thread_t thread) { task_t task; thread_ro_t tro; host_priv_t host_priv; lck_mtx_t *mutex; struct exception_action *actions; kern_return_t kr = KERN_FAILURE; assert(exception != EXC_RPC_ALERT); /* * If this behavior has been requested by the the kernel * (due to the boot environment), we should panic if we * enter this function. This is intended as a debugging * aid; it should allow us to debug why we caught an * exception in environments where debugging is especially * difficult. */ if (panic_on_exception_triage) { panic("called exception_triage when it was forbidden by the boot environment"); } /* * Try to raise the exception at the activation level. … more in source
pac_exception_triage source
static void pac_exception_triage( exception_type_t exception, mach_exception_data_t code) { boolean_t traced_flag = FALSE; task_t task = current_task(); void *proc = get_bsdtask_info(task); char *proc_name = (char *) "unknown"; int pid = 0; #ifdef MACH_BSD pid = proc_selfpid(); if (proc) { traced_flag = proc_is_traced(proc); /* Should only be called on current proc */ proc_name = proc_name_address(proc); /* * For a ptrauth violation, check if process isn't being ptraced and * the task has the TFRO_PAC_EXC_FATAL flag set. If both conditions are true, * terminate the task via exit_with_reason */ if (!traced_flag) { if (pac_exception_telemetry) { ca_event_t ca_event = CA_EVENT_ALLOCATE(pac_exception_event); CA_EVENT_TYPE(pac_exception_event) * pexc_event = ca_event->data; pexc_event->exception = exception; pexc_event->exception_code_0 = code[0]; pexc_event->exception_code_1 = code[1]; … more in source
maybe_unrecoverable_exception_triage source
__has_feature(ptrauth_calls)
static void maybe_unrecoverable_exception_triage( exception_type_t exception, mach_exception_data_t code) { task_t task = current_task(); void *proc = get_bsdtask_info(task); #ifdef MACH_BSD if (!proc) { return; } /* * Note that the below policy to decide whether this should be unrecoverable is * likely conceptually specific to the particular exception. * If you find yourself adding another user_brk_..._descriptor and want to customize the * policy for whether it should be unrecoverable, consider attaching each policy to * the corresponding descriptor and somehow carrying it through to here. */ /* These exceptions are deliverable (and potentially recoverable) if the process is being debugged. */ if (is_address_space_debugged(proc)) { return; } /* * By policy, this exception is uncatchable by exception/signal handlers. * Therefore exit immediately. */ /* Should only be called on current proc */ … more in source
exception_triage source
kern_return_t exception_triage( exception_type_t exception, mach_exception_data_t code, mach_msg_type_number_t codeCnt) { thread_t thread = current_thread(); task_t task = current_task(); assert(codeCnt > 0); if (VM_MAP_PAGE_SIZE(task->map) < PAGE_SIZE) { DEBUG4K_EXC("thread %p task %p map %p exception %d codes 0x%llx 0x%llx\n", thread, task, task->map, exception, code[0], codeCnt > 1 ? code[1] : 0); if (debug4k_panic_on_exception) { panic("DEBUG4K thread %p task %p map %p exception %d codes 0x%llx 0x%llx", thread, task, task->map, exception, code[0], codeCnt > 1 ? code[1] : 0); } } #if DEVELOPMENT || DEBUG #ifdef MACH_BSD if (proc_pid(get_bsdtask_info(task)) <= exception_log_max_pid) { record_system_event(SYSTEM_EVENT_TYPE_INFO, SYSTEM_EVENT_SUBSYSTEM_PROCESS, "process exit", "exception_log_max_pid: pid %d (%s): sending exception %d (0x%llx 0x%llx)", proc_pid(get_bsdtask_info(task)), proc_name_address(get_bsdtask_info(task)), exception, code[0], codeCnt > 1 ? code[1] : 0); } #endif /* MACH_BSD */ #endif /* DEVELOPMENT || DEBUG */ … more in source
bsd_exception source
kern_return_t bsd_exception( exception_type_t exception, mach_exception_data_t code, mach_msg_type_number_t codeCnt) { task_t task; lck_mtx_t *mutex; thread_t self = current_thread(); kern_return_t kr; /* * Maybe the task level will handle it. */ task = current_task(); mutex = &task->itk_lock_data; kr = exception_deliver(self, exception, code, codeCnt, task->exc_actions, mutex); if (kr == KERN_SUCCESS || kr == MACH_RCV_PORT_DIED) { return KERN_SUCCESS; } return KERN_FAILURE; }
task_exception_notify source
Raise an exception on a task.
This should tell launchd to launch Crash Reporter for this task.
If the exception is fatal, we should be careful about sending a synchronous exception
kern_return_t task_exception_notify(exception_type_t exception, mach_exception_data_type_t exccode, mach_exception_data_type_t excsubcode, const bool fatal) { mach_exception_data_type_t code[EXCEPTION_CODE_MAX]; wait_interrupt_t wsave; kern_return_t kr = KERN_SUCCESS; /* * If we are not in dev mode, nobody should be allowed to synchronously handle * a fatal EXC_GUARD - they might stall on it indefinitely */ if (fatal && !developer_mode_state() && exception == EXC_GUARD) { return KERN_DENIED; } code[0] = exccode; code[1] = excsubcode; wsave = thread_interrupt_level(THREAD_UNINT); kr = exception_triage(exception, code, EXCEPTION_CODE_MAX); (void) thread_interrupt_level(wsave); return kr; }
sys_perf_notify source
Handle interface for special performance monitoring
This is a special case of the host exception handler
kern_return_t sys_perf_notify(thread_t thread, int pid) { host_priv_t hostp; ipc_port_t xport; struct exception_action saved_exc_actions[EXC_TYPES_COUNT] = {}; wait_interrupt_t wsave; kern_return_t ret; struct label *temp_label; hostp = host_priv_self(); /* Get the host privileged ports */ mach_exception_data_type_t code[EXCEPTION_CODE_MAX]; code[0] = 0xFF000001; /* Set terminate code */ code[1] = pid; /* Pass out the pid */ #if CONFIG_MACF /* Create new label for saved_exc_actions[EXC_RPC_ALERT] */ mac_exc_associate_action_label(&saved_exc_actions[EXC_RPC_ALERT], mac_exc_create_label(&saved_exc_actions[EXC_RPC_ALERT])); #endif /* CONFIG_MACF */ lck_mtx_lock(&hostp->lock); xport = hostp->exc_actions[EXC_RPC_ALERT].port; /* Make sure we're not catching our own exception */ if (!IP_VALID(xport) || !ip_active(xport) || ip_in_space_noauth(xport, get_threadtask(thread)->itk_space)) { lck_mtx_unlock(&hostp->lock); #if CONFIG_MACF … more in source