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); }
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; }
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 }
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); }
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; }
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; }
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; }
size_t port_thread_get_stack_size() { port_tls_data_t* tlsdata = get_private_tls_data(); return tlsdata ? tlsdata->stack_size : 0; }
void* port_thread_get_stack_address() { port_tls_data_t* tlsdata = get_private_tls_data(); return tlsdata ? tlsdata->stack_addr : NULL; }
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(®s, 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, ®s, fault_addr, FALSE); else result = port_process_signal(PORT_SIGNAL_STACK_OVERFLOW, ®s, 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, ®s, 1, ®s); // port_thread_regs_to_context(nt_exception->ContextRecord, ®s); // 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, ®s, 4, ®s, 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, ®s); // Return from VEH - presumably continue execution return EXCEPTION_CONTINUE_EXECUTION; }
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); }
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(®s, (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(®s); _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, ®s, 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(®s); _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(®s); _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, ®s, 3, ®s, (void*)(size_t)signum, fault_addr); // Convert prepared Registers back to OS context port_thread_regs_to_context((ucontext_t*)context, ®s); // Return from signal handler to go to C handler }