/** 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; }
/** Our custom signal handler. * Restore the default signal handlers, record the signal information, and * write a crash report. * Once we're done, re-raise the signal and let the default handlers deal with * it. * * @param sigNum The signal that was raised. * * @param signalInfo Information about the signal. * * @param userContext Other contextual information. */ static void handleSignal(int sigNum, siginfo_t* signalInfo, void* userContext) { KSLOG_DEBUG("Trapped signal %d", sigNum); if(g_isEnabled) { ksmc_suspendEnvironment(); kscm_notifyFatalExceptionCaptured(false); KSLOG_DEBUG("Filling out context."); KSMC_NEW_CONTEXT(machineContext); ksmc_getContextForSignal(userContext, machineContext); kssc_initWithMachineContext(&g_stackCursor, 10000, machineContext); KSCrash_MonitorContext* crashContext = &g_monitorContext; memset(crashContext, 0, sizeof(*crashContext)); crashContext->crashType = KSCrashMonitorTypeSignal; crashContext->eventID = g_eventID; crashContext->offendingMachineContext = machineContext; crashContext->registersAreValid = true; crashContext->faultAddress = (uintptr_t)signalInfo->si_addr; crashContext->signal.userContext = userContext; crashContext->signal.signum = signalInfo->si_signo; crashContext->signal.sigcode = signalInfo->si_code; crashContext->stackCursor = &g_stackCursor; kscm_handleException(crashContext); ksmc_resumeEnvironment(); kscm_innerHandleSignal(signalInfo); kscm_handleSignal(signalInfo); } KSLOG_DEBUG("Re-raising signal for regular handlers to catch."); // This is technically not allowed, but it works in OSX and iOS. raise(sigNum); }
KSCrashType kscrashsentry_installWithContext(KSCrash_SentryContext* context, KSCrashType crashTypes) { KSLOG_DEBUG("Installing handlers with context %p, crash types 0x%x.", context, crashTypes); g_context = context; context->handlingCrash = false; KSCrashType installed = 0; if((crashTypes & KSCrashTypeMachException) && kscrashsentry_installMachHandler(context)) { installed |= KSCrashTypeMachException; } if((crashTypes & KSCrashTypeSignal) && kscrashsentry_installSignalHandler(context)) { installed |= KSCrashTypeSignal; } if((crashTypes & KSCrashTypeNSException) && kscrashsentry_installNSExceptionHandler(context)) { installed |= KSCrashTypeNSException; } KSLOG_DEBUG("Installation complete. Installed types 0x%x.", installed); return installed; }
KSCrashType kscrashsentry_installWithContext(KSCrash_SentryContext* context, KSCrashType crashTypes, void (*onCrash)(void)) { KSLOG_DEBUG("Installing handlers with context %p, crash types 0x%x.", context, crashTypes); g_context = context; kscrashsentry_clearContext(g_context); g_context->onCrash = onCrash; KSCrashType installed = 0; for(size_t i = 0; i < g_sentriesCount; i++) { CrashSentry* sentry = &g_sentries[i]; if(sentry->crashType & crashTypes) { if(sentry->install == NULL || sentry->install(context)) { installed |= sentry->crashType; } } } KSLOG_DEBUG("Installation complete. Installed types 0x%x.", installed); return installed; }
KSCrashType kscrash_install(const char* const crashReportFilePath, const char* const recrashReportFilePath, const char* stateFilePath, const char* crashID) { KSLOG_DEBUG("Installing crash reporter."); KSCrash_Context* context = crashContext(); if(g_installed) { KSLOG_DEBUG("Crash reporter already installed."); return context->config.handlingCrashTypes; } g_installed = 1; ksmach_init(); ksobjc_init(); kscrash_reinstall(crashReportFilePath, recrashReportFilePath, stateFilePath, crashID); KSCrashType crashTypes = kscrash_setHandlingCrashTypes(context->config.handlingCrashTypes); context->config.systemInfoJSON = kssysteminfo_toJSON(); context->config.processName = kssysteminfo_copyProcessName(); KSLOG_DEBUG("Installation complete."); return crashTypes; }
void kscrashsentry_uninstallMachHandler(void) { KSLOG_DEBUG("Uninstalling mach exception handler."); if(!g_installed) { KSLOG_DEBUG("Mach exception handler was already uninstalled."); return; } // NOTE: Do not deallocate the exception port. If a secondary crash occurs // it will hang the process. ksmachexc_i_restoreExceptionPorts(); thread_t thread_self = mach_thread_self(); if(g_primaryThread != 0 && pthread_mach_thread_np(g_primaryThread) != thread_self) { KSLOG_DEBUG("Cancelling primary exception thread."); pthread_cancel(g_primaryThread); g_primaryThread = 0; } if(g_secondaryThread != 0 && pthread_mach_thread_np(g_secondaryThread) != thread_self) { KSLOG_DEBUG("Cancelling secondary exception thread."); pthread_cancel(g_secondaryThread); g_secondaryThread = 0; } KSLOG_DEBUG("Mach exception handlers uninstalled."); g_installed = 0; }
void kscrashsentry_reportUserException(const char* name, const char* reason, const char* language, const char* lineOfCode, const char* stackTrace, bool terminateProgram) { if(g_context == NULL) { KSLOG_WARN("User-reported exception sentry is not installed. Exception has not been recorded."); } else { kscrashsentry_beginHandlingCrash(g_context); KSLOG_DEBUG("Suspending all threads"); kscrashsentry_suspendThreads(); KSLOG_DEBUG("Fetching call stack."); int callstackCount = 100; uintptr_t callstack[callstackCount]; callstackCount = backtrace((void**)callstack, callstackCount); if(callstackCount <= 0) { KSLOG_ERROR("backtrace() returned call stack length of %d", callstackCount); callstackCount = 0; } KSLOG_DEBUG("Filling out context."); g_context->crashType = KSCrashTypeUserReported; g_context->offendingThread = ksmach_thread_self(); g_context->registersAreValid = false; g_context->crashReason = reason; g_context->stackTrace = callstack; g_context->stackTraceLength = callstackCount; g_context->userException.name = name; g_context->userException.language = language; g_context->userException.lineOfCode = lineOfCode; g_context->userException.customStackTrace = stackTrace; KSLOG_DEBUG("Calling main crash handler."); g_context->onCrash(); if(terminateProgram) { kscrashsentry_uninstall(KSCrashTypeAll); kscrashsentry_resumeThreads(); abort(); } else { kscrashsentry_clearContext(g_context); kscrashsentry_resumeThreads(); } } }
static void uninstallSignalHandler(void) { KSLOG_DEBUG("Uninstalling signal handlers."); const int* fatalSignals = kssignal_fatalSignals(); int fatalSignalsCount = kssignal_numFatalSignals(); for(int i = 0; i < fatalSignalsCount; i++) { KSLOG_DEBUG("Restoring original handler for signal %d", fatalSignals[i]); sigaction(fatalSignals[i], &g_previousSignalHandlers[i], NULL); } KSLOG_DEBUG("Signal handlers uninstalled."); }
static inline bool getThreadList(KSMachineContext* context) { const task_t thisTask = mach_task_self(); KSLOG_DEBUG("Getting thread list"); kern_return_t kr; thread_act_array_t threads; mach_msg_type_number_t actualThreadCount; if((kr = task_threads(thisTask, &threads, &actualThreadCount)) != KERN_SUCCESS) { KSLOG_ERROR("task_threads: %s", mach_error_string(kr)); return false; } KSLOG_TRACE("Got %d threads", context->threadCount); int threadCount = (int)actualThreadCount; int maxThreadCount = sizeof(context->allThreads) / sizeof(context->allThreads[0]); if(threadCount > maxThreadCount) { KSLOG_ERROR("Thread count %d is higher than maximum of %d", threadCount, maxThreadCount); threadCount = maxThreadCount; } for(int i = 0; i < threadCount; i++) { context->allThreads[i] = threads[i]; } context->threadCount = threadCount; for(mach_msg_type_number_t i = 0; i < actualThreadCount; i++) { mach_port_deallocate(thisTask, context->allThreads[i]); } vm_deallocate(thisTask, (vm_address_t)threads, sizeof(thread_t) * actualThreadCount); return true; }
void kscrashsentry_uninstall(KSCrashType crashTypes) { KSLOG_DEBUG("Uninstalling handlers with crash types 0x%x.", crashTypes); for(size_t i = 0; i < g_sentriesCount; i++) { CrashSentry* sentry = &g_sentries[i]; if(sentry->crashType & crashTypes) { if(sentry->install != NULL) { sentry->uninstall(); } } } KSLOG_DEBUG("Uninstall complete."); }
void kscrashsentry_uninstall(KSCrashType crashTypes) { KSLOG_DEBUG("Uninstalling handlers with crash types 0x%x.", crashTypes); if(crashTypes & KSCrashTypeMachException) { kscrashsentry_uninstallMachHandler(); } if(crashTypes & KSCrashTypeSignal) { kscrashsentry_uninstallSignalHandler(); } if(crashTypes & KSCrashTypeNSException) { kscrashsentry_uninstallNSExceptionHandler(); } KSLOG_DEBUG("Uninstall complete."); }
void kscrashsentry_suspendThreads(void) { KSLOG_DEBUG("Suspending threads."); if(!g_threads_are_running) { KSLOG_DEBUG("Threads already suspended."); return; } if(g_context != NULL) { int numThreads = sizeof(g_context->reservedThreads) / sizeof(g_context->reservedThreads[0]); KSLOG_DEBUG("Suspending all threads except for %d reserved threads.", numThreads); if(ksmach_suspendAllThreadsExcept(g_context->reservedThreads, numThreads)) { KSLOG_DEBUG("Suspend successful."); g_threads_are_running = false; } } else { KSLOG_DEBUG("Suspending all threads."); if(ksmach_suspendAllThreads()) { KSLOG_DEBUG("Suspend successful."); g_threads_are_running = false; } } KSLOG_DEBUG("Suspend complete."); }
void kscrashsentry_resumeThreads(void) { KSLOG_DEBUG("Resuming threads."); if(g_threads_are_running) { KSLOG_DEBUG("Threads already resumed."); return; } if(g_context != NULL) { int numThreads = sizeof(g_context->reservedThreads) / sizeof(g_context->reservedThreads[0]); KSLOG_DEBUG("Resuming all threads except for %d reserved threads.", numThreads); if(ksmach_resumeAllThreadsExcept(g_context->reservedThreads, numThreads)) { KSLOG_DEBUG("Resume successful."); g_threads_are_running = true; } } else { KSLOG_DEBUG("Resuming all threads."); if(ksmach_resumeAllThreads()) { KSLOG_DEBUG("Resume successful."); g_threads_are_running = true; } } KSLOG_DEBUG("Resume complete."); }
void kscrashsentry_uninstallSignalHandler(void) { KSLOG_DEBUG("Uninstalling signal handlers."); if(!g_installed) { KSLOG_DEBUG("Signal handlers were already uninstalled."); return; } const int* fatalSignals = kssignal_fatalSignals(); int fatalSignalsCount = kssignal_numFatalSignals(); for(int i = 0; i < fatalSignalsCount; i++) { KSLOG_DEBUG("Restoring original handler for signal %d", fatalSignals[i]); sigaction(fatalSignals[i], &g_previousSignalHandlers[i], NULL); } KSLOG_DEBUG("Signal handlers uninstalled."); g_installed = 0; }
bool ksmc_getContextForSignal(void* signalUserContext, KSMachineContext* destinationContext) { KSLOG_DEBUG("Get context from signal user context and put into %p.", destinationContext); _STRUCT_MCONTEXT* sourceContext = ((SignalUserContext*)signalUserContext)->UC_MCONTEXT; memcpy(&destinationContext->machineContext, sourceContext, sizeof(destinationContext->machineContext)); destinationContext->thisThread = (thread_t)ksthread_self(); destinationContext->isCrashedContext = true; destinationContext->isSignalContext = true; destinationContext->isStackOverflow = isStackOverflow(destinationContext); getThreadList(destinationContext); KSLOG_TRACE("Context retrieved."); return true; }
void ksmc_resumeEnvironment() { #if KSCRASH_HAS_THREADS_API KSLOG_DEBUG("Resuming environment."); kern_return_t kr; const task_t thisTask = mach_task_self(); const thread_t thisThread = (thread_t)ksthread_self(); thread_act_array_t threads; mach_msg_type_number_t numThreads; if((kr = task_threads(thisTask, &threads, &numThreads)) != KERN_SUCCESS) { KSLOG_ERROR("task_threads: %s", mach_error_string(kr)); return; } for(mach_msg_type_number_t i = 0; i < numThreads; i++) { thread_t thread = threads[i]; if(thread != thisThread && !isThreadInList(thread, g_reservedThreads, g_reservedThreadsCount)) { if((kr = thread_resume(thread)) != KERN_SUCCESS) { // Record the error and keep going. KSLOG_ERROR("thread_resume (%08x): %s", thread, mach_error_string(kr)); } } } for(mach_msg_type_number_t i = 0; i < numThreads; i++) { mach_port_deallocate(thisTask, threads[i]); } vm_deallocate(thisTask, (vm_address_t)threads, sizeof(thread_t) * numThreads); KSLOG_DEBUG("Resume complete."); #endif }
bool ksmc_getContextForThread(KSThread thread, KSMachineContext* destinationContext, bool isCrashedContext) { KSLOG_DEBUG("Fill thread 0x%x context into %p. is crashed = %d", thread, destinationContext, isCrashedContext); memset(destinationContext, 0, sizeof(*destinationContext)); destinationContext->thisThread = (thread_t)thread; destinationContext->isCurrentThread = thread == ksthread_self(); destinationContext->isCrashedContext = isCrashedContext; destinationContext->isSignalContext = false; if(ksmc_canHaveCPUState(destinationContext)) { kscpu_getState(destinationContext); } if(ksmc_isCrashedContext(destinationContext)) { destinationContext->isStackOverflow = isStackOverflow(destinationContext); getThreadList(destinationContext); } KSLOG_TRACE("Context retrieved."); return true; }
/** Called when a crash occurs. * * This function gets passed as a callback to a crash handler. */ void kscrash_i_onCrash(void) { KSLOG_DEBUG("Updating application state to note crash."); kscrashstate_notifyAppCrash(); KSCrash_Context* context = crashContext(); if(context->config.printTraceToStdout) { kscrashreport_logCrash(context); } if(context->crash.crashedDuringCrashHandling) { kscrashreport_writeMinimalReport(context, g_recrashReportFilePath); } else { kscrashreport_writeStandardReport(context, g_crashReportFilePath); } }
/** Our custom signal handler. * Restore the default signal handlers, record the signal information, and * write a crash report. * Once we're done, re-raise the signal and let the default handlers deal with * it. * * @param signal The signal that was raised. * * @param signalInfo Information about the signal. * * @param userContext Other contextual information. */ void kssighndl_i_handleSignal(int sigNum, siginfo_t* signalInfo, void* userContext) { KSLOG_DEBUG("Trapped signal %d", sigNum); if(g_installed) { KSLOG_DEBUG("Signal handler is installed. Continuing signal handling."); KSLOG_DEBUG("Suspending all threads."); kscrashsentry_suspendThreads(); if(g_context->handlingCrash) { KSLOG_INFO("Detected crash in the crash reporter. Restoring original handlers."); g_context->crashedDuringCrashHandling = true; kscrashsentry_uninstall(KSCrashTypeAsyncSafe); } g_context->handlingCrash = true; KSLOG_DEBUG("Filling out context."); g_context->crashType = KSCrashTypeSignal; g_context->offendingThread = mach_thread_self(); g_context->registersAreValid = true; g_context->faultAddress = (uintptr_t)signalInfo->si_addr; g_context->signal.userContext = userContext; g_context->signal.signalInfo = signalInfo; KSLOG_DEBUG("Calling main crash handler."); g_context->onCrash(); KSLOG_DEBUG("Crash handling complete. Restoring original handlers."); kscrashsentry_uninstall(KSCrashTypeAsyncSafe); kscrashsentry_resumeThreads(); } KSLOG_DEBUG("Re-raising signal for regular handlers to catch."); // This is technically not allowed, but it works in OSX and iOS. raise(sigNum); }
bool kscrash_install(const char* const crashReportFilePath, const char* const recrashReportFilePath, const char* const stateFilePath, const char* const crashID, const char* const userInfoJSON, unsigned int zombieCacheSize, const bool printTraceToStdout, const KSReportWriteCallback onCrashNotify) { KSLOG_DEBUG("Installing crash reporter."); KSLOG_TRACE("reportFilePath = %s", reportFilePath); KSLOG_TRACE("secondaryReportFilePath = %s", secondaryReportFilePath); KSLOG_TRACE("stateFilePath = %s", stateFilePath); KSLOG_TRACE("crashID = %s", crashID); KSLOG_TRACE("userInfoJSON = %p", userInfoJSON); KSLOG_TRACE("zombieCacheSize = %d", zombieCacheSize); KSLOG_TRACE("printTraceToStdout = %d", printTraceToStdout); KSLOG_TRACE("onCrashNotify = %p", onCrashNotify); static volatile sig_atomic_t initialized = 0; if(!initialized) { initialized = 1; g_stateFilePath = strdup(stateFilePath); g_crashReportFilePath = strdup(crashReportFilePath); g_recrashReportFilePath = strdup(recrashReportFilePath); KSCrash_Context* context = crashContext(); context->crash.onCrash = kscrash_i_onCrash; if(ksmach_isBeingTraced()) { KSLOGBASIC_WARN("KSCrash: App is running in a debugger. Crash handlers have been disabled for the sanity of all."); } else if(kscrashsentry_installWithContext(&context->crash, KSCrashTypeAll) == 0) { KSLOG_ERROR("Failed to install any handlers"); } if(!kscrashstate_init(g_stateFilePath, &context->state)) { KSLOG_ERROR("Failed to initialize persistent crash state"); } context->state.appLaunchTime = mach_absolute_time(); context->config.printTraceToStdout = printTraceToStdout; context->config.systemInfoJSON = kssysteminfo_toJSON(); context->config.processName = kssystemInfo_copyProcessName(); kscrash_setUserInfoJSON(userInfoJSON); context->config.crashID = strdup(crashID); context->config.onCrashNotify = onCrashNotify; if(zombieCacheSize > 0) { KSLOG_DEBUG("zombieCacheSize > 0. Installing zombie handler."); kszombie_install(zombieCacheSize); } KSLOG_DEBUG("Installation complete."); return true; } KSLOG_ERROR("Called more than once"); return false; }
/** Our exception handler thread routine. * Wait for an exception message, uninstall our exception port, record the * exception information, and write a report. */ void* ksmachexc_i_handleExceptions(void* const userData) { #pragma unused(userData) MachExceptionMessage exceptionMessage = {{0}}; MachReplyMessage replyMessage = {{0}}; const char* threadName = (const char*) userData; pthread_setname_np(threadName); if(threadName == kThreadSecondary) { KSLOG_DEBUG("This is the secondary thread. Suspending."); thread_suspend(mach_thread_self()); } for(;;) { KSLOG_DEBUG("Waiting for mach exception"); // Wait for a message. kern_return_t kr = mach_msg(&exceptionMessage.header, MACH_RCV_MSG, 0, sizeof(exceptionMessage), g_exceptionPort, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); if(kr == KERN_SUCCESS) { break; } // Loop and try again on failure. KSLOG_ERROR("mach_msg: %s", mach_error_string(kr)); } KSLOG_DEBUG("Trapped mach exception code 0x%x, subcode 0x%x", exceptionMessage.code[0], exceptionMessage.code[1]); if(g_installed) { KSLOG_DEBUG("Exception handler is installed. Continuing exception handling."); KSLOG_DEBUG("Suspending all threads"); kscrashsentry_suspendThreads(); // Switch to the secondary thread if necessary, or uninstall the handler // to avoid a death loop. if(mach_thread_self() == pthread_mach_thread_np(g_primaryThread)) { KSLOG_DEBUG("This is the primary exception thread. Activating secondary thread."); if(thread_resume(pthread_mach_thread_np(g_secondaryThread)) != KERN_SUCCESS) { KSLOG_DEBUG("Could not activate secondary thread. Restoring original exception ports."); ksmachexc_i_restoreExceptionPorts(); } } else { KSLOG_DEBUG("This is the secondary exception thread. Restoring original exception ports."); ksmachexc_i_restoreExceptionPorts(); } if(g_context->handlingCrash) { KSLOG_INFO("Detected crash in the crash reporter. Restoring original handlers."); // The crash reporter itself crashed. Make a note of this and // uninstall all handlers so that we don't get stuck in a loop. g_context->crashedDuringCrashHandling = true; kscrashsentry_uninstall(KSCrashTypeAsyncSafe); } // Fill out crash information g_context->handlingCrash = true; KSLOG_DEBUG("Fetching machine state."); _STRUCT_MCONTEXT machineContext; if(ksmachexc_i_fetchMachineState(exceptionMessage.thread.name, &machineContext)) { if(exceptionMessage.exception == EXC_BAD_ACCESS) { g_context->faultAddress = ksmach_faultAddress(&machineContext); } else { g_context->faultAddress = ksmach_instructionAddress(&machineContext); } } KSLOG_DEBUG("Filling out context."); g_context->crashType = KSCrashTypeMachException; g_context->offendingThread = exceptionMessage.thread.name; g_context->registersAreValid = true; g_context->mach.type = exceptionMessage.exception; g_context->mach.code = exceptionMessage.code[0]; g_context->mach.subcode = exceptionMessage.code[1]; KSLOG_DEBUG("Calling main crash handler."); g_context->onCrash(); KSLOG_DEBUG("Crash handling complete. Restoring original handlers."); kscrashsentry_uninstall(KSCrashTypeAsyncSafe); kscrashsentry_resumeThreads(); } KSLOG_DEBUG("Replying to mach exception message."); // Send a reply saying "I didn't handle this exception". replyMessage.header = exceptionMessage.header; replyMessage.NDR = exceptionMessage.NDR; replyMessage.returnCode = KERN_FAILURE; mach_msg(&replyMessage.header, MACH_SEND_MSG, sizeof(replyMessage), 0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); return NULL; }
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; }
bool kscrashsentry_installUserExceptionHandler(KSCrash_SentryContext* const context) { KSLOG_DEBUG("Installing user exception handler."); g_context = context; return true; }
bool kscrashsentry_installSignalHandler(KSCrash_SentryContext* context) { KSLOG_DEBUG("Installing signal handler."); if(g_installed) { KSLOG_DEBUG("Signal handler already installed."); return true; } g_installed = 1; g_context = context; if(g_signalStack.ss_size == 0) { KSLOG_DEBUG("Allocating signal stack area."); g_signalStack.ss_size = SIGSTKSZ; g_signalStack.ss_sp = malloc(g_signalStack.ss_size); } KSLOG_DEBUG("Setting signal stack area."); if(sigaltstack(&g_signalStack, NULL) != 0) { KSLOG_ERROR("signalstack: %s", strerror(errno)); goto failed; } const int* fatalSignals = kssignal_fatalSignals(); int fatalSignalsCount = kssignal_numFatalSignals(); if(g_previousSignalHandlers == NULL) { KSLOG_DEBUG("Allocating memory to store previous signal handlers."); g_previousSignalHandlers = malloc(sizeof(*g_previousSignalHandlers) * (unsigned)fatalSignalsCount); } struct sigaction action = {{0}}; action.sa_flags = SA_SIGINFO | SA_ONSTACK; #ifdef __LP64__ action.sa_flags |= SA_64REGSET; #endif sigemptyset(&action.sa_mask); action.sa_sigaction = &kssighndl_i_handleSignal; for(int i = 0; i < fatalSignalsCount; i++) { KSLOG_DEBUG("Assigning handler for signal %d", fatalSignals[i]); if(sigaction(fatalSignals[i], &action, &g_previousSignalHandlers[i]) != 0) { char sigNameBuff[30]; const char* sigName = kssignal_signalName(fatalSignals[i]); if(sigName == NULL) { snprintf(sigNameBuff, sizeof(sigNameBuff), "%d", fatalSignals[i]); sigName = sigNameBuff; } KSLOG_ERROR("sigaction (%s): %s", sigName, strerror(errno)); // Try to reverse the damage for(i--;i >= 0; i--) { sigaction(fatalSignals[i], &g_previousSignalHandlers[i], NULL); } goto failed; } } KSLOG_DEBUG("Signal handlers installed."); return true; failed: KSLOG_DEBUG("Failed to install signal handlers."); g_installed = 0; return false; }
void kscrashsentry_uninstallUserExceptionHandler(void) { KSLOG_DEBUG("Uninstalling user exception handler."); g_context = NULL; }