/** 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;
}
示例#2
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);
}
示例#3
0
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;
}
示例#4
0
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;
}
示例#5
0
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();
        }
    }
}
示例#8
0
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.");
}
示例#9
0
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;
}
示例#10
0
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.");
}
示例#11
0
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.");
}
示例#12
0
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.");
}
示例#13
0
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.");
}
示例#14
0
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;
}
示例#15
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;
}
示例#16
0
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
}
示例#17
0
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;
}
示例#18
0
/** 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);
    }
}
示例#19
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 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);
}
示例#20
0
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;
}
示例#24
0
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;
}