diff --git a/vm/port/src/signals/include/signals_internal.h b/vm/port/src/signals/include/signals_internal.h index 8496f8d..8853903 100644 --- a/vm/port/src/signals/include/signals_internal.h +++ b/vm/port/src/signals/include/signals_internal.h @@ -102,6 +102,21 @@ LONG NTAPI vectored_exception_handler_internal(LPEXCEPTION_POINTERS nt_exception void PORT_CDECL port_win_dbg_break(void); + +/** +* The function to call another function on alternative stack. +* +* @param [in] fn - the address of the function to be called +* @param [in] stack - the address of memory area to be used as a stack +* @param [in] num - the number of parameters passed to the 'fn' function +* in the variable args list (6 args at maximum) +* @param [in] ... - the parameters for 'fn'; should all be void* or of +* the same size (pointer-sized) +* @return - can return any pointer-size value +*/ +void* port_call_alt_stack(void* fn, void* stack_addr, int num, ...); + + #else /* UNIX */ // diff --git a/vm/port/src/signals/win/signals_asm_em64t.asm b/vm/port/src/signals/win/signals_asm_em64t.asm index df3acff..3c3ace5 100644 --- a/vm/port/src/signals/win/signals_asm_em64t.asm +++ b/vm/port/src/signals/win/signals_asm_em64t.asm @@ -145,6 +145,54 @@ port_longjump_stub PROC port_longjump_stub ENDP +; void* __cdecl port_setstack_stub(Registers* regs); +; +; The function calls some function on alternative stack. +; Function address is already in RIP register of 'regs' structure. +; We only need to store return address and stack address. +; +; | saved stack | +; | address | <- to store RSP of current stack +; |-------------| +; | arg 5 | <- present even if not used +; |-------------| +; | arg 4 | <- present even if not used +; |-------------| +; | 32 bytes | <- 'red zone' for argument registers flushing +; |-------------| +; | return addr | <- points to port_setstack_stub_end +; |-------------| <- regs->rsp points to this cell + +PUBLIC port_setstack_stub + +port_setstack_stub PROC + + push rbp + + mov r9, qword ptr [rcx] ; get new rsp + + call port_setstack_stub_mid +port_setstack_stub_mid: + pop rax ; get address of port_setstack_stub_mid + ; calculate address of port_setstack_stub_end + add rax, (port_setstack_stub_end - port_setstack_stub_mid) + ; store return address + mov qword ptr [r9], rax + ; store current RSP + mov qword ptr [r9 + 56], rsp + + ; RCX is unchanged + call port_transfer_to_regs + +port_setstack_stub_end: + ; restore RSP + mov rsp, qword ptr [rsp + 48] + pop rbp + ret + +port_setstack_stub ENDP + + _TEXT ENDS END diff --git a/vm/port/src/signals/win/signals_asm_ia32.asm b/vm/port/src/signals/win/signals_asm_ia32.asm index b3b4adb..2c6f3b3 100644 --- a/vm/port/src/signals/win/signals_asm_ia32.asm +++ b/vm/port/src/signals/win/signals_asm_ia32.asm @@ -102,6 +102,54 @@ port_longjump_stub PROC port_longjump_stub ENDP +; void* __cdecl port_setstack_stub(Registers* regs); +; +; The function calls some function on alternative stack. +; Function address is already in EIP register of 'regs' structure. +; We only need to store return address and stack address. +; +; | saved stack | +; | address | <- to store ESP of current stack +; |-------------| +; | arg 5 | <- present even if not used +; |-------------| +; | ... | +; |-------------| +; | arg 0 | <- present even if not used +; |-------------| +; | return addr | <- points to port_setstack_stub_end +; |-------------| <- regs->esp points to this cell + +PUBLIC port_setstack_stub + +port_setstack_stub PROC + + push ebp + mov edx, dword ptr [esp + 4 + 4] ; get regs pointer + mov ebp, dword ptr [edx + 1Ch] ; get new esp + + call port_setstack_stub_mid +port_setstack_stub_mid: + pop eax ; get address of port_setstack_stub_mid + ; calculate address of port_setstack_stub_end + add eax, (port_setstack_stub_end - port_setstack_stub_mid) + ; store return address + mov dword ptr [ebp], eax + ; store current ESP + mov dword ptr [ebp + 28], esp + + push edx + call port_transfer_to_regs + +port_setstack_stub_end: + ; restore ESP + mov esp, dword ptr [esp + 24] + pop ebp + ret + +port_setstack_stub ENDP + + _TEXT ENDS END diff --git a/vm/port/src/signals/win/signals_common.cpp b/vm/port/src/signals/win/signals_common.cpp index a6d72e7..a07e1e5 100644 --- a/vm/port/src/signals/win/signals_common.cpp +++ b/vm/port/src/signals/win/signals_common.cpp @@ -41,6 +41,8 @@ #define FLAG_CORE ((port_crash_handler_get_flags() & PORT_CRASH_DUMP_PROCESS_CORE) != 0) #define FLAG_DBG ((port_crash_handler_get_flags() & PORT_CRASH_CALL_DEBUGGER) != 0) +#define ALT_PAGES_COUNT 16 + typedef void (PORT_CDECL *sigh_t)(int); // Signal handler type @@ -142,6 +144,20 @@ void prepare_assert_dialog(Registers* regs) shutdown_signals(); } +static void* map_alt_stack(size_t size) +{ + LPVOID res = VirtualAlloc(0, (SIZE_T)size, MEM_RESERVE, PAGE_READWRITE); + if (!res) + return NULL; + + return VirtualAlloc(res, (SIZE_T)size, MEM_COMMIT, PAGE_READWRITE); +} + +static void unmap_alt_stack(void* addr, size_t size) +{ + VirtualFree(addr, (SIZE_T)size, MEM_RELEASE); +} + LONG NTAPI vectored_exception_handler_internal(LPEXCEPTION_POINTERS nt_exception) { DWORD code = nt_exception->ExceptionRecord->ExceptionCode; @@ -181,17 +197,37 @@ LONG NTAPI vectored_exception_handler_internal(LPEXCEPTION_POINTERS nt_exception if (code == STATUS_STACK_OVERFLOW && sd_is_handler_registered(PORT_SIGNAL_STACK_OVERFLOW)) { - int result = port_process_signal(PORT_SIGNAL_STACK_OVERFLOW, ®s, fault_addr, FALSE); + 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) - create_minidump(nt_exception); + { + 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) { diff --git a/vm/port/src/signals/win/signals_em64t.cpp b/vm/port/src/signals/win/signals_em64t.cpp index d7eefca..81ec026 100644 --- a/vm/port/src/signals/win/signals_em64t.cpp +++ b/vm/port/src/signals/win/signals_em64t.cpp @@ -135,3 +135,53 @@ void port_transfer_to_function(void* fn, Registers* pregs, int num, ...) port_transfer_to_regs(®s); } + + +extern "C" void* __cdecl port_setstack_stub(Registers* regs); + +// New stack's organization: +// +// | alignment | +// |-------------| +// | saved stack | +// | address | <- to store RSP of current stack +// |-------------| +// | arg 5 | <- present even if not used +// |-------------| +// | arg 4 | <- present even if not used +// |-------------| +// | 32 bytes | <- 'red zone' for argument registers flushing +// |-------------| +// | return addr | <- points to port_setstack_stub_end +// |-------------| <- regs->rsp points to this cell + +void* port_call_alt_stack(void* fn, void* stack_addr, int num, ...) +{ + void** sp; + va_list ap; + Registers regs = {}; + + regs.rsp = (uint64)stack_addr; + regs.rsp -= 8*(1 + 1 + 6 + 1); + sp = (void**)regs.rsp; + + va_start(ap, num); + + if (num > 0) + regs.rcx = (uint64)va_arg(ap, void*); + if (num > 1) + regs.rdx = (uint64)va_arg(ap, void*); + if (num > 2) + regs.r8 = (uint64)va_arg(ap, void*); + if (num > 3) + regs.r9 = (uint64)va_arg(ap, void*); + + for (int i = 5; i <= num; i++) + { + sp[i] = va_arg(ap, void*); + } + + regs.rip = (uint64)fn; + + return port_setstack_stub(®s); +} diff --git a/vm/port/src/signals/win/signals_ia32.cpp b/vm/port/src/signals/win/signals_ia32.cpp index 55e7e70..290f752 100644 --- a/vm/port/src/signals/win/signals_ia32.cpp +++ b/vm/port/src/signals/win/signals_ia32.cpp @@ -125,3 +125,42 @@ void port_transfer_to_function(void* fn, Registers* pregs, int num, ...) port_transfer_to_regs(®s); } + + +extern "C" void* __cdecl port_setstack_stub(Registers* regs); + +// New stack's organization: +// +// | saved stack | +// | address | <- to store ESP of current stack +// |-------------| +// | arg 5 | <- present even if not used +// |-------------| +// | ... | +// |-------------| +// | arg 0 | <- present even if not used +// |-------------| +// | return addr | <- points to port_setstack_stub_end +// |-------------| <- regs->esp points to this cell + +void* port_call_alt_stack(void* fn, void* stack_addr, int num, ...) +{ + void** sp; + va_list ap; + Registers regs = {}; + + regs.esp = (U_32)stack_addr; + regs.esp -= 4*(1 + 6 + 1); + sp = (void**)regs.esp; + + va_start(ap, num); + + for (int i = 1; i <= num; i++) + { + sp[i] = va_arg(ap, void*); + } + + regs.eip = (U_32)fn; + + return port_setstack_stub(®s); +}