RuntimeProtector::RuntimeProtector() : m_pImpl( new Impl( this ) ) , m_bHashadException( false ) { if( !Impl::ms_bMachPortSet ) { // prevent OS X debugger from catching signals in a none re-catchable way task_set_exception_ports(mach_task_self(), EXC_MASK_BAD_ACCESS, MACH_PORT_NULL, EXCEPTION_DEFAULT, MACHINE_THREAD_STATE); task_set_exception_ports(mach_task_self(), EXC_MASK_BAD_INSTRUCTION, MACH_PORT_NULL, EXCEPTION_DEFAULT, MACHINE_THREAD_STATE); } }
void RegisterExceptionHandler(void) { mach_port_t task = mach_task_self(); mach_port_t handler_port; kern_return_t rc; pthread_t tid; int err; /* Use the same mask as Breakpad. */ exception_mask_t exception_mask = EXC_MASK_BAD_ACCESS | EXC_MASK_BAD_INSTRUCTION | EXC_MASK_ARITHMETIC | EXC_MASK_BREAKPOINT; rc = mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE, &handler_port); CHECK(rc == KERN_SUCCESS); rc = mach_port_insert_right(task, handler_port, handler_port, MACH_MSG_TYPE_MAKE_SEND); CHECK(rc == KERN_SUCCESS); err = pthread_create(&tid, NULL, ExceptionHandlerThread, (void *) (uintptr_t) handler_port); CHECK(err == 0); err = pthread_detach(tid); CHECK(err == 0); rc = task_set_exception_ports(mach_task_self(), exception_mask, handler_port, EXCEPTION_DEFAULT, THREAD_STATE_NONE); CHECK(rc == KERN_SUCCESS); }
EXPORT int start(mach_port_t task, pid_t infoPid) { kern_return_t kret; pthread_t tid[2]; interface* face = find_interface(task); kret = mach_port_allocate(current_task(), MACH_PORT_RIGHT_RECEIVE, &face->server_port); RETURN_ON_MACH_ERROR("[-start] mach_port_allocate failed", kret); kret = mach_port_insert_right(current_task(), face->server_port, face->server_port, MACH_MSG_TYPE_MAKE_SEND); RETURN_ON_MACH_ERROR("[-start] mach_port_insert_right failed", kret); kret = task_set_exception_ports(task, EXC_MASK_ALL, face->server_port, EXCEPTION_DEFAULT|MACH_EXCEPTION_CODES, THREAD_STATE_NONE); RETURN_ON_MACH_ERROR("[-start] task_set_exception_ports failed", kret); int err = pthread_create(&tid[0], NULL, (void *(*)(void*))kqueue_loop, (void *)(unsigned long long)infoPid); if (err != 0) DEBUG_PRINT("\n[-start] can't create thread :[%s]", strerror(err)); else DEBUG_PRINT("\n[-start] Thread created successfully %d\n", 0); err = pthread_create(&tid[1], NULL, (void *(*)(void*))exception_server, (void *(*)(void*))(unsigned long long)face->server_port); if (err != 0) DEBUG_PRINT("\n[-start] can't create thread :[%s]", strerror(err)); else DEBUG_PRINT("\n[-start] Thread created successfully %d\n", 0); return 1; }
/** Restore the original mach exception ports. */ void ksmachexc_i_restoreExceptionPorts(void) { KSLOG_DEBUG("Restoring original exception ports."); if(g_previousExceptionPorts.count == 0) { KSLOG_DEBUG("Original exception ports were already restored."); return; } const task_t thisTask = mach_task_self(); kern_return_t kr; // Reinstall old exception ports. for(mach_msg_type_number_t i = 0; i < g_previousExceptionPorts.count; i++) { KSLOG_TRACE("Restoring port index %d", i); kr = task_set_exception_ports(thisTask, g_previousExceptionPorts.masks[i], g_previousExceptionPorts.ports[i], g_previousExceptionPorts.behaviors[i], g_previousExceptionPorts.flavors[i]); if(kr != KERN_SUCCESS) { KSLOG_ERROR("task_set_exception_ports: %s", mach_error_string(kr)); } } KSLOG_DEBUG("Exception ports restored."); g_previousExceptionPorts.count = 0; }
int vm_init(void) { #ifdef HAVE_MMAP_VM #ifndef zero_fd zero_fd = open("/dev/zero", O_RDWR); if (zero_fd < 0) return -1; #endif #endif // On 10.4 and earlier, reset CrashReporter's task signal handler to // avoid having it show up for signals that get handled. #if defined(__APPLE__) && defined(__MACH__) struct utsname info; if (!uname(&info) && atoi(info.release) <= 8) { task_set_exception_ports(mach_task_self(), EXC_MASK_BAD_ACCESS | EXC_MASK_ARITHMETIC, MACH_PORT_NULL, EXCEPTION_STATE_IDENTITY, MACHINE_THREAD_STATE); } #endif return 0; }
void next_restore_exception_ports (task_t task, struct next_exception_info *info) { int i; kern_return_t kret; for (i = 0; i < info->count; i++) { kret = task_set_exception_ports (task, info->masks[i], info->ports[i], info->behaviors[i], info->flavors[i]); MACH_CHECK_ERROR (kret); } }
void setMachExceptionPort(mach_port_t exceptionPort) { // Assert that we dont try to call setMachExceptionPort more than once per process. #if !ASSERT_DISABLED static mach_port_t taskExceptionPort = MACH_PORT_NULL; ASSERT(taskExceptionPort == MACH_PORT_NULL); taskExceptionPort = exceptionPort; #endif if (task_set_exception_ports(mach_task_self(), EXC_MASK_CRASH, exceptionPort, EXCEPTION_STATE_IDENTITY | MACH_EXCEPTION_CODES, MACHINE_THREAD_STATE) != KERN_SUCCESS) ASSERT_NOT_REACHED(); }
static void EnableMachExceptionHandler( MachExceptionHandlerData *saved_handlers) { for (size_t index = 0; index < saved_handlers->count; ++index) { if (saved_handlers->ports[index] != MACH_PORT_NULL) { kern_return_t kr = task_set_exception_ports( mach_task_self(), saved_handlers->masks[index], saved_handlers->ports[index], saved_handlers->behaviors[index], saved_handlers->flavors[index]); CHECK(kr == KERN_SUCCESS); } } }
void osfmach3_trap_syscall_self( boolean_t catch_them, exception_behavior_t behavior, thread_state_flavor_t flavor) { kern_return_t kr; exception_mask_t mask; mach_port_t syscall_port; exception_behavior_t syscall_behavior; thread_state_flavor_t syscall_flavor; if (current != &init_task) panic("osfmach3_trap_syscall_self: " "current=0x%p is not init_task\n", current); if (! parent_server) return; /* * Change the way the server syscalls are handled. */ if (catch_them) { /* catch our own syscalls */ syscall_port = current->osfmach3.thread->mach_trap_port; syscall_behavior = behavior; syscall_flavor = flavor; } else { /* let our syscalls go to the parent server */ syscall_port = parent_server_syscall_port; syscall_behavior = parent_server_syscall_behavior; syscall_flavor = parent_server_syscall_flavor; } mask = parent_server_syscall_exc_mask(); kr = task_set_exception_ports(current->osfmach3.task->mach_task_port, mask, syscall_port, syscall_behavior, syscall_flavor); if (kr != KERN_SUCCESS) { MACH3_DEBUG(0, kr, ("osfmach3_trap_syscall_self(catch_them=%d): " "thread_set_exception_ports", catch_them)); panic("osfmach3_trap_syscall_self: can't set exception port"); } }
static bool xnu_restore_exception_ports (int pid) { kern_return_t kr; int i; task_t task = pid_to_task (pid); for (i = 0; i < ex.count; i++) { kr = task_set_exception_ports (task, ex.masks[i], ex.ports[i], ex.behaviors[i], ex.flavors[i]); if (kr != KERN_SUCCESS) { eprintf ("fail to restore exception ports\n"); return false; } } return true; }
void mach_exn_init() { kern_return_t r; mach_port_t me; pthread_t thread; pthread_attr_t attr; exception_mask_t mask; me = mach_task_self(); r = mach_port_allocate(me,MACH_PORT_RIGHT_RECEIVE,&exception_port); if(r != MACH_MSG_SUCCESS) DIE("mach_port_allocate"); r = mach_port_insert_right(me,exception_port,exception_port, MACH_MSG_TYPE_MAKE_SEND); if(r != MACH_MSG_SUCCESS) DIE("mach_port_insert_right"); /* for others see mach/exception_types.h */ mask = EXC_MASK_BAD_ACCESS; /* get the old exception ports */ r = task_get_exception_ports( me, mask, old_exc_ports.masks, &old_exc_ports.count, old_exc_ports.ports, old_exc_ports.behaviors, old_exc_ports.flavors ); if(r != MACH_MSG_SUCCESS) DIE("task_get_exception_ports"); /* set the new exception ports */ r = task_set_exception_ports( me, mask, exception_port, EXCEPTION_DEFAULT, MACHINE_THREAD_STATE ); if(r != MACH_MSG_SUCCESS) DIE("task_set_exception_ports"); if(pthread_attr_init(&attr) != 0) DIE("pthread_attr_init"); if(pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED) != 0) DIE("pthread_attr_setdetachedstate"); if(pthread_create(&thread,&attr,exc_thread,NULL) != 0) DIE("pthread_create"); pthread_attr_destroy(&attr); }
int main() { kern_return_t kr = 0; mach_port_t exc_port; mach_port_t task = mach_task_self(); pthread_t exception_thread; int err; mach_msg_type_number_t maskCount = 1; exception_mask_t mask; exception_handler_t handler; exception_behavior_t behavior; thread_state_flavor_t flavor; if ((kr = mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE, &exc_port)) != KERN_SUCCESS) { fprintf(stderr, "mach_port_allocate: %#x\n", kr); exit(1); } if ((kr = mach_port_insert_right(task, exc_port, exc_port, MACH_MSG_TYPE_MAKE_SEND)) != KERN_SUCCESS) { fprintf(stderr, "mach_port_allocate: %#x\n", kr); exit(1); } if ((kr = task_get_exception_ports(task, EXC_MASK_ALL, &mask, &maskCount, &handler, &behavior, &flavor)) != KERN_SUCCESS) { fprintf(stderr,"task_get_exception_ports: %#x\n", kr); exit(1); } if ((err = pthread_create(&exception_thread, NULL, server_thread, &exc_port)) != 0) { fprintf(stderr, "pthread_create server_thread: %s\n", strerror(err)); exit(1); } pthread_detach(exception_thread); if ((kr = task_set_exception_ports(task, EXC_MASK_ALL, exc_port, EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES, flavor)) != KERN_SUCCESS) { fprintf(stderr, "task_set_exception_ports: %#x\n", kr); exit(1); } puts("Starting exception stuff"); int a = 1; int b = 0; float c = a / b; int cat = *(int *)(0x13371337); printf("Have c %f and cat %d\n", c, cat); puts("End."); }
void registerDarwinExceptionHandler(void) { // Create the Mach exception port mach_port_t self = mach_task_self(); assert(mach_port_allocate(self, MACH_PORT_RIGHT_RECEIVE, &mach_ex_port) == KERN_SUCCESS); assert(mach_port_insert_right(self, mach_ex_port, mach_ex_port, MACH_MSG_TYPE_MAKE_SEND) == KERN_SUCCESS); // Create the thread which receives the exceptions pthread_t thread; pthread_attr_t attr; assert(!pthread_attr_init(&attr)); assert(!pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED)); assert(!pthread_create(&thread, &attr, exceptionHandlerEntryPoint, NULL)); pthread_attr_destroy(&attr); assert(task_set_exception_ports(self, EXC_MASK_BAD_ACCESS, mach_ex_port, EXCEPTION_DEFAULT, MACHINE_THREAD_STATE) == KERN_SUCCESS); }
/* Initialize the Mach exception handler thread. Return 0 if OK, -1 on error. */ static int mach_initialize () { mach_port_t self; exception_mask_t mask; pthread_attr_t attr; pthread_t thread; self = mach_task_self (); /* Allocate a port on which the thread shall listen for exceptions. */ if (mach_port_allocate (self, MACH_PORT_RIGHT_RECEIVE, &our_exception_port) != KERN_SUCCESS) return -1; /* See http://web.mit.edu/darwin/src/modules/xnu/osfmk/man/mach_port_insert_right.html. */ if (mach_port_insert_right (self, our_exception_port, our_exception_port, MACH_MSG_TYPE_MAKE_SEND) != KERN_SUCCESS) return -1; /* The exceptions we want to catch. Only EXC_BAD_ACCESS is interesting for us (see above in function catch_exception_raise). */ mask = EXC_MASK_BAD_ACCESS; /* Create the thread listening on the exception port. */ if (pthread_attr_init (&attr) != 0) return -1; if (pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED) != 0) return -1; if (pthread_create (&thread, &attr, mach_exception_thread, NULL) != 0) return -1; pthread_attr_destroy (&attr); /* Replace the exception port info for these exceptions with our own. Note that we replace the exception port for the entire task, not only for a particular thread. This has the effect that when our exception port gets the message, the thread specific exception port has already been asked, and we don't need to bother about it. See http://web.mit.edu/darwin/src/modules/xnu/osfmk/man/task_set_exception_ports.html. */ if (task_set_exception_ports (self, mask, our_exception_port, EXCEPTION_DEFAULT, MACHINE_THREAD_STATE) != KERN_SUCCESS) return -1; return 0; }
bool exception_handler_install_platform() { if (s_installed) { return true; } // allocate port to listen for exceptions if (mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &s_listen_port) != KERN_SUCCESS) { return false; } // http://web.mit.edu/darwin/src/modules/xnu/osfmk/man/mach_port_insert_right.html if (mach_port_insert_right(mach_task_self(), s_listen_port, s_listen_port, MACH_MSG_TYPE_MAKE_SEND) != KERN_SUCCESS) { return false; } // filter out any exception other than EXC_BAD_ACCESS // http://web.mit.edu/darwin/src/modules/xnu/osfmk/man/task_set_exception_ports.html if (task_set_exception_ports(mach_task_self(), exception_mask, s_listen_port, EXCEPTION_DEFAULT, MACHINE_THREAD_STATE) != KERN_SUCCESS) { return false; } // launch thread to listen for exceptions pthread_attr_t attr; pthread_t thread; if (pthread_attr_init(&attr) != 0) { return false; } if (pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED) != 0) { return false; } if (pthread_create(&thread, &attr, &mach_exception_thread, NULL) != 0) { return -1; } pthread_attr_destroy(&attr); s_installed = true; return true; }
static bool xnu_create_exception_thread(RDebug *dbg) { kern_return_t kr; int ret; mach_port_t exception_port = MACH_PORT_NULL; // Got the mach port for the current process mach_port_t task_self = mach_task_self (); task_t task = pid_to_task (dbg->pid); if (task == -1) { eprintf ("error to get task for the debugging process" " xnu_start_exception_thread\n"); return false; } if (!MACH_PORT_VALID (task_self)) { eprintf ("error to get the task for the current process" " xnu_start_exception_thread\n"); return false; } // Allocate an exception port that we will use to track our child process kr = mach_port_allocate (task_self, MACH_PORT_RIGHT_RECEIVE, &exception_port); RETURN_ON_MACH_ERROR ("error to allocate mach_port exception\n", R_FALSE); // Add the ability to send messages on the new exception port kr = mach_port_insert_right (task_self, exception_port, exception_port, MACH_MSG_TYPE_MAKE_SEND); RETURN_ON_MACH_ERROR ("error to allocate insert right\n", R_FALSE); // Save the original state of the exception ports for our child process ret = xnu_save_exception_ports (dbg); if (ret == R_FALSE) { eprintf ("error to save exception port info\n"); return false; } // Set the ability to get all exceptions on this port kr = task_set_exception_ports (task, EXC_MASK_ALL, exception_port, EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES, THREAD_STATE_NONE); RETURN_ON_MACH_ERROR ("error to set port to receive exceptions\n", R_FALSE); // Create the exception thread //TODO where to save the exception thread //TODO see options pthread_create ret = pthread_create (&dbg->ex->thread, NULL, xnu_exception_thread, dbg); if (ret) { perror ("pthread_create"); return false; } return true; }
bool TargetException::setExceptionCallback(ExceptionCallback callback) { m_callback = std::move(callback); auto kr = mach_port_allocate(mach_task_self(),MACH_PORT_RIGHT_RECEIVE,&m_exceptionPort); if (kr != KERN_SUCCESS) { log(QString("mach_port_allocate failde: %1").arg(mach_error_string(kr)), LogType::Error); return false; } kr = mach_port_insert_right(mach_task_self(), m_exceptionPort, m_exceptionPort, MACH_MSG_TYPE_MAKE_SEND); if (kr != KERN_SUCCESS) { log(QString("mach_port_insert_right failde: %1").arg(mach_error_string(kr)), LogType::Error); return false; } kr = task_get_exception_ports( g_task, EXC_MASK_ALL, m_oldExcPorts.masks, &m_oldExcPorts.count, m_oldExcPorts.ports, m_oldExcPorts.behaviors, m_oldExcPorts.flavors ); if (kr != KERN_SUCCESS) { log(QString("task_get_exception_ports failde: %1").arg(mach_error_string(kr)), LogType::Error); return false; } kr = task_set_exception_ports( g_task, EXC_MASK_ALL, m_exceptionPort, EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES, THREAD_STATE_NONE ); if (kr != KERN_SUCCESS) { log(QString("task_set_exception_ports failde: %1").arg(mach_error_string(kr)), LogType::Error); return false; } return true; }
// s/inferior_task/port/ static int debug_attach(int pid) { task_t task = pid_to_task (pid); if (task == -1) return -1; eprintf ("pid: %d\ntask: %d\n", pid, task); #if 0 // TODO : move this code into debug if (task_threads (task, &inferior_threads, &inferior_thread_count) != KERN_SUCCESS) { eprintf ("Failed to get list of task's threads.\n"); return -1; } eprintf ("Thread count: %d\n", inferior_thread_count); #endif #if SUSPEND if (task_suspend (this->port) != KERN_SUCCESS) { eprintf ("cannot suspend task\n"); return -1; // R_FALSE } #endif /* is this required for arm ? */ #if EXCEPTION_PORT int exception_port; if (mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_RECEIVE, &exception_port) != KERN_SUCCESS) { eprintf ("Failed to create exception port.\n"); return -1; } if (mach_port_insert_right(mach_task_self(), exception_port, exception_port, MACH_MSG_TYPE_MAKE_SEND) != KERN_SUCCESS) { eprintf ("Failed to acquire insertion rights on the port.\n"); return -1; } if (task_set_exception_ports(inferior_task, EXC_MASK_ALL, exception_port, EXCEPTION_DEFAULT, THREAD_STATE_NONE) != KERN_SUCCESS) { eprintf ("Failed to set the inferior's exception ports.\n"); return -1; } #endif return task; }
/*++ Function : SEHCleanupExceptionPort Restore default exception port handler (no parameters, no return value) Note : During PAL_Terminate, we reach a point where SEH isn't possible any more (handle manager is off, etc). Past that point, we can't avoid crashing on an exception. --*/ void SEHCleanupExceptionPort (void) { #if !DISABLE_EXCEPTIONS kern_return_t MachRet; TRACE("Restoring default exception ports\n"); // Set the port that listens to exceptions for this task. MachRet = task_set_exception_ports(mach_task_self(), PAL_EXC_MASK | (s_DebugInitialized ? PAL_EXC_DEBUGGING_MASK : 0), MACH_PORT_NULL, EXCEPTION_DEFAULT, PPC_THREAD_STATE); if (MachRet != KERN_SUCCESS) { UTIL_SetLastErrorFromMach(MachRet); } s_DebugInitialized = FALSE; #endif // !DISABLE_EXCEPTIONS }
/*++ Function : MachExceptionInitializeDebug Initialize the mach exception handlers necessary for a managed debugger to work Return value : None --*/ void MachExceptionInitializeDebug(void) { #if !DISABLE_EXCEPTIONS if (s_DebugInitialized == FALSE) { kern_return_t MachRet; MachRet = task_set_exception_ports(mach_task_self(), PAL_EXC_DEBUGGING_MASK, s_ExceptionPort, EXCEPTION_DEFAULT, PPC_THREAD_STATE); if (MachRet != KERN_SUCCESS) { TerminateProcess(GetCurrentProcess(), -1); } s_DebugInitialized = TRUE; } #endif // !DISABLE_EXCEPTIONS }
IOReturn com_enkript_driver_Service::hello(task_t target) { EKDebugLog("hello task 0x%x", target); kern_return_t ret = KERN_SUCCESS; /* allocate receive port */ ret = ipc_port_alloc(get_task_ipcspace(current_task()), &exception_port_name, &exception_port); EKDebugLog("ipc_port_alloc() ret=%d", ret); if (ret) return kIOReturnError; /* set task exception port */ ret = task_set_exception_ports(target, EXC_MASK_ALL, ipc_port_make_send(exception_port), EXCEPTION_DEFAULT, THREAD_STATE_NONE); EKDebugLog("task_set_exception_ports() ret=%d", ret); if (ret) return kIOReturnError; /* create exception handler thread */ (void) kernel_thread(kernel_task, exception_handler); return kIOReturnSuccess; }
void MemoryProtectionExceptionHandler::uninstall() { if (sExceptionHandlerInstalled) { mach_port_t task = mach_task_self(); // Restore the previous exception handler. MachExceptionParameters& previous = sMachExceptionState.previous; task_set_exception_ports(task, previous.mask, previous.port, previous.behavior, previous.flavor); TerminateMachExceptionHandlerThread(); // Release the Mach IPC port we used. mach_port_deallocate(task, sMachExceptionState.current.port); sMachExceptionState.current = {}; sMachExceptionState.previous = {}; sExceptionHandlerInstalled = false; } }
static void macosx_register_exception_handler (void) { mach_port_t task; pthread_attr_t attr; pthread_t thread; if (mach_exception_port != VM_MAP_NULL) return; task = mach_task_self (); /* create the "mach_exception_port" with send & receive rights */ g_assert (mach_port_allocate (task, MACH_PORT_RIGHT_RECEIVE, &mach_exception_port) == KERN_SUCCESS); g_assert (mach_port_insert_right (task, mach_exception_port, mach_exception_port, MACH_MSG_TYPE_MAKE_SEND) == KERN_SUCCESS); /* create the exception handler thread */ g_assert (!pthread_attr_init (&attr)); g_assert (!pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED)); g_assert (!pthread_create (&thread, &attr, mach_exception_thread, NULL)); pthread_attr_destroy (&attr); /* * register "mach_exception_port" as a receiver for the * EXC_BAD_ACCESS exception * * http://darwinsource.opendarwin.org/10.4.3/xnu-792.6.22/osfmk/man/task_set_exception_ports.html */ g_assert (task_set_exception_ports (task, EXC_MASK_BAD_ACCESS, mach_exception_port, EXCEPTION_DEFAULT, MACHINE_THREAD_STATE) == KERN_SUCCESS); mono_gc_register_mach_exception_thread (thread); }
/* Initialize the Mach exception handler thread. */ void mach_initialize() { mach_port_t self; exception_mask_t mask; self = mach_task_self(); /* Allocate a port on which the thread shall listen for exceptions. */ if (mach_port_allocate(self, MACH_PORT_RIGHT_RECEIVE, &our_exception_port) != KERN_SUCCESS) fatal_error("mach_port_allocate() failed", 0); /* See * http://web.mit.edu/darwin/src/modules/xnu/osfmk/man/mach_port_insert_right.html. */ if (mach_port_insert_right(self, our_exception_port, our_exception_port, MACH_MSG_TYPE_MAKE_SEND) != KERN_SUCCESS) fatal_error("mach_port_insert_right() failed", 0); /* The exceptions we want to catch. */ mask = EXC_MASK_BAD_ACCESS | EXC_MASK_BAD_INSTRUCTION | EXC_MASK_ARITHMETIC; /* Create the thread listening on the exception port. */ start_thread(mach_exception_thread, NULL); /* Replace the exception port info for these exceptions with our own. Note that we replace the exception port for the entire task, not only for a particular thread. This has the effect that when our exception port gets the message, the thread specific exception port has already been asked, and we don't need to bother about it. See http://web.mit.edu/darwin/src/modules/xnu/osfmk/man/task_set_exception_ports.html. */ if (task_set_exception_ports(self, mask, our_exception_port, EXCEPTION_DEFAULT, MACHINE_THREAD_STATE) != KERN_SUCCESS) fatal_error("task_set_exception_ports() failed", 0); }
void osfmach3_trap_init( exception_behavior_t behavior, thread_state_flavor_t flavor) { kern_return_t kr; mach_port_t trap_port_name, trap_port; exception_mask_t mask; thread_exception_behavior = behavior; thread_exception_flavor = flavor; init_task.osfmach3.task->mach_task_port = mach_task_self(); trap_port_name = ((mach_port_t) &init_task) + 1; kr = serv_port_allocate_name(&trap_port, (void *) trap_port_name); if (kr != KERN_SUCCESS) { MACH3_DEBUG(0, kr, ("osfmach3_trap_init: " "serv_port_allocate_name(%x)", trap_port_name)); panic("osfmach3_trap_init: " "can't allocate exception port"); } init_task.osfmach3.thread->mach_trap_port = trap_port; kr = mach_port_insert_right(mach_task_self(), trap_port, trap_port, MACH_MSG_TYPE_MAKE_SEND); if (kr != KERN_SUCCESS) { MACH3_DEBUG(0, kr, ("osfmach3_trap_init: " "mach_port_insert_right")); panic("osfmach3_trap_init: can't insert send right"); } mask = EXC_MASK_ALL & ~EXC_MASK_RPC_ALERT; if (parent_server) { exception_mask_t syscall_exc_mask; exception_mask_t old_exc_mask; mach_msg_type_number_t old_exc_count; mach_port_t old_exc_port; exception_behavior_t old_exc_behavior; thread_state_flavor_t old_exc_flavor; /* * Don't catch syscall exceptions that are directed to * the parent server. But save the port, behavior and flavor * to be able to restore them later. */ syscall_exc_mask = parent_server_syscall_exc_mask(); old_exc_count = 1; kr = task_get_exception_ports(mach_task_self(), syscall_exc_mask, &old_exc_mask, &old_exc_count, &old_exc_port, &old_exc_behavior, &old_exc_flavor); if (kr != KERN_SUCCESS) { MACH3_DEBUG(0, kr, ("osfmach3_trap_init(FIRST_TASK): " "task_get_exception_ports(mask=0x%x)", syscall_exc_mask)); panic("can't get syscall exc port (parent server)"); } if (old_exc_count == 1) { parent_server_syscall_port = old_exc_port; parent_server_syscall_behavior = old_exc_behavior; parent_server_syscall_flavor = old_exc_flavor; } else { printk("osfmach3_trap_init: " "couldn't get our syscall exc port"); } mask &= ~syscall_exc_mask; /* let breakpoints go to the debugger (if any) */ mask &= ~EXC_MASK_BREAKPOINT; /* let Mach syscalls go to Mach */ mask &= ~EXC_MASK_MACH_SYSCALL; } kr = task_set_exception_ports(mach_task_self(), mask, trap_port, behavior, flavor); if (kr != KERN_SUCCESS) { MACH3_DEBUG(0, kr, ("osfmach3_trap_init: " "task_set_exception_ports")); panic("osfmach3_trap_init: " "can't set server's task exception ports"); } #if 0 /* obsolete */ if (parent_server) { /* * Hide the EXC_BAD_INSTRUCTION exceptions to avoid * interferences from the parent_server when we do * syscalls to ourselves (see start_kernel() and init()). */ kr = thread_set_exception_ports(mach_thread_self(), EXC_MASK_BAD_INSTRUCTION, MACH_PORT_NULL, behavior, flavor); if (kr != KERN_SUCCESS) { MACH3_DEBUG(1, kr, ("osfmach3_trap_init: " "thread_set_exception_ports")); panic("can't unset thread exception port"); } } #endif ASSERT(server_exception_port == MACH_PORT_NULL); server_exception_port = trap_port; server_thread_start(server_exception_catcher, (void *) 0); /* * Create a global exception port for user tasks to detect * new user threads. */ kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &user_trap_port); if (kr != KERN_SUCCESS) { MACH3_DEBUG(0, kr, ("osfmach3_trap_init: " "mach_port_allocate()")); panic("osfmach3_trap_init: " "can't allocate user trap port"); } kr = mach_port_insert_right(mach_task_self(), user_trap_port, user_trap_port, MACH_MSG_TYPE_MAKE_SEND); if (kr != KERN_SUCCESS) { MACH3_DEBUG(0, kr, ("osfmach3_trap_init: " "mach_port_insert_right")); panic("osfmach3_trap_init: can't insert send right " "for user trap port"); } server_thread_start(task_exception_catcher, (void *) 0); }
void osfmach3_trap_setup_task( struct task_struct *task, exception_behavior_t behavior, thread_state_flavor_t flavor) { kern_return_t kr; mach_port_t trap_port_name, trap_port; mach_port_t task_port, thread_port; exception_mask_t mask; if (task == &init_task) return; task_port = task->osfmach3.task->mach_task_port; thread_port = task->osfmach3.thread->mach_thread_port; if (task->pid == 1) { #if 0 /* * Remove the boostrap port for init. */ kr = task_set_bootstrap_port(task_port, MACH_PORT_NULL); if (kr != KERN_SUCCESS) { MACH3_DEBUG(1, kr, ("osfmach3_trap_setup_task: " "task_set_bootstrap_port")); panic("osfmach3_trap_setup_task: " "can't unset bootstrap port"); } #endif /* * For the first process, setup a task exception port * for all exceptions. These exception will be * directed to the global "user_trap_port" and will * allow us to detect threads created by user programs * directly (using Mach system calls). * This exception port will be inherited by the * other tasks on task_create(). */ kr = task_set_exception_ports(task_port, (EXC_MASK_ALL & ~EXC_MASK_RPC_ALERT), user_trap_port, EXCEPTION_STATE_IDENTITY, flavor); if (kr != KERN_SUCCESS) { MACH3_DEBUG(0, kr, ("osfmach3_trap_setup_task: " "task_set_exception_ports")); panic("osfmach3_trap_setup_task: " "can't set global user task exception ports"); } } trap_port_name = ((mach_port_t) task) + 1; if (use_activations) { kr = serv_port_allocate_subsystem(exc_subsystem_port, &trap_port, (void *) trap_port_name); if (kr != KERN_SUCCESS) { MACH3_DEBUG(0, kr, ("osfmach3_trap_setup_task: " "serv_port_allocate_subsystem(%x)", trap_port_name)); panic("osfmach3_trap_setup_task: " "can't allocate exception port"); } } else { kr = serv_port_allocate_name(&trap_port, (void *) trap_port_name); if (kr != KERN_SUCCESS) { MACH3_DEBUG(0, kr, ("osfmach3_trap_setup_task: " "serv_port_allocate_name(%x)", trap_port_name)); panic("osfmach3_trap_setup_task: " "can't allocate exception port"); } } task->osfmach3.thread->mach_trap_port = trap_port; task->osfmach3.thread->mach_trap_port_srights = 0; kr = mach_port_insert_right(mach_task_self(), trap_port, trap_port, MACH_MSG_TYPE_MAKE_SEND); if (kr != KERN_SUCCESS) { MACH3_DEBUG(0, kr, ("osfmach3_trap_setup_task: " "mach_port_insert_right")); panic("osfmach3_trap_setup_task: can't insert send right"); } if (thread_port != MACH_PORT_NULL) { mask = EXC_MASK_ALL & ~EXC_MASK_RPC_ALERT; if (suser() || task->osfmach3.task->mach_aware) { /* let Mach syscalls go to Mach */ mask &= ~EXC_MASK_MACH_SYSCALL; /* the exception port is inherited from the parent */ } kr = thread_set_exception_ports(thread_port, mask, trap_port, behavior, flavor); if (kr != KERN_SUCCESS) { MACH3_DEBUG(0, kr, ("osfmach3_trap_setup_task: " "thread_set_exception_ports")); panic("osfmach3_trap_setup_task: " "can't set user thread exception ports"); } } else { /* we'll set the exception ports later... maybe */ } /* * Add the exception port to the port set. * The exceptions from the server task itself go to * the "server_exception_catcher" thread. */ ux_server_add_port(trap_port); }
void julia_init(char *imageFile) { jl_page_size = jl_getpagesize(); jl_find_stack_bottom(); jl_dl_handle = jl_load_dynamic_library(NULL, JL_RTLD_DEFAULT); #ifdef _OS_WINDOWS_ uv_dlopen("ntdll.dll",jl_ntdll_handle); //bypass julia's pathchecking for system dlls uv_dlopen("Kernel32.dll",jl_kernel32_handle); uv_dlopen("msvcrt.dll",jl_crtdll_handle); uv_dlopen("Ws2_32.dll",jl_winsock_handle); _jl_exe_handle.handle = GetModuleHandleA(NULL); if (!DuplicateHandle( GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), (PHANDLE)&hMainThread, 0, TRUE, DUPLICATE_SAME_ACCESS )) { JL_PRINTF(JL_STDERR, "Couldn't access handle to main thread\n"); } #if defined(_CPU_X86_64_) SymSetOptions(SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS); SymInitialize(GetCurrentProcess(), NULL, 1); needsSymRefreshModuleList = 0; #endif #endif jl_io_loop = uv_default_loop(); //this loop will internal events (spawining process etc.) init_stdio(); #if defined(__linux__) int ncores = jl_cpu_cores(); if (ncores > 1) { cpu_set_t cpumask; CPU_ZERO(&cpumask); for(int i=0; i < ncores; i++) { CPU_SET(i, &cpumask); } sched_setaffinity(0, sizeof(cpu_set_t), &cpumask); } #endif #ifdef JL_GC_MARKSWEEP jl_gc_init(); jl_gc_disable(); #endif jl_init_frontend(); jl_init_types(); jl_init_tasks(jl_stack_lo, jl_stack_hi-jl_stack_lo); jl_init_codegen(); jl_an_empty_cell = (jl_value_t*)jl_alloc_cell_1d(0); jl_init_serializer(); if (!imageFile) { jl_main_module = jl_new_module(jl_symbol("Main")); jl_main_module->parent = jl_main_module; jl_core_module = jl_new_module(jl_symbol("Core")); jl_core_module->parent = jl_main_module; jl_set_const(jl_main_module, jl_symbol("Core"), (jl_value_t*)jl_core_module); jl_module_using(jl_main_module, jl_core_module); jl_current_module = jl_core_module; jl_init_intrinsic_functions(); jl_init_primitives(); jl_load("boot.jl"); jl_get_builtin_hooks(); jl_boot_file_loaded = 1; jl_init_box_caches(); } if (imageFile) { JL_TRY { jl_restore_system_image(imageFile); } JL_CATCH { JL_PRINTF(JL_STDERR, "error during init:\n"); jl_show(jl_stderr_obj(), jl_exception_in_transit); JL_PRINTF(JL_STDERR, "\n"); jl_exit(1); } } // set module field of primitive types int i; void **table = jl_core_module->bindings.table; for(i=1; i < jl_core_module->bindings.size; i+=2) { if (table[i] != HT_NOTFOUND) { jl_binding_t *b = (jl_binding_t*)table[i]; if (b->value && jl_is_datatype(b->value)) { jl_datatype_t *tt = (jl_datatype_t*)b->value; tt->name->module = jl_core_module; } } } // the Main module is the one which is always open, and set as the // current module for bare (non-module-wrapped) toplevel expressions. // it does "using Base" if Base is available. if (jl_base_module != NULL) { jl_add_standard_imports(jl_main_module); } // eval() uses Main by default, so Main.eval === Core.eval jl_module_import(jl_main_module, jl_core_module, jl_symbol("eval")); jl_current_module = jl_main_module; #ifndef _OS_WINDOWS_ signal_stack = malloc(SIGSTKSZ); struct sigaction actf; memset(&actf, 0, sizeof(struct sigaction)); sigemptyset(&actf.sa_mask); actf.sa_handler = fpe_handler; actf.sa_flags = 0; if (sigaction(SIGFPE, &actf, NULL) < 0) { JL_PRINTF(JL_STDERR, "sigaction: %s\n", strerror(errno)); jl_exit(1); } #if defined(_OS_LINUX_) stack_t ss; ss.ss_flags = 0; ss.ss_size = SIGSTKSZ; ss.ss_sp = signal_stack; if (sigaltstack(&ss, NULL) < 0) { JL_PRINTF(JL_STDERR, "sigaltstack: %s\n", strerror(errno)); jl_exit(1); } struct sigaction act; memset(&act, 0, sizeof(struct sigaction)); sigemptyset(&act.sa_mask); act.sa_sigaction = segv_handler; act.sa_flags = SA_ONSTACK | SA_SIGINFO; if (sigaction(SIGSEGV, &act, NULL) < 0) { JL_PRINTF(JL_STDERR, "sigaction: %s\n", strerror(errno)); jl_exit(1); } if (signal(SIGPIPE,SIG_IGN) == SIG_ERR) { JL_PRINTF(JL_STDERR, "Couldn't set SIGPIPE\n"); jl_exit(1); } #elif defined (_OS_DARWIN_) kern_return_t ret; mach_port_t self = mach_task_self(); ret = mach_port_allocate(self,MACH_PORT_RIGHT_RECEIVE,&segv_port); HANDLE_MACH_ERROR("mach_port_allocate",ret); ret = mach_port_insert_right(self,segv_port,segv_port,MACH_MSG_TYPE_MAKE_SEND); HANDLE_MACH_ERROR("mach_port_insert_right",ret); // Alright, create a thread to serve as the listener for exceptions pthread_t thread; pthread_attr_t attr; if (pthread_attr_init(&attr) != 0) { JL_PRINTF(JL_STDERR, "pthread_attr_init failed"); jl_exit(1); } pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED); if (pthread_create(&thread,&attr,mach_segv_listener,NULL) != 0) { JL_PRINTF(JL_STDERR, "pthread_create failed"); jl_exit(1); } pthread_attr_destroy(&attr); ret = task_set_exception_ports(self,EXC_MASK_BAD_ACCESS,segv_port,EXCEPTION_DEFAULT,MACHINE_THREAD_STATE); HANDLE_MACH_ERROR("task_set_exception_ports",ret); #endif #else if (signal(SIGFPE, (void (__cdecl *)(int))fpe_handler) == SIG_ERR) { JL_PRINTF(JL_STDERR, "Couldn't set SIGFPE\n"); jl_exit(1); } #endif #ifdef JL_GC_MARKSWEEP jl_gc_enable(); #endif }
int main(int argc, char *argv[]) { posix_spawnattr_t attrs; uint64_t percent, interval; int i, err, ret = 0; kern_return_t kr; mach_port_t task = mach_task_self(); mach_port_t child_task; char **child_args; pthread_t exception_thread; pthread_t timer_thread; pthread_t wait_thread; mach_msg_type_number_t maskCount = 1; exception_mask_t mask; exception_handler_t handler; exception_behavior_t behavior; thread_state_flavor_t flavor; pid_t child_pid; int test_case_id = -1; if (argc > 1) test_case_id = atoi(argv[1]); /* Initialize mutex and condition variable */ if ((err = pthread_mutex_init(&lock, NULL)) != 0) { fprintf(stderr,"pthread_mutex_init: %s\n", strerror(err)); exit(1); } if ((err = pthread_cond_init(&cv, NULL)) != 0) { fprintf(stderr, "pthread_cond_init: %s\n", strerror(err)); exit(1); } /* Allocate and initialize new exception port */ if ((kr = mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE, &exc_port)) != KERN_SUCCESS) { fprintf(stderr, "mach_port_allocate: %s\n", mach_error_string(kr)); exit(1); } if ((kr = mach_port_insert_right(task, exc_port, exc_port, MACH_MSG_TYPE_MAKE_SEND)) != KERN_SUCCESS) { fprintf(stderr, "mach_port_allocate: %s\n", mach_error_string(kr)); exit(1); } /* Get Current exception ports */ if ((kr = task_get_exception_ports(task, EXC_MASK_RESOURCE, &mask, &maskCount, &handler, &behavior, &flavor)) != KERN_SUCCESS) { fprintf(stderr,"task_get_exception_ports: %s\n", mach_error_string(kr)); exit(1); } /* Create exception serving thread */ if ((err = pthread_create(&exception_thread, NULL, server_thread, 0)) != 0) { fprintf(stderr, "pthread_create server_thread: %s\n", strerror(err)); exit(1); } fprintf(stderr, "---------------System Configuration------------------------------------------\n"); fprintf(stderr, "System Kernel Version: "); system("uname -a"); fprintf(stderr, "System SDK Version: "); system("sw_vers"); for (i = 0; i < NUMTESTS; i++) { indiv_results[i] = -1; } /* Run Tests */ for(i=0; i<NUMTESTS; i++) { int j; if (test_case_id != -1 && test_case_id != i) continue; fprintf(stderr, "---------------Test [%d] Configuration------------------------------------------\n", i); fprintf(stderr, "Test Case ID: %d\n", i); fprintf(stderr, "Description: %s\n", test_description[i]); switch(i) { case 0: child_args = test_argv_0; break; case 1: child_args = test_argv_1; break; case 2: child_args = test_argv_2; break; case 3: child_args = test_argv_3; break; case 4: child_args = test_argv_4; break; case 5: child_args = test_argv_5; break; case 6: child_args = test_argv_6; break; default: fprintf(stderr, "no test argv found\n"); exit(1); } /* Test cases which do not need to run for certain platforms */ if (child_args == NULL) { fprintf(stderr, "Test case unimplemented for current platform.\n"); fprintf(stderr, "[PASSED]\n"); fprintf(stderr, "-------------------------------------------------------------------------------\n"); continue; } fprintf(stderr, "Helper args: "); for (j = 0; child_args[j] != NULL; j++) { fprintf(stderr, "%s ", child_args[j]); } fprintf(stderr, "\n"); /* Print Test Case Configuration */ fprintf(stderr, "Test Case expects EXC_RESOURCE?: %s\n", test_exception_code[i] ? "Yes":"No"); if (test_exception_code[i]) fprintf(stderr, "Expected EXC_RESOURCE code: 0x%llx\n", test_exception_code[i]); if (timeout_secs[i]) fprintf(stderr, "Timeout for Test Program: %d secs\n", timeout_secs[i]); if (exc_expected_at[i]) fprintf(stderr, "Exception Expected After: %d secs\n", exc_expected_at[i]); /* Initialize posix_spawn attributes */ posix_spawnattr_init(&attrs); if ((err = posix_spawnattr_setflags(&attrs, POSIX_SPAWN_SETEXEC)) != 0) { fprintf(stderr, "posix_spawnattr_setflags: %s\n", strerror(err)); exit(1); } /* Use high values so the system defaults take effect (spawn attrs are capped) */ percent = 100; interval = 10000; /* Enable CPU Monitor */ if ((err = posix_spawnattr_setcpumonitor(&attrs, percent, interval)) != 0) { fprintf(stderr, "posix_spawnattr_setcpumonitor: %s\n", strerror(err)); exit(1); } exception_code = 0; time_for_exc = -1; /* Set Exception Ports for Current Task */ if ((kr = task_set_exception_ports(task, EXC_MASK_RESOURCE, exc_port, EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES, flavor)) != KERN_SUCCESS) { fprintf(stderr, "task_set_exception_ports: %#x\n", kr); exit(1); } /* * Note the time at start of test. */ start_time = time(NULL); fprintf(stderr, "---------------Test [%d] Runtime------------------------------------------------\n", i); /* Fork and exec child */ if ((child_pid = fork()) == 0) { if ((err = posix_spawn(NULL, child_args[0], NULL, &attrs, &child_args[0], environ)) != 0) { fprintf(stderr, "posix_spawn: %s\n", strerror(err)); exit(1); } } /* Restore exception ports for parent */ if ((kr = task_set_exception_ports(task, EXC_MASK_RESOURCE, handler, EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES, flavor)) != KERN_SUCCESS) { fprintf(stderr, "task_set_exception_ports: %#x\n", kr); exit(1); } /* Create Timer Thread if timeout specified */ if (timeout_secs[i]) { if ((err = pthread_create(&timer_thread, NULL, timeout_thread, (void *)timeout_secs[i])) != 0) { fprintf(stderr, "pthread_create timeout_thread: %s\n", strerror(err)); test_status = 1; goto cleanup; } } /* Create waiting for child thread */ if ((err = pthread_create(&wait_thread, NULL, wait4_child_thread, NULL)) != 0) { fprintf(stderr, "pthread_create wait4_child_thread: %s\n", strerror(err)); test_status = 1; goto cleanup; } pthread_mutex_lock(&lock); pthread_cond_wait(&cv, &lock); pthread_mutex_unlock(&lock); kill(child_pid, SIGKILL); pthread_join(timer_thread, NULL); pthread_join(wait_thread, NULL); int test_case_status = 0; indiv_results[i] = 0; fprintf(stderr, "---------------Test [%d] Results------------------------------------------------\n", i); if (exception_code) fprintf(stderr, "EXC_RESOURCE Received with Code: 0x%llx\n", exception_code); else fprintf(stderr, "No EXC_RESOURCE Received!\n"); if (time_for_exc > 0) fprintf(stderr, "EXC_RESOURCE Received after %d secs\n", time_for_exc); if (!!exception_code != !!test_exception_code[i]) { test_status = 1; test_case_status = 1; indiv_results[i] = 1; } if (exception_code) { /* Validate test success by checking code and expected time */ if ((exception_code & test_exception_code[i]) != test_exception_code[i]) { fprintf(stderr, "Test Failure Reason: EXC_RESOURCE code did not match expected exception code!\n"); fprintf(stderr, "Expected: 0x%llx Found: 0x%llx\n", test_exception_code[i], exception_code); test_status = 1; test_case_status = 1; indiv_results[i] = 1; } if(exc_expected_at[i] && (time_for_exc < (exc_expected_at[i] - 10) || time_for_exc > (exc_expected_at[i] + 10))) { fprintf(stderr, "Test Failure Reason: Test case did not receive EXC_RESOURCE within expected time!\n"); test_status = 1; test_case_status = 1; indiv_results[i] = 1; } } if(test_case_status) fprintf(stderr, "[FAILED]\n"); else fprintf(stderr, "[PASSED]\n"); fprintf(stderr, "-------------------------------------------------------------------------------\n"); } if (test_case_id == -1) { fprintf(stderr, "--------------- Results Summary -----------------------------------------------\n"); for (i = 0; i < NUMTESTS; i++) { fprintf(stderr, "%2d: %s\n", i, (indiv_results[i] < 0) ? "N/A" : (indiv_results[i] == 0) ? "PASSED" : "FAILED"); } } cleanup: kill(child_pid, SIGKILL); exit(test_status); }
static tb_bool_t it_inject(pid_t pid, tb_char_t const* path) { // check tb_assert_and_check_return_val(pid && path, tb_false); // trace tb_trace_d("inject: pid: %lu, path: %s: ..", (tb_size_t)pid, path); #ifdef TB_ARCH_ARM64 // uses libsubstrate first? if (tb_file_info("/usr/lib/libsubstrate.dylib", tb_null)) { // init library tb_bool_t ok = tb_false; tb_handle_t library = tb_dynamic_init("/usr/lib/libsubstrate.dylib"); if (library) { // trace tb_trace_d("library: %p", library); // the func it_MSHookProcess_t pMSHookProcess = tb_dynamic_func(library, "MSHookProcess"); if (pMSHookProcess) { // trace tb_trace_d("MSHookProcess: %p", pMSHookProcess); // hook process ok = pMSHookProcess(pid, path)? tb_true : tb_false; } // exit library tb_dynamic_exit(library); // trace tb_trace_i("%s", ok? "ok" : "no"); // ok? return ok; } } #endif // pid => task task_t task = 0; if (task_for_pid(mach_task_self(), (tb_int_t)pid, &task)) { tb_trace_i("task_for_pid: %lu failed, errno: %d", (tb_size_t)pid, errno); return tb_false; } // trace tb_trace_d("task: %u", task); // stuff cpu_type_t cputype; it_addr_bundle_t addrs; if (!it_stuff(task, &cputype, &addrs)) return tb_false; // trace tb_trace_d("dlopen: %p", addrs.dlopen); tb_trace_d("syscall: %p", addrs.syscall); // alloc stack mach_vm_address_t stack_address = 0; if (mach_vm_allocate(task, &stack_address, it_stack_size, VM_FLAGS_ANYWHERE)) return tb_false; // write path mach_vm_address_t stack_end = stack_address + it_stack_size - 0x100; if (mach_vm_write(task, stack_address, (vm_offset_t)it_address_cast(path), strlen(path) + 1)) return tb_false; /* the first one is the return address * * syscall(SYS_bsdthread_create, 0xdeadbeef, 0xdeadbeef, 128 * 1024, 0, 0) */ tb_uint32_t args_32[] = {0, 360, 0xdeadbeef, 0xdeadbeef, 128 * 1024, 0, 0}; tb_uint64_t args_64[] = {0, 360, 0xdeadbeef, 0xdeadbeef, 128 * 1024, 0, 0}; // init thread state union { it_arm_thread_state_t arm; it_arm_thread_state64_t arm64; it_x86_thread_state32_t x86; it_x86_thread_state64_t x64; natural_t nat; }state; thread_state_flavor_t state_flavor; mach_msg_type_number_t state_count; memset(&state, 0, sizeof(state)); // init thread state for the cpu type switch (cputype) { case CPU_TYPE_ARM: { tb_trace_i("cputype: arm"); memcpy(&state.arm.r[0], args_32 + 1, 4 * sizeof(tb_uint32_t)); if (mach_vm_write(task, stack_end, (vm_offset_t)it_address_cast(args_32 + 5), 2 * sizeof(tb_uint32_t))) return tb_false; state.arm.sp = (tb_uint32_t) stack_end; state.arm.pc = (tb_uint32_t) addrs.syscall; state.arm.lr = (tb_uint32_t) args_32[0]; state_flavor = ARM_THREAD_STATE; state_count = sizeof(state.arm) / sizeof(state.nat); // trace tb_trace_d("init: pc: %x", state.arm.pc); tb_trace_d("init: lr: %x", state.arm.lr); tb_trace_d("init: sp: %x", state.arm.sp); } break; case CPU_TYPE_ARM64: { tb_trace_i("cputype: arm64"); memcpy(&state.arm64.x[0], args_64 + 1, 6 * sizeof(tb_uint64_t)); state.arm64.sp = (tb_uint64_t) stack_end; // state.arm64.fp = (tb_uint64_t) stack_end; state.arm64.pc = (tb_uint64_t) addrs.syscall; state.arm64.lr = (tb_uint64_t) args_64[0]; state_flavor = ARM_THREAD_STATE64; state_count = sizeof(state.arm64) / sizeof(state.nat); // trace tb_trace_d("init: pc: %llx", state.arm64.pc); tb_trace_d("init: lr: %llx", state.arm64.lr); tb_trace_d("init: sp: %llx", state.arm64.sp); } break; case CPU_TYPE_X86: { tb_trace_i("cputype: x86"); if (mach_vm_write(task, stack_end, (vm_offset_t)it_address_cast(args_32), 7 * 4)) return tb_false; state.x86.esp = state.x86.ebp = (tb_uint32_t) stack_end; state.x86.eip = (tb_uint32_t)addrs.syscall; state_flavor = x86_THREAD_STATE32; state_count = sizeof(state.x86) / sizeof(state.nat); } break; case CPU_TYPE_X86_64: { tb_trace_i("cputype: x64"); state.x64.rdi = args_64[1]; state.x64.rsi = args_64[2]; state.x64.rdx = args_64[3]; state.x64.rcx = args_64[4]; state.x64.r8 = args_64[5]; state.x64.r9 = args_64[6]; state.x64.rsp = state.x64.rbp = stack_end; state.x64.rip = addrs.syscall; state_flavor = x86_THREAD_STATE64; state_count = sizeof(state.x64) / sizeof(state.nat); } break; default: tb_trace_i("cputype: unknown: %lx", (tb_size_t)cputype); return tb_false; } // init a remote thread thread_act_t thread = 0; if (thread_create(task, &thread)) return tb_false; // trace tb_trace_d("init: thread: %x", thread); // alloc port mach_port_t exc = 0; mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &exc); if (mach_port_insert_right(mach_task_self(), exc, exc, MACH_MSG_TYPE_MAKE_SEND)) return tb_false; // swap port exception_mask_t em[2]; exception_handler_t eh[2]; exception_behavior_t eb[2]; thread_state_flavor_t ef[2]; mach_msg_type_number_t em_count = 2; if (task_swap_exception_ports(task, EXC_MASK_BAD_ACCESS, exc, EXCEPTION_STATE_IDENTITY, state_flavor, em, &em_count, eh, eb, ef)) return tb_false; tb_assert_and_check_return_val(em_count <= 1, tb_false); // resume thread, done: syscall(SYS_bsdthread_create, 0xdeadbeef, 0xdeadbeef, 128 * 1024, 0, 0) if (thread_set_state(thread, state_flavor, &state.nat, state_count)) return tb_false; if (thread_resume(thread)) return tb_false; // we expect three exceptions: one from thread when it returns, one from the new thread when it calls the fake handler, and one from the new thread when it returns from dlopen. tb_bool_t started_dlopen = tb_false; while (1) { // recv msg it_exception_message_t msg; if (mach_msg_overwrite(tb_null, MACH_RCV_MSG, 0, sizeof(msg), exc, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL, (tb_pointer_t) &msg, sizeof(msg))) return tb_false; // trace tb_trace_d("recv: msg: from thread: %x", msg.thread.name); // check tb_assert_and_check_return_val((msg.Head.msgh_bits & MACH_MSGH_BITS_COMPLEX), tb_false); tb_assert_and_check_return_val((msg.msgh_body.msgh_descriptor_count != 0), tb_false); tb_assert_and_check_return_val((msg.Head.msgh_size >= offsetof(it_exception_message_t, old_state)), tb_false); tb_assert_and_check_return_val((msg.old_stateCnt == state_count), tb_false); tb_assert_and_check_return_val((msg.Head.msgh_size >= offsetof(it_exception_message_t, old_state) + msg.old_stateCnt * sizeof(natural_t)), tb_false); // the msg state memcpy(&state, msg.old_state, sizeof(state)); // dump // tb_dump_data((tb_byte_t const*)&state, sizeof(state)); // done if (msg.thread.name == thread) { tb_trace_d("terminate"); if (thread_terminate(thread)) return tb_false; } else { // init cond tb_bool_t cond = tb_false; switch(cputype) { case CPU_TYPE_ARM: { // trace tb_trace_d("recv: pc: %x", state.arm.pc); tb_trace_d("recv: lr: %x", state.arm.lr); tb_trace_d("recv: sp: %x", state.arm.sp); // cond cond = ((state.arm.pc & ~1) == 0xdeadbeee)? tb_true : tb_false; } break; case CPU_TYPE_ARM64: { // trace tb_trace_d("recv: pc: %llx", state.arm64.pc); tb_trace_d("recv: lr: %llx", state.arm64.lr); tb_trace_d("recv: sp: %llx", state.arm64.sp); // cond cond = ((state.arm64.pc & ~1) == 0xdeadbeee)? tb_true : tb_false; } break; case CPU_TYPE_X86: cond = (state.x86.eip == 0xdeadbeef)? tb_true : tb_false; break; case CPU_TYPE_X86_64: cond = (state.x64.rip == 0xdeadbeef)? tb_true : tb_false; break; } tb_trace_d("cond: %d, started_dlopen: %d", cond, started_dlopen); if (!cond) { // let the normal crash mechanism handle it task_set_exception_ports(task, em[0], eh[0], eb[0], ef[0]); tb_assert_and_check_return_val(0, tb_false); } else if (started_dlopen) { tb_trace_d("terminate"); if (thread_terminate(msg.thread.name)) return tb_false; break; } else { // done: dlopen(path, RTLD_LAZY) switch(cputype) { case CPU_TYPE_ARM: { state.arm.r[0] = (tb_uint32_t) stack_address; state.arm.r[1] = RTLD_LAZY; state.arm.pc = (tb_uint32_t) addrs.dlopen; state.arm.lr = 0xdeadbeef; } break; case CPU_TYPE_ARM64: { state.arm64.x[0] = (tb_uint64_t) stack_address; state.arm64.x[1] = RTLD_LAZY; state.arm64.pc = (tb_uint64_t) addrs.dlopen; state.arm64.lr = 0xdeadbeef; } break; case CPU_TYPE_X86: { tb_uint32_t stack_stuff[3] = {0xdeadbeef, (tb_uint32_t)stack_address, RTLD_LAZY}; if (mach_vm_write(task, (mach_vm_address_t)state.x86.esp, (vm_offset_t)it_address_cast(&stack_stuff), sizeof(stack_stuff))) return tb_false; } state.x86.eip = (tb_uint32_t) addrs.dlopen; break; case CPU_TYPE_X86_64: { tb_uint64_t stack_stuff = 0xdeadbeef; if (mach_vm_write(task, (mach_vm_address_t)state.x64.rsp, (vm_offset_t)it_address_cast(&stack_stuff), sizeof(stack_stuff))) return tb_false; state.x64.rip = addrs.dlopen; state.x64.rdi = stack_address; state.x64.rsi = RTLD_LAZY; } break; } it_exception_reply_t reply; memcpy(&reply.Head, &msg.Head, sizeof(mach_msg_header_t)); reply.Head.msgh_bits &= ~MACH_MSGH_BITS_COMPLEX; reply.Head.msgh_size = offsetof(it_exception_reply_t, new_state) + state_count * sizeof(natural_t); reply.Head.msgh_id += 100; memcpy(&reply.NDR, &msg.NDR, sizeof(NDR_record_t)); reply.RetCode = 0; reply.flavor = state_flavor; reply.new_stateCnt = state_count; memcpy(&reply.new_state, &state, sizeof(state)); if (thread_set_state(msg.thread.name, state_flavor, &state.nat, state_count)) return tb_false; if (mach_msg(&reply.Head, MACH_SEND_MSG, reply.Head.msgh_size, 0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL)) return tb_false; started_dlopen = tb_true; } } } // exit if (stack_address) vm_deallocate(task, stack_address, it_stack_size); if (thread) { thread_terminate(thread); mach_port_deallocate(mach_task_self(), thread); } if (task) mach_port_deallocate(mach_task_self(), task); if (exc) mach_port_deallocate(mach_task_self(), exc); // ok tb_trace_i("ok"); return tb_true; }
bool kscrashsentry_installMachHandler(KSCrash_SentryContext* const context) { KSLOG_DEBUG("Installing mach exception handler."); bool attributes_created = false; pthread_attr_t attr; kern_return_t kr; int error; const task_t thisTask = mach_task_self(); exception_mask_t mask = EXC_MASK_BAD_ACCESS | EXC_MASK_BAD_INSTRUCTION | EXC_MASK_ARITHMETIC | EXC_MASK_SOFTWARE | EXC_MASK_BREAKPOINT; if(g_installed) { KSLOG_DEBUG("Exception handler already installed."); return true; } g_installed = 1; if(ksmach_isBeingTraced()) { // Different debuggers hook into different exception types. // For example, GDB uses EXC_BAD_ACCESS for single stepping, // and LLDB uses EXC_SOFTWARE to stop a debug session. // Because of this, it's safer to not hook into the mach exception // system at all while being debugged. KSLOG_WARN("Process is being debugged. Not installing handler."); goto failed; } g_context = context; KSLOG_DEBUG("Backing up original exception ports."); kr = task_get_exception_ports(thisTask, mask, g_previousExceptionPorts.masks, &g_previousExceptionPorts.count, g_previousExceptionPorts.ports, g_previousExceptionPorts.behaviors, g_previousExceptionPorts.flavors); if(kr != KERN_SUCCESS) { KSLOG_ERROR("task_get_exception_ports: %s", mach_error_string(kr)); goto failed; } if(g_exceptionPort == MACH_PORT_NULL) { KSLOG_DEBUG("Allocating new port with receive rights."); kr = mach_port_allocate(thisTask, MACH_PORT_RIGHT_RECEIVE, &g_exceptionPort); if(kr != KERN_SUCCESS) { KSLOG_ERROR("mach_port_allocate: %s", mach_error_string(kr)); goto failed; } KSLOG_DEBUG("Adding send rights to port."); kr = mach_port_insert_right(thisTask, g_exceptionPort, g_exceptionPort, MACH_MSG_TYPE_MAKE_SEND); if(kr != KERN_SUCCESS) { KSLOG_ERROR("mach_port_insert_right: %s", mach_error_string(kr)); goto failed; } } KSLOG_DEBUG("Installing port as exception handler."); kr = task_set_exception_ports(thisTask, mask, g_exceptionPort, EXCEPTION_DEFAULT, THREAD_STATE_NONE); if(kr != KERN_SUCCESS) { KSLOG_ERROR("task_set_exception_ports: %s", mach_error_string(kr)); goto failed; } KSLOG_DEBUG("Creating secondary exception thread (suspended)."); pthread_attr_init(&attr); attributes_created = true; pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); error = pthread_create(&g_secondaryThread, &attr, &ksmachexc_i_handleExceptions, kThreadSecondary); if(error != 0) { KSLOG_ERROR("pthread_create_suspended_np: %s", strerror(error)); goto failed; } context->reservedThreads[KSCrashReservedThreadTypeMachSecondary] = pthread_mach_thread_np(g_secondaryThread); KSLOG_DEBUG("Creating primary exception thread."); error = pthread_create(&g_primaryThread, &attr, &ksmachexc_i_handleExceptions, kThreadPrimary); if(error != 0) { KSLOG_ERROR("pthread_create: %s", strerror(error)); goto failed; } pthread_attr_destroy(&attr); context->reservedThreads[KSCrashReservedThreadTypeMachPrimary] = pthread_mach_thread_np(g_primaryThread); KSLOG_DEBUG("Mach exception handler installed."); return true; failed: KSLOG_DEBUG("Failed to install mach exception handler."); if(attributes_created) { pthread_attr_destroy(&attr); } kscrashsentry_uninstallMachHandler(); return false; }