ntdll: Take stack guarantee into account when handling stack overflows.

Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Alexandre Julliard 2019-08-24 12:24:09 +02:00
parent 505be3a0a2
commit 87d9fef2ae
6 changed files with 58 additions and 41 deletions

View file

@ -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;

View file

@ -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 );

View file

@ -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 );

View file

@ -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 );

View file

@ -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 );

View file

@ -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;