ntdll: Support growing both 32- and 64-bit stacks.

Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Alexandre Julliard 2021-06-17 09:25:19 +02:00
parent e91d19a58a
commit de89ce1bfe
1 changed files with 63 additions and 27 deletions

View File

@ -3131,29 +3131,67 @@ void virtual_map_user_shared_data(void)
}
struct thread_stack_info
{
char *start;
char *limit;
char *end;
SIZE_T guaranteed;
BOOL is_wow;
};
/***********************************************************************
* is_inside_thread_stack
*/
static BOOL is_inside_thread_stack( void *ptr, struct thread_stack_info *stack )
{
TEB *teb = NtCurrentTeb();
WOW_TEB *wow_teb = get_wow_teb( teb );
stack->start = teb->DeallocationStack;
stack->limit = teb->Tib.StackLimit;
stack->end = teb->Tib.StackBase;
stack->guaranteed = max( teb->GuaranteedStackBytes, page_size * (is_win64 ? 2 : 1) );
stack->is_wow = FALSE;
if ((char *)ptr > stack->start && (char *)ptr <= stack->end) return TRUE;
if (!wow_teb) return FALSE;
stack->start = ULongToPtr( wow_teb->DeallocationStack );
stack->limit = ULongToPtr( wow_teb->Tib.StackLimit );
stack->end = ULongToPtr( wow_teb->Tib.StackBase );
stack->guaranteed = max( wow_teb->GuaranteedStackBytes, page_size * (is_win64 ? 1 : 2) );
stack->is_wow = TRUE;
return ((char *)ptr > stack->start && (char *)ptr <= stack->end);
}
/***********************************************************************
* grow_thread_stack
*/
static NTSTATUS grow_thread_stack( char *page )
static NTSTATUS grow_thread_stack( char *page, struct thread_stack_info *stack_info )
{
NTSTATUS ret = 0;
size_t guaranteed = max( NtCurrentTeb()->GuaranteedStackBytes, page_size * (is_win64 ? 2 : 1) );
set_page_vprot_bits( page, page_size, 0, VPROT_GUARD );
mprotect_range( page, page_size, 0, 0 );
if (page >= (char *)NtCurrentTeb()->DeallocationStack + page_size + guaranteed)
if (page >= stack_info->start + page_size + stack_info->guaranteed)
{
set_page_vprot_bits( page - page_size, page_size, VPROT_COMMITTED | VPROT_GUARD, 0 );
mprotect_range( page - page_size, page_size, 0, 0 );
}
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 );
page = stack_info->start + page_size;
set_page_vprot_bits( page, stack_info->guaranteed, VPROT_COMMITTED, VPROT_GUARD );
mprotect_range( page, stack_info->guaranteed, 0, 0 );
ret = STATUS_STACK_OVERFLOW;
}
NtCurrentTeb()->Tib.StackLimit = page;
if (stack_info->is_wow)
{
WOW_TEB *wow_teb = get_wow_teb( NtCurrentTeb() );
wow_teb->Tib.StackLimit = PtrToUlong( page );
}
else NtCurrentTeb()->Tib.StackLimit = page;
return ret;
}
@ -3171,14 +3209,14 @@ NTSTATUS virtual_handle_fault( void *addr, DWORD err, void *stack )
vprot = get_page_vprot( page );
if (!is_inside_signal_stack( stack ) && (vprot & VPROT_GUARD))
{
if (page < (char *)NtCurrentTeb()->DeallocationStack ||
page >= (char *)NtCurrentTeb()->Tib.StackBase)
struct thread_stack_info stack_info;
if (!is_inside_thread_stack( page, &stack_info ))
{
set_page_vprot_bits( page, page_size, 0, VPROT_GUARD );
mprotect_range( page, page_size, 0, 0 );
ret = STATUS_GUARD_PAGE_VIOLATION;
}
else ret = grow_thread_stack( page );
else ret = grow_thread_stack( page, &stack_info );
}
else if (err & EXCEPTION_WRITE_FAULT)
{
@ -3205,19 +3243,16 @@ NTSTATUS virtual_handle_fault( void *addr, DWORD err, void *stack )
void *virtual_setup_exception( void *stack_ptr, size_t size, EXCEPTION_RECORD *rec )
{
char *stack = stack_ptr;
struct thread_stack_info stack_info;
if (is_inside_signal_stack( stack ))
{
ERR( "nested exception on signal stack in thread %04x addr %p stack %p (%p-%p-%p)\n",
GetCurrentThreadId(), rec->ExceptionAddress, stack, NtCurrentTeb()->DeallocationStack,
NtCurrentTeb()->Tib.StackLimit, NtCurrentTeb()->Tib.StackBase );
abort_thread(1);
}
if (stack - size > stack || /* check for overflow in subtraction */
stack <= (char *)NtCurrentTeb()->DeallocationStack ||
stack > (char *)NtCurrentTeb()->Tib.StackBase)
if (!is_inside_thread_stack( stack, &stack_info ))
{
if (is_inside_signal_stack( stack ))
{
ERR( "nested exception on signal stack in thread %04x addr %p stack %p\n",
GetCurrentThreadId(), rec->ExceptionAddress, stack );
abort_thread(1);
}
WARN( "exception outside of stack limits in thread %04x addr %p stack %p (%p-%p-%p)\n",
GetCurrentThreadId(), rec->ExceptionAddress, stack, NtCurrentTeb()->DeallocationStack,
NtCurrentTeb()->Tib.StackLimit, NtCurrentTeb()->Tib.StackBase );
@ -3226,19 +3261,20 @@ void *virtual_setup_exception( void *stack_ptr, size_t size, EXCEPTION_RECORD *r
stack -= size;
if (stack < (char *)NtCurrentTeb()->DeallocationStack + 4096)
if (stack < stack_info.start + 4096)
{
/* stack overflow on last page, unrecoverable */
UINT diff = (char *)NtCurrentTeb()->DeallocationStack + 4096 - stack;
UINT diff = stack_info.start + 4096 - stack;
ERR( "stack overflow %u bytes in thread %04x addr %p stack %p (%p-%p-%p)\n",
diff, GetCurrentThreadId(), rec->ExceptionAddress, stack, NtCurrentTeb()->DeallocationStack,
NtCurrentTeb()->Tib.StackLimit, NtCurrentTeb()->Tib.StackBase );
diff, GetCurrentThreadId(), rec->ExceptionAddress, stack, stack_info.start,
stack_info.limit, stack_info.end );
abort_thread(1);
}
else if (stack < (char *)NtCurrentTeb()->Tib.StackLimit)
else if (stack < stack_info.limit)
{
mutex_lock( &virtual_mutex ); /* no need for signal masking inside signal handler */
if ((get_page_vprot( stack ) & VPROT_GUARD) && grow_thread_stack( ROUND_ADDR( stack, page_mask )))
if ((get_page_vprot( stack ) & VPROT_GUARD) &&
grow_thread_stack( ROUND_ADDR( stack, page_mask ), &stack_info ))
{
rec->ExceptionCode = STATUS_STACK_OVERFLOW;
rec->NumberParameters = 0;