Alexandre Julliard : ntdll: Add support for handling page faults caused by guard pages on the thread stack .

Alexandre Julliard julliard at winehq.org
Tue Apr 1 16:44:10 CDT 2008


Module: wine
Branch: master
Commit: 061bfac076b1c191b123f64f51579cc9ea8b68cc
URL:    http://source.winehq.org/git/wine.git/?a=commit;h=061bfac076b1c191b123f64f51579cc9ea8b68cc

Author: Alexandre Julliard <julliard at winehq.org>
Date:   Tue Apr  1 17:37:17 2008 +0200

ntdll: Add support for handling page faults caused by guard pages on the thread stack.

---

 dlls/ntdll/ntdll_misc.h  |    1 +
 dlls/ntdll/signal_i386.c |   44 +++++++++++++++++++++++++++++++++-----------
 dlls/ntdll/virtual.c     |   43 +++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 77 insertions(+), 11 deletions(-)

diff --git a/dlls/ntdll/ntdll_misc.h b/dlls/ntdll/ntdll_misc.h
index 3a12b8f..cba9643 100644
--- a/dlls/ntdll/ntdll_misc.h
+++ b/dlls/ntdll/ntdll_misc.h
@@ -134,6 +134,7 @@ extern unsigned int DIR_get_drives_info( struct drive_info info[MAX_DOS_DRIVES]
 
 /* virtual memory */
 extern NTSTATUS virtual_alloc_thread_stack( void *base, SIZE_T stack_size );
+extern BOOL virtual_handle_stack_fault( void *addr );
 extern NTSTATUS VIRTUAL_HandleFault(LPCVOID addr);
 extern void VIRTUAL_SetForceExec( BOOL enable );
 extern void VIRTUAL_UseLargeAddressSpace(void);
diff --git a/dlls/ntdll/signal_i386.c b/dlls/ntdll/signal_i386.c
index bb8332a..7920d0a 100644
--- a/dlls/ntdll/signal_i386.c
+++ b/dlls/ntdll/signal_i386.c
@@ -1009,13 +1009,12 @@ static BOOL check_atl_thunk( EXCEPTION_RECORD *rec, CONTEXT *context )
 
 
 /***********************************************************************
- *           setup_exception
+ *           setup_exception_record
  *
- * Setup a proper stack frame for the raise function, and modify the
- * sigcontext so that the return from the signal handler will call
- * the raise function.
+ * Setup the exception record and context on the thread stack.
  */
-static EXCEPTION_RECORD *setup_exception( SIGCONTEXT *sigcontext, raise_func func )
+static EXCEPTION_RECORD *setup_exception_record( SIGCONTEXT *sigcontext, void *stack_ptr,
+                                                 WORD fs, WORD gs, raise_func func )
 {
     struct stack_layout
     {
@@ -1026,11 +1025,7 @@ static EXCEPTION_RECORD *setup_exception( SIGCONTEXT *sigcontext, raise_func fun
         EXCEPTION_RECORD  rec;
         DWORD             ebp;
         DWORD             eip;
-    } *stack;
-
-    WORD fs, gs;
-
-    stack = init_handler( sigcontext, &fs, &gs );
+    } *stack = stack_ptr;
 
     /* stack sanity checks */
 
@@ -1097,6 +1092,22 @@ static EXCEPTION_RECORD *setup_exception( SIGCONTEXT *sigcontext, raise_func fun
 
 
 /***********************************************************************
+ *           setup_exception
+ *
+ * Setup a proper stack frame for the raise function, and modify the
+ * sigcontext so that the return from the signal handler will call
+ * the raise function.
+ */
+static EXCEPTION_RECORD *setup_exception( SIGCONTEXT *sigcontext, raise_func func )
+{
+    WORD fs, gs;
+    void *stack = init_handler( sigcontext, &fs, &gs );
+
+    return setup_exception_record( sigcontext, stack, fs, gs, func );
+}
+
+
+/***********************************************************************
  *           get_exception_context
  *
  * Get a pointer to the context built by setup_exception.
@@ -1259,8 +1270,19 @@ static void usr2_handler( int signal, siginfo_t *siginfo, void *sigcontext )
  */
 static void segv_handler( int signal, siginfo_t *siginfo, void *sigcontext )
 {
+    WORD fs, gs;
+    EXCEPTION_RECORD *rec;
     SIGCONTEXT *context = sigcontext;
-    EXCEPTION_RECORD *rec = setup_exception( context, raise_segv_exception );
+    void *stack = init_handler( sigcontext, &fs, &gs );
+
+    /* 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 ))
+        return;
+
+    rec = setup_exception_record( context, stack, fs, gs, raise_segv_exception );
 
     switch(get_trap_code(context))
     {
diff --git a/dlls/ntdll/virtual.c b/dlls/ntdll/virtual.c
index 548e819..5e4327b 100644
--- a/dlls/ntdll/virtual.c
+++ b/dlls/ntdll/virtual.c
@@ -571,6 +571,19 @@ static BOOL VIRTUAL_SetProt( FILE_VIEW *view, /* [in] Pointer to view */
     TRACE("%p-%p %s\n",
           base, (char *)base + size - 1, VIRTUAL_GetProtStr( vprot ) );
 
+    /* if setting stack guard pages, store the permissions first, as the guard may be
+     * triggered at any point after mprotect and change the permissions again */
+    if ((vprot & VPROT_GUARD) &&
+        ((char *)base >= (char *)NtCurrentTeb()->DeallocationStack) &&
+        ((char *)base < (char *)NtCurrentTeb()->Tib.StackBase))
+    {
+        memset( view->prot + (((char *)base - (char *)view->base) >> page_shift),
+                vprot, size >> page_shift );
+        mprotect( base, size, unix_prot );
+        VIRTUAL_DEBUG_DUMP_VIEW( view );
+        return TRUE;
+    }
+
     if (force_exec_prot && (unix_prot & PROT_READ) && !(unix_prot & PROT_EXEC))
     {
         TRACE( "forcing exec permission on %p-%p\n", base, (char *)base + size - 1 );
@@ -1327,6 +1340,36 @@ NTSTATUS VIRTUAL_HandleFault( LPCVOID addr )
 }
 
 
+
+/***********************************************************************
+ *           virtual_handle_stack_fault
+ *
+ * Handle an access fault inside the current thread stack.
+ * Called from inside a signal handler.
+ */
+BOOL virtual_handle_stack_fault( void *addr )
+{
+    FILE_VIEW *view;
+    BOOL ret = FALSE;
+
+    RtlEnterCriticalSection( &csVirtual );  /* no need for signal masking inside signal handler */
+    if ((view = VIRTUAL_FindView( addr )))
+    {
+        void *page = ROUND_ADDR( addr, page_mask );
+        BYTE vprot = view->prot[((const char *)page - (const char *)view->base) >> page_shift];
+        if (vprot & VPROT_GUARD)
+        {
+            VIRTUAL_SetProt( view, page, page_size, vprot & ~VPROT_GUARD );
+            if ((char *)page + page_size == NtCurrentTeb()->Tib.StackLimit)
+                NtCurrentTeb()->Tib.StackLimit = page;
+            ret = TRUE;
+        }
+    }
+    RtlLeaveCriticalSection( &csVirtual );
+    return ret;
+}
+
+
 /***********************************************************************
  *           VIRTUAL_SetForceExec
  *




More information about the wine-cvs mailing list