From 87d9fef2ae3a10f08a6a76b5e4cf542390ea3ae0 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Sat, 24 Aug 2019 12:24:09 +0200 Subject: [PATCH] ntdll: Take stack guarantee into account when handling stack overflows. Signed-off-by: Alexandre Julliard --- dlls/ntdll/ntdll_misc.h | 2 +- dlls/ntdll/signal_arm.c | 13 ++++++------- dlls/ntdll/signal_arm64.c | 13 ++++++------- dlls/ntdll/signal_i386.c | 22 +++++++++++++--------- dlls/ntdll/signal_x86_64.c | 22 +++++++++++++--------- dlls/ntdll/virtual.c | 27 +++++++++++++++++++-------- 6 files changed, 58 insertions(+), 41 deletions(-) diff --git a/dlls/ntdll/ntdll_misc.h b/dlls/ntdll/ntdll_misc.h index 62e9d080656..04aa15636d1 100644 --- a/dlls/ntdll/ntdll_misc.h +++ b/dlls/ntdll/ntdll_misc.h @@ -177,7 +177,7 @@ extern NTSTATUS virtual_create_builtin_view( void *base ) DECLSPEC_HIDDEN; extern NTSTATUS virtual_alloc_thread_stack( INITIAL_TEB *stack, SIZE_T reserve_size, SIZE_T commit_size, SIZE_T *pthread_size ) DECLSPEC_HIDDEN; extern void virtual_clear_thread_stack( void *stack_end ) DECLSPEC_HIDDEN; -extern BOOL virtual_handle_stack_fault( void *addr ) DECLSPEC_HIDDEN; +extern int virtual_handle_stack_fault( void *addr ) DECLSPEC_HIDDEN; extern BOOL virtual_is_valid_code_address( const void *addr, SIZE_T size ) DECLSPEC_HIDDEN; extern NTSTATUS virtual_handle_fault( LPCVOID addr, DWORD err, BOOL on_signal_stack ) DECLSPEC_HIDDEN; extern unsigned int virtual_locked_server_call( void *req_ptr ) DECLSPEC_HIDDEN; diff --git a/dlls/ntdll/signal_arm.c b/dlls/ntdll/signal_arm.c index ae0b4932a9e..3de6a0c45a5 100644 --- a/dlls/ntdll/signal_arm.c +++ b/dlls/ntdll/signal_arm.c @@ -721,18 +721,17 @@ static void segv_handler( int signal, siginfo_t *info, void *ucontext ) ucontext_t *context = ucontext; /* check for page fault inside the thread stack */ - if (get_trap_code(signal, context) == TRAP_ARM_PAGEFLT && - (char *)info->si_addr >= (char *)NtCurrentTeb()->DeallocationStack && - (char *)info->si_addr < (char *)NtCurrentTeb()->Tib.StackBase && - virtual_handle_stack_fault( info->si_addr )) + if (get_trap_code(signal, context) == TRAP_ARM_PAGEFLT) { - /* check if this was the last guard page */ - if ((char *)info->si_addr < (char *)NtCurrentTeb()->DeallocationStack + 2*4096) + switch (virtual_handle_stack_fault( info->si_addr )) { + case 1: /* handled */ + return; + case -1: /* overflow */ rec = setup_exception( context, raise_segv_exception ); rec->ExceptionCode = EXCEPTION_STACK_OVERFLOW; + return; } - return; } rec = setup_exception( context, raise_segv_exception ); diff --git a/dlls/ntdll/signal_arm64.c b/dlls/ntdll/signal_arm64.c index d9a43e1e764..f535c893d31 100644 --- a/dlls/ntdll/signal_arm64.c +++ b/dlls/ntdll/signal_arm64.c @@ -669,18 +669,17 @@ static void segv_handler( int signal, siginfo_t *info, void *ucontext ) ucontext_t *context = ucontext; /* check for page fault inside the thread stack */ - if (signal == SIGSEGV && - (char *)info->si_addr >= (char *)NtCurrentTeb()->DeallocationStack && - (char *)info->si_addr < (char *)NtCurrentTeb()->Tib.StackBase && - virtual_handle_stack_fault( info->si_addr )) + if (signal == SIGSEGV) { - /* check if this was the last guard page */ - if ((char *)info->si_addr < (char *)NtCurrentTeb()->DeallocationStack + 2*4096) + switch (virtual_handle_stack_fault( info->si_addr )) { + case 1: /* handled */ + return; + case -1: /* overflow */ rec = setup_exception( context, raise_segv_exception ); rec->ExceptionCode = EXCEPTION_STACK_OVERFLOW; + return; } - return; } rec = setup_exception( context, raise_segv_exception ); diff --git a/dlls/ntdll/signal_i386.c b/dlls/ntdll/signal_i386.c index b2925062bf7..a43599e3750 100644 --- a/dlls/ntdll/signal_i386.c +++ b/dlls/ntdll/signal_i386.c @@ -1822,8 +1822,9 @@ static EXCEPTION_RECORD *setup_exception_record( ucontext_t *sigcontext, void *s else if ((char *)(stack - 1) < (char *)NtCurrentTeb()->Tib.StackLimit) { /* stack access below stack limit, may be recoverable */ - if (virtual_handle_stack_fault( stack - 1 )) exception_code = EXCEPTION_STACK_OVERFLOW; - else + switch (virtual_handle_stack_fault( stack - 1 )) + { + case 0: /* not handled */ { UINT diff = (char *)NtCurrentTeb()->Tib.StackLimit - (char *)(stack - 1); WINE_ERR( "stack overflow %u bytes in thread %04x eip %08x esp %08x stack %p-%p-%p\n", @@ -1832,6 +1833,10 @@ static EXCEPTION_RECORD *setup_exception_record( ucontext_t *sigcontext, void *s NtCurrentTeb()->Tib.StackLimit, NtCurrentTeb()->Tib.StackBase ); abort_thread(1); } + case -1: /* overflow */ + exception_code = EXCEPTION_STACK_OVERFLOW; + break; + } } stack--; /* push the stack_layout structure */ @@ -2051,18 +2056,17 @@ static void segv_handler( int signal, siginfo_t *siginfo, void *sigcontext ) } /* check for page fault inside the thread stack */ - if (get_trap_code(context) == TRAP_x86_PAGEFLT && - (char *)siginfo->si_addr >= (char *)NtCurrentTeb()->DeallocationStack && - (char *)siginfo->si_addr < (char *)NtCurrentTeb()->Tib.StackBase && - virtual_handle_stack_fault( siginfo->si_addr )) + if (get_trap_code(context) == TRAP_x86_PAGEFLT) { - /* check if this was the last guard page */ - if ((char *)siginfo->si_addr < (char *)NtCurrentTeb()->DeallocationStack + 2*4096) + switch (virtual_handle_stack_fault( siginfo->si_addr )) { + case 1: /* handled */ + return; + case -1: /* overflow */ rec = setup_exception_record( context, stack, fs, gs, raise_segv_exception ); rec->ExceptionCode = EXCEPTION_STACK_OVERFLOW; + return; } - return; } rec = setup_exception_record( context, stack, fs, gs, raise_segv_exception ); diff --git a/dlls/ntdll/signal_x86_64.c b/dlls/ntdll/signal_x86_64.c index bf30815bf00..a0cff30b3ee 100644 --- a/dlls/ntdll/signal_x86_64.c +++ b/dlls/ntdll/signal_x86_64.c @@ -2413,8 +2413,9 @@ static EXCEPTION_RECORD *setup_exception( ucontext_t *sigcontext, raise_func fun else if ((char *)(stack - 1) < (char *)NtCurrentTeb()->Tib.StackLimit) { /* stack access below stack limit, may be recoverable */ - if (virtual_handle_stack_fault( stack - 1 )) exception_code = EXCEPTION_STACK_OVERFLOW; - else + switch (virtual_handle_stack_fault( stack - 1 )) + { + case 0: /* not handled */ { UINT diff = (char *)NtCurrentTeb()->Tib.StackLimit - (char *)(stack - 1); ERR( "stack overflow %u bytes in thread %04x eip %016lx esp %016lx stack %p-%p-%p\n", @@ -2423,6 +2424,10 @@ static EXCEPTION_RECORD *setup_exception( ucontext_t *sigcontext, raise_func fun NtCurrentTeb()->Tib.StackLimit, NtCurrentTeb()->Tib.StackBase ); abort_thread(1); } + case -1: /* overflow */ + exception_code = EXCEPTION_STACK_OVERFLOW; + break; + } } stack--; /* push the stack_layout structure */ @@ -2940,18 +2945,17 @@ static void segv_handler( int signal, siginfo_t *siginfo, void *sigcontext ) ucontext_t *ucontext = sigcontext; /* check for page fault inside the thread stack */ - if (TRAP_sig(ucontext) == TRAP_x86_PAGEFLT && - (char *)siginfo->si_addr >= (char *)NtCurrentTeb()->DeallocationStack && - (char *)siginfo->si_addr < (char *)NtCurrentTeb()->Tib.StackBase && - virtual_handle_stack_fault( siginfo->si_addr )) + if (TRAP_sig(ucontext) == TRAP_x86_PAGEFLT) { - /* check if this was the last guard page */ - if ((char *)siginfo->si_addr < (char *)NtCurrentTeb()->DeallocationStack + 2*4096) + switch (virtual_handle_stack_fault( siginfo->si_addr )) { + case 1: /* handled */ + return; + case -1: /* overflow */ rec = setup_exception( sigcontext, raise_segv_exception ); rec->ExceptionCode = EXCEPTION_STACK_OVERFLOW; + return; } - return; } rec = setup_exception( sigcontext, raise_segv_exception ); diff --git a/dlls/ntdll/virtual.c b/dlls/ntdll/virtual.c index f8d80b616e4..d15b49f6fdf 100644 --- a/dlls/ntdll/virtual.c +++ b/dlls/ntdll/virtual.c @@ -2269,26 +2269,37 @@ BOOL virtual_is_valid_code_address( const void *addr, SIZE_T size ) * virtual_handle_stack_fault * * Handle an access fault inside the current thread stack. + * Return 1 if safely handled, -1 if handled into the overflow space. * Called from inside a signal handler. */ -BOOL virtual_handle_stack_fault( void *addr ) +int virtual_handle_stack_fault( void *addr ) { - BOOL ret = FALSE; + int ret = 0; + + if ((char *)addr < (char *)NtCurrentTeb()->DeallocationStack) return 0; + if ((char *)addr >= (char *)NtCurrentTeb()->Tib.StackBase) return 0; RtlEnterCriticalSection( &csVirtual ); /* no need for signal masking inside signal handler */ if (get_page_vprot( addr ) & VPROT_GUARD) { + size_t guaranteed = max( NtCurrentTeb()->GuaranteedStackBytes, page_size * (is_win64 ? 2 : 1) ); char *page = ROUND_ADDR( addr, page_mask ); set_page_vprot_bits( page, page_size, 0, VPROT_GUARD ); mprotect_range( page, page_size, 0, 0 ); - NtCurrentTeb()->Tib.StackLimit = page; - if (page >= (char *)NtCurrentTeb()->DeallocationStack + 2*page_size) + if (page >= (char *)NtCurrentTeb()->DeallocationStack + page_size + guaranteed) { - page -= page_size; - set_page_vprot_bits( page, page_size, VPROT_COMMITTED | VPROT_GUARD, 0 ); - mprotect_range( page, page_size, 0, 0 ); + set_page_vprot_bits( page - page_size, page_size, VPROT_COMMITTED | VPROT_GUARD, 0 ); + mprotect_range( page - page_size, page_size, 0, 0 ); + ret = 1; } - ret = TRUE; + else /* inside guaranteed space -> overflow exception */ + { + page = (char *)NtCurrentTeb()->DeallocationStack + page_size; + set_page_vprot_bits( page, guaranteed, VPROT_COMMITTED, VPROT_GUARD ); + mprotect_range( page, guaranteed, 0, 0 ); + ret = -1; + } + NtCurrentTeb()->Tib.StackLimit = page; } RtlLeaveCriticalSection( &csVirtual ); return ret;