Example #1
0
bool ksmach_getThreadQueueName(const thread_t thread,
                               char* const buffer,
                               size_t bufLength)
{
    struct internal_dispatch_queue_s* pQueue;
    struct internal_dispatch_queue_s queue;

    if(bufLength > sizeof(queue.dq_label))
    {
        bufLength = sizeof(queue.dq_label);
    }

    // Recast the opaque thread to our hacky internal thread structure pointer.
    const pthread_t pthread = pthread_from_mach_thread_np(thread);
    const internal_pthread_t const threadStruct = (internal_pthread_t)pthread;

    if(ksmach_copyMem(&threadStruct->tsd[dispatch_queue_key], &pQueue, sizeof(pQueue)) != KERN_SUCCESS)
    {
        KSLOG_TRACE("Could not copy queue pointer from %p", &threadStruct->tsd[dispatch_queue_key]);
        return false;
    }

    if(pQueue == NULL)
    {
        KSLOG_TRACE("Queue pointer is NULL");
        return false;
    }

    if(ksmach_copyMem(pQueue, &queue, sizeof(queue)) != KERN_SUCCESS)
    {
        KSLOG_TRACE("Could not copy queue data from %p", pQueue);
        return false;
    }

    // Queue label must be a null terminated string.
    int iLabel;
    for(iLabel = 0; iLabel < (int)sizeof(queue.dq_label); iLabel++)
    {
        if(queue.dq_label[iLabel] < ' ' || queue.dq_label[iLabel] > '~')
        {
            break;
        }
    }
    if(queue.dq_label[iLabel] != 0)
    {
        // Found a non-null, invalid char.
        KSLOG_TRACE("Queue label contains invalid chars");
        return false;
    }
    
    strncpy(buffer, queue.dq_label, bufLength);
    KSLOG_TRACE("Queue label = %s", buffer);
    return true;
}
Example #2
0
int ksmc_indexOfThread(const KSMachineContext* const context, KSThread thread)
{
    KSLOG_TRACE("check thread vs %d threads", context->threadCount);
    for(int i = 0; i < (int)context->threadCount; i++)
    {
        KSLOG_TRACE("%d: %x vs %x", i, thread, context->allThreads[i]);
        if(context->allThreads[i] == thread)
        {
            return i;
        }
    }
    return -1;
}
Example #3
0
void kscrash_setUserInfoJSON(const char* const userInfoJSON)
{
    KSLOG_TRACE("set userInfoJSON to %p", userInfoJSON);
    KSCrash_Context* context = crashContext();
    if(context->config.userInfoJSON != NULL)
    {
        KSLOG_TRACE("Free old data at %p", context->config.userInfoJSON);
        free((void*)context->config.userInfoJSON);
    }
    if(userInfoJSON != NULL)
    {
        context->config.userInfoJSON = strdup(userInfoJSON);
        KSLOG_TRACE("Duplicated string to %p", context->config.userInfoJSON);
    }
}
Example #4
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;
}
/** 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;
}
Example #6
0
thread_t ksmach_machThreadFromPThread(const pthread_t pthread)
{
    const internal_pthread_t threadStruct = (internal_pthread_t)pthread;
    thread_t machThread = 0;
    if(ksmach_copyMem(&threadStruct->kernel_thread, &machThread, sizeof(machThread)) != KERN_SUCCESS)
    {
        KSLOG_TRACE("Could not copy mach thread from %p", threadStruct->kernel_thread);
        return 0;
    }
    return machThread;
}
Example #7
0
void kscrash_reinstall(const char* const crashReportFilePath,
                       const char* const recrashReportFilePath,
                       const char* const stateFilePath,
                       const char* const crashID)
{
    KSLOG_TRACE("reportFilePath = %s", crashReportFilePath);
    KSLOG_TRACE("secondaryReportFilePath = %s", recrashReportFilePath);
    KSLOG_TRACE("stateFilePath = %s", stateFilePath);
    KSLOG_TRACE("crashID = %s", crashID);

    ksstring_replace((const char**)&g_stateFilePath, stateFilePath);
    ksstring_replace((const char**)&g_crashReportFilePath, crashReportFilePath);
    ksstring_replace((const char**)&g_recrashReportFilePath, recrashReportFilePath);
    KSCrash_Context* context = crashContext();
    ksstring_replace(&context->config.crashID, crashID);

    if(!kscrashstate_init(g_stateFilePath, &context->state))
    {
        KSLOG_ERROR("Failed to initialize persistent crash state");
    }
    context->state.appLaunchTime = mach_absolute_time();
}
Example #8
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;
}
Example #9
0
bool ksmach_getThreadQueueName(const thread_t thread,
                               char* const buffer,
                               size_t bufLength)
{
    // WARNING: This implementation is no longer async-safe!

    integer_t infoBuffer[THREAD_IDENTIFIER_INFO_COUNT] = {0};
    thread_info_t info = infoBuffer;
    mach_msg_type_number_t inOutSize = THREAD_IDENTIFIER_INFO_COUNT;
    kern_return_t kr = 0;

    kr = thread_info(thread, THREAD_IDENTIFIER_INFO, info, &inOutSize);
    if(kr != KERN_SUCCESS)
    {
        KSLOG_TRACE("Error getting thread_info with flavor THREAD_IDENTIFIER_INFO from mach thread : %s", mach_error_string(kr));
        return false;
    }

    thread_identifier_info_t idInfo = (thread_identifier_info_t)info;
    dispatch_queue_t* dispatch_queue_ptr = (dispatch_queue_t*)idInfo->dispatch_qaddr;
    //thread_handle shouldn't be 0 also, because
    //identifier_info->dispatch_qaddr =  identifier_info->thread_handle + get_dispatchqueue_offset_from_proc(thread->task->bsd_info);
    if(dispatch_queue_ptr == NULL || idInfo->thread_handle == 0 || *dispatch_queue_ptr == NULL)
    {
        KSLOG_TRACE("This thread doesn't have a dispatch queue attached : %p", thread);
        return false;
    }

    dispatch_queue_t dispatch_queue = *dispatch_queue_ptr;
    const char* queue_name = dispatch_queue_get_label(dispatch_queue);
    if(queue_name == NULL)
    {
        KSLOG_TRACE("Error while getting dispatch queue name : %p", dispatch_queue);
        return false;
    }
    KSLOG_TRACE("Dispatch queue name: %s", queue_name);
    size_t length = strlen(queue_name);

    // Queue label must be a null terminated string.
    size_t iLabel;
    for(iLabel = 0; iLabel < length + 1; iLabel++)
    {
        if(queue_name[iLabel] < ' ' || queue_name[iLabel] > '~')
        {
            break;
        }
    }
    if(queue_name[iLabel] != 0)
    {
        // Found a non-null, invalid char.
        KSLOG_TRACE("Queue label contains invalid chars");
        return false;
    }
    bufLength = MIN(length, bufLength - 1);//just strlen, without null-terminator
    strncpy(buffer, queue_name, bufLength);
    buffer[bufLength] = 0;//terminate string
    KSLOG_TRACE("Queue label = %s", buffer);
    return true;
}
Example #10
0
bool ksmach_getThreadName(const thread_t thread,
                          char* const buffer,
                          size_t bufLength)
{
    const pthread_t pthread = ksmach_pthreadFromMachThread(thread);
    const internal_pthread_t threadStruct = (internal_pthread_t)pthread;
    size_t copyLength = bufLength > MAXTHREADNAMESIZE ? MAXTHREADNAMESIZE : bufLength;

    if(ksmach_copyMem(threadStruct->pthread_name, buffer, copyLength) != KERN_SUCCESS)
    {
        KSLOG_TRACE("Could not copy thread name from %p", threadStruct->pthread_name);
        return false;
    }
    buffer[copyLength-1] = 0;
    return true;
}
Example #11
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;
}
Example #12
0
void kscrash_setCrashNotifyCallback(const KSReportWriteCallback onCrashNotify)
{
    KSLOG_TRACE("Set onCrashNotify to %p", onCrashNotify);
    crashContext()->config.onCrashNotify = onCrashNotify;
}
Example #13
0
void kscrash_setUserInfoJSON(const char* const userInfoJSON)
{
    KSLOG_TRACE("set userInfoJSON to %p", userInfoJSON);
    KSCrash_Context* context = crashContext();
    ksstring_replace(&context->config.userInfoJSON, userInfoJSON);
}
Example #14
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;
}