kern_return_t catch_mach_exception_raise( __unused mach_port_t exception_port, mach_port_t thread, mach_port_t task, exception_type_t exception, mach_exception_data_t code, __unused mach_msg_type_number_t codeCnt ) { task_t self = current_task(); thread_t th_act; ipc_port_t thread_port; struct proc *p; kern_return_t result = MACH_MSG_SUCCESS; int ux_signal = 0; mach_exception_code_t ucode = 0; struct uthread *ut; mach_port_name_t thread_name = CAST_MACH_PORT_TO_NAME(thread); mach_port_name_t task_name = CAST_MACH_PORT_TO_NAME(task); /* * Convert local thread name to global port. */ if (MACH_PORT_VALID(thread_name) && (ipc_object_copyin(get_task_ipcspace(self), thread_name, MACH_MSG_TYPE_PORT_SEND, (void *) &thread_port) == MACH_MSG_SUCCESS)) { if (IPC_PORT_VALID(thread_port)) { th_act = convert_port_to_thread(thread_port); ipc_port_release(thread_port); } else { th_act = THREAD_NULL; } /* * Catch bogus ports */ if (th_act != THREAD_NULL) { /* * Convert exception to unix signal and code. */ ux_exception(exception, code[0], code[1], &ux_signal, &ucode); ut = get_bsdthread_info(th_act); p = proc_findthread(th_act); /* Can't deliver a signal without a bsd process reference */ if (p == NULL) { ux_signal = 0; result = KERN_FAILURE; } /* * Stack overflow should result in a SIGSEGV signal * on the alternate stack. * but we have one or more guard pages after the * stack top, so we would get a KERN_PROTECTION_FAILURE * exception instead of KERN_INVALID_ADDRESS, resulting in * a SIGBUS signal. * Detect that situation and select the correct signal. */ if (code[0] == KERN_PROTECTION_FAILURE && ux_signal == SIGBUS) { user_addr_t sp, stack_min, stack_max; int mask; struct sigacts *ps; sp = code[1]; stack_max = p->user_stack; stack_min = p->user_stack - MAXSSIZ; if (sp >= stack_min && sp < stack_max) { /* * This is indeed a stack overflow. Deliver a * SIGSEGV signal. */ ux_signal = SIGSEGV; /* * If the thread/process is not ready to handle * SIGSEGV on an alternate stack, force-deliver * SIGSEGV with a SIG_DFL handler. */ mask = sigmask(ux_signal); ps = p->p_sigacts; if ((p->p_sigignore & mask) || (ut->uu_sigwait & mask) || (ut->uu_sigmask & mask) || (ps->ps_sigact[SIGSEGV] == SIG_IGN) || (! (ps->ps_sigonstack & mask))) { p->p_sigignore &= ~mask; p->p_sigcatch &= ~mask; ps->ps_sigact[SIGSEGV] = SIG_DFL; ut->uu_sigwait &= ~mask; ut->uu_sigmask &= ~mask; } } } /* * Send signal. */ if (ux_signal != 0) { ut->uu_exception = exception; //ut->uu_code = code[0]; // filled in by threadsignal ut->uu_subcode = code[1]; threadsignal(th_act, ux_signal, code[0]); } if (p != NULL) proc_rele(p); thread_deallocate(th_act); } else result = KERN_INVALID_ARGUMENT; } else result = KERN_INVALID_ARGUMENT; /* * Delete our send rights to the task port. */ (void)mach_port_deallocate(get_task_ipcspace(ux_handler_self), task_name); return (result); }
int cs_invalid_page( addr64_t vaddr) { struct proc *p; int send_kill = 0, retval = 0, verbose = cs_debug; uint32_t csflags; p = current_proc(); if (verbose) printf("CODE SIGNING: cs_invalid_page(0x%llx): p=%d[%s]\n", vaddr, p->p_pid, p->p_comm); proc_lock(p); /* XXX for testing */ if (cs_force_kill) p->p_csflags |= CS_KILL; if (cs_force_hard) p->p_csflags |= CS_HARD; /* CS_KILL triggers a kill signal, and no you can't have the page. Nothing else. */ if (p->p_csflags & CS_KILL) { if (panic_on_cs_killed && vaddr >= SHARED_REGION_BASE && vaddr < SHARED_REGION_BASE + SHARED_REGION_SIZE) { panic("<rdar://14393620> cs_invalid_page(va=0x%llx): killing p=%p\n", (uint64_t) vaddr, p); } p->p_csflags |= CS_KILLED; cs_procs_killed++; send_kill = 1; retval = 1; } #if __x86_64__ if (panic_on_cs_killed && vaddr >= SHARED_REGION_BASE && vaddr < SHARED_REGION_BASE + SHARED_REGION_SIZE) { panic("<rdar://14393620> cs_invalid_page(va=0x%llx): cs error p=%p\n", (uint64_t) vaddr, p); } #endif /* __x86_64__ */ /* CS_HARD means fail the mapping operation so the process stays valid. */ if (p->p_csflags & CS_HARD) { retval = 1; } else { if (p->p_csflags & CS_VALID) { p->p_csflags &= ~CS_VALID; cs_procs_invalidated++; verbose = 1; } } csflags = p->p_csflags; proc_unlock(p); if (verbose) printf("CODE SIGNING: cs_invalid_page(0x%llx): " "p=%d[%s] final status 0x%x, %s page%s\n", vaddr, p->p_pid, p->p_comm, p->p_csflags, retval ? "denying" : "allowing (remove VALID)", send_kill ? " sending SIGKILL" : ""); if (send_kill) threadsignal(current_thread(), SIGKILL, EXC_BAD_ACCESS); return retval; }
int cs_invalid_page(addr64_t vaddr, boolean_t *cs_killed) { struct proc *p; int send_kill = 0, retval = 0, verbose = cs_debug; uint32_t csflags; p = current_proc(); if (verbose) printf("CODE SIGNING: cs_invalid_page(0x%llx): p=%d[%s]\n", vaddr, p->p_pid, p->p_comm); proc_lock(p); /* XXX for testing */ if (cs_force_kill) p->p_csflags |= CS_KILL; if (cs_force_hard) p->p_csflags |= CS_HARD; /* CS_KILL triggers a kill signal, and no you can't have the page. Nothing else. */ if (p->p_csflags & CS_KILL) { p->p_csflags |= CS_KILLED; cs_procs_killed++; send_kill = 1; retval = 1; } /* CS_HARD means fail the mapping operation so the process stays valid. */ if (p->p_csflags & CS_HARD) { retval = 1; } else { if (p->p_csflags & CS_VALID) { p->p_csflags &= ~CS_VALID; cs_procs_invalidated++; verbose = 1; } } csflags = p->p_csflags; proc_unlock(p); if (verbose) printf("CODE SIGNING: cs_invalid_page(0x%llx): " "p=%d[%s] final status 0x%x, %s page%s\n", vaddr, p->p_pid, p->p_comm, p->p_csflags, retval ? "denying" : "allowing (remove VALID)", send_kill ? " sending SIGKILL" : ""); if (send_kill) { /* We will set the exit reason for the thread later */ threadsignal(current_thread(), SIGKILL, EXC_BAD_ACCESS, FALSE); if (cs_killed) { *cs_killed = TRUE; } } else if (cs_killed) { *cs_killed = FALSE; } return retval; }