Пример #1
0
int port_thread_detach()
{
    port_tls_data_t* tlsdata;
    int res;

    if (!port_shared_data && (res = init_port_shared_data()) != 0)
        return res;

    tlsdata = get_private_tls_data();

    if (!tlsdata)
        return 0;

    if (port_thread_detach_temporary() == 0)
        return 0;

    res = set_guard_page(tlsdata, FALSE);

    if (res != 0)
        return res;

    size_t mapping_addr = (size_t)tlsdata->stack_addr - tlsdata->stack_size;
    size_t mapping_size =
        (tlsdata->guard_stack_size + tlsdata->mem_protect_size + 2*PSD->guard_page_size - 1) &
            ~(PSD->guard_page_size - 1);
    munmap((void*)mapping_addr, mapping_size);

    if (tlsdata->foreign)
        STD_FREE(tlsdata);

    return set_private_tls_data(NULL);
}
Пример #2
0
static int set_guard_page(port_tls_data_t* tlsdata, Boolean set)
{
    int res;

    if (!tlsdata)
        tlsdata = get_private_tls_data();
	
    if (!tlsdata)
        return -1;

    if (!tlsdata->guard_page_addr)
        return 0;

    if ((set && tlsdata->guard_page_set) ||
        (!set && !tlsdata->guard_page_set))
        return 0; // Already in needed state

    res = mprotect(tlsdata->guard_page_addr, tlsdata->guard_page_size,
                    set ? PROT_NONE : (PROT_READ | PROT_WRITE | PROT_EXEC));

	
    if (res != 0)
        return errno;

    if (set)
    {
        res = set_alt_stack(tlsdata, TRUE);

        if (res != 0)
            return res;
    }

    tlsdata->guard_page_set = set;
    return 0;
}
Пример #3
0
static void c_handler(Registers* pregs,
                        void* fault_addr, size_t code, size_t flags)
{ // this exception handler is executed *after* VEH handler returned
    int result;
    Boolean iscrash = (DWORD)flags == EXCEPTION_NONCONTINUABLE;

    switch ((DWORD)code)
    {
    case STATUS_STACK_OVERFLOW:
        result = port_process_signal(PORT_SIGNAL_STACK_OVERFLOW, pregs, fault_addr, iscrash);
        break;
    case STATUS_ACCESS_VIOLATION:
        result = port_process_signal(PORT_SIGNAL_GPF, pregs, fault_addr, iscrash);
        break;
    case STATUS_INTEGER_DIVIDE_BY_ZERO:
    case EXCEPTION_FLT_DIVIDE_BY_ZERO:
    case EXCEPTION_FLT_OVERFLOW:
    case EXCEPTION_FLT_UNDERFLOW:
    case EXCEPTION_INT_OVERFLOW:
        result = port_process_signal(PORT_SIGNAL_ARITHMETIC, pregs, fault_addr, iscrash);
        break;
    case JVMTI_EXCEPTION_STATUS:
        result = port_process_signal(PORT_SIGNAL_BREAKPOINT, pregs, fault_addr, iscrash);
        break;
    default:
        result = port_process_signal(PORT_SIGNAL_UNKNOWN, pregs, fault_addr, TRUE);
    }

    port_tls_data_t* tlsdata = get_private_tls_data();

    if (result == 0)
    {
        // Restore guard page if needed
        if (tlsdata->restore_guard_page)
        {
            port_thread_restore_guard_page();
            tlsdata->restore_guard_page = FALSE;
        }

        if (port_thread_detach_temporary() == 0)
            STD_FREE(tlsdata);
        return;
    }

    if (result > 0 /*Assert dialog*/|| FLAG_CORE)
    {
        // Prepare second catch of this exception to produce minidump (because
        // we've lost LPEXCEPTION_POINTERS structure) and/or show assert dialog
        if (FLAG_CORE)
            tlsdata->produce_core = TRUE;
        if (result > 0)
            tlsdata->debugger = TRUE;
        // To catch STACK_OVERFLOW
        port_thread_restore_guard_page();

        return; // To produce exception again
    }

    _exit(-1); // We need neither dump nor assert dialog
}
Пример #4
0
int port_thread_detach_temporary()
{
    port_tls_data_t* tlsdata = get_private_tls_data();

    if (!tlsdata || tlsdata->guard_page_addr)
        return -1;

    return set_private_tls_data(NULL);
}
Пример #5
0
void port_thread_postpone_guard_page()
{
    port_tls_data_t* tlsdata = get_private_tls_data();

    if (!tlsdata || !tlsdata->guard_page_addr)
        return;

    tlsdata->restore_guard_page = FALSE;
}
Пример #6
0
size_t port_thread_get_effective_stack_size()
{
    port_tls_data_t* tlsdata = get_private_tls_data();

    if (!tlsdata)
        return 0;

    if (!tlsdata->guard_page_addr || !tlsdata->guard_page_set)
        return tlsdata->stack_size
               - PSD->guard_page_size
               - PSD->mem_protect_size;

    return tlsdata->stack_size - 2*PSD->guard_page_size
           - PSD->guard_stack_size - PSD->mem_protect_size;
}
Пример #7
0
int port_thread_attach()
{
    int res;
    port_tls_data_t* tlsdata;

    if (!port_shared_data && (res = init_port_shared_data()) != 0)
        return res;

    if (get_private_tls_data())
        return 0;

    tlsdata = (port_tls_data_t*)STD_MALLOC(sizeof(port_tls_data_t));

    if (!tlsdata)
        return ENOMEM;

    res = port_thread_attach_local(tlsdata, FALSE, TRUE, 0);

    if (res != 0)
        STD_FREE(tlsdata);

    return res;
}
Пример #8
0
size_t port_thread_get_stack_size()
{
    port_tls_data_t* tlsdata = get_private_tls_data();
    return tlsdata ? tlsdata->stack_size : 0;
}
Пример #9
0
void* port_thread_get_stack_address()
{
    port_tls_data_t* tlsdata = get_private_tls_data();
    return tlsdata ? tlsdata->stack_addr : NULL;
}
Пример #10
0
LONG NTAPI vectored_exception_handler_internal(LPEXCEPTION_POINTERS nt_exception)
{
    DWORD code = nt_exception->ExceptionRecord->ExceptionCode;
    void* fault_addr = nt_exception->ExceptionRecord->ExceptionAddress;

    Registers regs;
    // Convert NT context to Registers
    port_thread_context_to_regs(&regs, nt_exception->ContextRecord);

    // Check if TLS structure is set - probably we should produce minidump
    port_tls_data_t* tlsdata = get_private_tls_data();

    if (!tlsdata) // Tread is not attached - attach thread temporarily
    {
        int res;
        tlsdata = (port_tls_data_t*)STD_MALLOC(sizeof(port_tls_data_t));

        if (tlsdata) // Try to attach the thread
            res = port_thread_attach_local(tlsdata, TRUE, TRUE, 0);

        if (!tlsdata || res != 0)
        { // Can't process correctly; perform default actions
            if (FLAG_CORE)
                create_minidump(nt_exception);

            if (FLAG_DBG)
            {
                show_debugger_dialog(); // Workaround; EXCEPTION_CONTINUE_SEARCH does not work
                _exit(-1);
                return EXCEPTION_CONTINUE_SEARCH; // Assert dialog
            }

            _exit(-1);
        }

        // SO for alien thread can't be processed out of VEH
        if (code == STATUS_STACK_OVERFLOW &&
            sd_is_handler_registered(PORT_SIGNAL_STACK_OVERFLOW))
        {
            int result;
            size_t alt_stack_size = ALT_PAGES_COUNT*tlsdata->guard_page_size;
            void* alt_stack = map_alt_stack(alt_stack_size);
            void* stack_bottom = (void*)((POINTER_SIZE_INT)alt_stack + alt_stack_size);

            if (alt_stack)
                result = (int)(POINTER_SIZE_INT)port_call_alt_stack(
                                            port_process_signal, stack_bottom, 4,
                                            PORT_SIGNAL_STACK_OVERFLOW, &regs, fault_addr, FALSE);
            else
                result = port_process_signal(PORT_SIGNAL_STACK_OVERFLOW, &regs, fault_addr, FALSE);

            if (result == 0)
            {
                if (port_thread_detach_temporary() == 0)
                    STD_FREE(tlsdata);
                if (alt_stack)
                    unmap_alt_stack(alt_stack, alt_stack_size);
                return EXCEPTION_CONTINUE_EXECUTION;
            }

            if (FLAG_CORE)
            {
                if (alt_stack)
                    port_call_alt_stack(create_minidump, stack_bottom, 1, nt_exception);
                else
                    create_minidump(nt_exception);
            }

            if (alt_stack)
                unmap_alt_stack(alt_stack, alt_stack_size);

            if (result > 0)
            {
                show_debugger_dialog(); // Workaround; EXCEPTION_CONTINUE_SEARCH does not work
                _exit(-1);
                shutdown_signals();
                return EXCEPTION_CONTINUE_SEARCH; // Assert dialog
            }

            _exit(-1);
        }
    }

    if (tlsdata->produce_core)
    {
        create_minidump(nt_exception);
        if (!tlsdata->debugger)
            _exit(-1);
    }

    if (tlsdata->debugger)
    {
        show_debugger_dialog(); // Workaround
        _exit(-1);
        // Go to handler to restore CRT/VEH settings and crash once again
//        port_set_longjump_regs(&prepare_assert_dialog, &regs, 1, &regs);
//        port_thread_regs_to_context(nt_exception->ContextRecord, &regs);
//        return EXCEPTION_CONTINUE_EXECUTION;
    }

    switch (code)
    {
    case STATUS_STACK_OVERFLOW:
        if (!sd_is_handler_registered(PORT_SIGNAL_STACK_OVERFLOW))
            return EXCEPTION_CONTINUE_SEARCH;
        break;
    case STATUS_ACCESS_VIOLATION:
        if (!sd_is_handler_registered(PORT_SIGNAL_GPF))
            return EXCEPTION_CONTINUE_SEARCH;
        break;
    case JVMTI_EXCEPTION_STATUS:
        if (!sd_is_handler_registered(PORT_SIGNAL_BREAKPOINT))
            return EXCEPTION_CONTINUE_SEARCH;
        break;
    case STATUS_INTEGER_DIVIDE_BY_ZERO:
    case EXCEPTION_FLT_DIVIDE_BY_ZERO:
    case EXCEPTION_FLT_OVERFLOW:
    case EXCEPTION_FLT_UNDERFLOW:
    case EXCEPTION_INT_OVERFLOW:
        if (!sd_is_handler_registered(PORT_SIGNAL_ARITHMETIC))
            return EXCEPTION_CONTINUE_SEARCH;
        break;
    default:
        return EXCEPTION_CONTINUE_SEARCH;
    }

    if (code == STATUS_STACK_OVERFLOW)
    {
        tlsdata->guard_page_set = FALSE; // GUARD_PAGE was cleared by OS

        if (!tlsdata->restore_guard_page)
            tlsdata->restore_guard_page = TRUE;
    }

    // Prepare to transfering control out of VEH handler
    port_set_longjump_regs(&c_handler, &regs, 4, &regs,
        nt_exception->ExceptionRecord->ExceptionAddress,
        (void*)(size_t)nt_exception->ExceptionRecord->ExceptionCode,
        (void*)(size_t)nt_exception->ExceptionRecord->ExceptionFlags);
    // Convert prepared Registers back to NT context
    port_thread_regs_to_context(nt_exception->ContextRecord, &regs);
    // Return from VEH - presumably continue execution
    return EXCEPTION_CONTINUE_EXECUTION;
}
Пример #11
0
static void c_handler(Registers* pregs, size_t signum, void* fault_addr)
{   // this exception handler is executed *after* OS signal handler returned
    int result;
    port_tls_data_t* tlsdata = get_private_tls_data();

    switch ((int)signum)
    {
    case SIGSEGV:
        if (tlsdata->restore_guard_page)
        {
            // Now it's safe to disable alternative stack
            set_alt_stack(tlsdata, FALSE);
            result = port_process_signal(PORT_SIGNAL_STACK_OVERFLOW, pregs, fault_addr, FALSE);
        }
        else
            result = port_process_signal(PORT_SIGNAL_GPF, pregs, fault_addr, FALSE);
        break;
    case SIGFPE:
        result = port_process_signal(PORT_SIGNAL_ARITHMETIC, pregs, fault_addr, FALSE);
        break;
    case SIGTRAP:
        // Correct return address
        pregs->set_ip((void*)((POINTER_SIZE_INT)pregs->get_ip() - 1));
        result = port_process_signal(PORT_SIGNAL_BREAKPOINT, pregs, fault_addr, FALSE);
        break;
    case SIGINT:
        result = port_process_signal(PORT_SIGNAL_CTRL_C, pregs, fault_addr, FALSE);
        break;
    case SIGQUIT:
        result = port_process_signal(PORT_SIGNAL_QUIT, pregs, fault_addr, FALSE);
        break;
    case SIGABRT:
        result = port_process_signal(PORT_SIGNAL_ABORT, NULL, fault_addr, FALSE);
        break;
    default:
        result = port_process_signal(PORT_SIGNAL_UNKNOWN, pregs, fault_addr, TRUE);
    }

    if (result == 0)
    {
        // Restore guard page if needed
        if (tlsdata->restore_guard_page)
        {
            port_thread_restore_guard_page();
            tlsdata->restore_guard_page = FALSE;

            if (port_thread_detach_temporary() == 0)
                STD_FREE(tlsdata);
        }
        return;
    }

    // We've got a crash
    if (signum == SIGSEGV)
    {
        port_thread_restore_guard_page(); // To catch SO again
        tlsdata->restore_guard_page = FALSE;
    }

    if (result > 0) // invoke debugger
    {   // Prepare second catch of signal to attach GDB from signal handler
        //assert(tlsdata); // Should be attached - provided by general_signal_handler
        tlsdata->debugger = TRUE;
        return; // To produce signal again
    }

    // result < 0 - exit process
    if (FLAG_CORE)
    {   // Return to the same place to produce the same crash and generate core
        signal(signum, SIG_DFL); // setup default handler
        return;
    }

    // No core needed - simply terminate
    _exit(-1);
}
Пример #12
0
static void general_signal_handler(int signum, siginfo_t* info, void* context)
{
    Registers regs;

    if (!context)
        return;

    // Convert OS context to Registers
    port_thread_context_to_regs(&regs, (ucontext_t*)context);
    void* fault_addr = info ? info->si_addr : NULL;

    // Check if SIGSEGV is produced by port_read/write_memory
    port_tls_data_t* tlsdata = get_private_tls_data();
    if (tlsdata && tlsdata->violation_flag)
    {
        tlsdata->violation_flag = 0;
        regs.set_ip(tlsdata->restart_address);
        return;
    }

    if (!tlsdata) // Tread is not attached - attach thread temporarily
    {
        int res;
        tlsdata = (port_tls_data_t*)STD_MALLOC(sizeof(port_tls_data_t));

        if (tlsdata) // Try to attach the thread
            res = port_thread_attach_local(tlsdata, TRUE, TRUE, 0);

        if (!tlsdata || res != 0)
        {   // Can't process correctly; perform default actions
            if (FLAG_DBG)
            {
                bool result = gdb_crash_handler(&regs);
                _exit(-1); // Exit process if not sucessful...
            }

            if (FLAG_CORE &&
                    signum != SIGABRT) // SIGABRT can't be rethrown
            {
                signal(signum, SIG_DFL); // setup default handler
                return;
            }

            _exit(-1);
        }

        // SIGSEGV can represent SO which can't be processed out of signal handler
        if (signum == SIGSEGV && // This can occur only when a user set an alternative stack
                is_stack_overflow(tlsdata, fault_addr))
        {
            int result = port_process_signal(PORT_SIGNAL_STACK_OVERFLOW, &regs, fault_addr, FALSE);

            if (result == 0)
            {
                if (port_thread_detach_temporary() == 0)
                    STD_FREE(tlsdata);
                return;
            }

            if (result > 0)
                tlsdata->debugger = TRUE;
            else
            {
                if (FLAG_CORE)
                {   // Rethrow crash to generate core
                    signal(signum, SIG_DFL); // setup default handler
                    return;
                }
                _exit(-1);
            }
        }
    }

    if (tlsdata->debugger)
    {
        bool result = gdb_crash_handler(&regs);
        _exit(-1); // Exit process if not sucessful...
    }

    if (signum == SIGABRT && // SIGABRT can't be trown again from c_handler
            FLAG_DBG)
    {   // So attaching GDB right here
        bool result = gdb_crash_handler(&regs);
        _exit(-1); // Exit process if not sucessful...
    }

    if (signum == SIGSEGV &&
            is_stack_overflow(tlsdata, fault_addr))
    {
        // Second SO while previous SO is not processed yet - is GPF
        if (tlsdata->restore_guard_page)
            tlsdata->restore_guard_page = FALSE;
        else
        {   // To process signal on protected stack area
            port_thread_clear_guard_page();
            // Note: the call above does not disable alternative stack
            // It can't be made while we are on alternative stack
            // Alt stack will be disabled explicitly in c_handler()
            tlsdata->restore_guard_page = TRUE;
        }
    }

    // Prepare registers for transfering control out of signal handler
    void* callback = (void*)&c_handler;

    port_set_longjump_regs(callback, &regs, 3,
                           &regs, (void*)(size_t)signum, fault_addr);

    // Convert prepared Registers back to OS context
    port_thread_regs_to_context((ucontext_t*)context, &regs);
    // Return from signal handler to go to C handler
}