Sebastian Lackner : ntdll: Improve check_atl_thunk to prevent passing exceptions to the usermode application.

Alexandre Julliard julliard at wine.codeweavers.com
Wed Oct 15 11:37:24 CDT 2014


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

Author: Sebastian Lackner <sebastian at fds-team.de>
Date:   Tue Oct 14 06:22:55 2014 +0200

ntdll: Improve check_atl_thunk to prevent passing exceptions to the usermode application.

---

 dlls/kernel32/tests/virtual.c |  1 -
 dlls/ntdll/ntdll_misc.h       |  2 ++
 dlls/ntdll/signal_i386.c      | 20 +++++------
 dlls/ntdll/virtual.c          | 78 +++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 89 insertions(+), 12 deletions(-)

diff --git a/dlls/kernel32/tests/virtual.c b/dlls/kernel32/tests/virtual.c
index d258190..73b753e 100644
--- a/dlls/kernel32/tests/virtual.c
+++ b/dlls/kernel32/tests/virtual.c
@@ -2071,7 +2071,6 @@ static void test_atl_thunk_emulation( ULONG dep_flags )
             pRtlRemoveVectoredExceptionHandler( vectored_handler );
 
             ok( ret == 43, "call returned wrong result, expected 43, got %d\n", ret );
-            todo_wine
             ok( num_execute_fault_calls == 1, "expected one STATUS_ACCESS_VIOLATION exception, got %d exceptions\n", num_execute_fault_calls );
         }
         else
diff --git a/dlls/ntdll/ntdll_misc.h b/dlls/ntdll/ntdll_misc.h
index 4370084..aac9320 100644
--- a/dlls/ntdll/ntdll_misc.h
+++ b/dlls/ntdll/ntdll_misc.h
@@ -169,6 +169,8 @@ extern BOOL virtual_is_valid_code_address( const void *addr, SIZE_T size ) DECLS
 extern NTSTATUS virtual_handle_fault( LPCVOID addr, DWORD err ) DECLSPEC_HIDDEN;
 extern BOOL virtual_check_buffer_for_read( const void *ptr, SIZE_T size ) DECLSPEC_HIDDEN;
 extern BOOL virtual_check_buffer_for_write( void *ptr, SIZE_T size ) DECLSPEC_HIDDEN;
+extern SIZE_T virtual_uninterrupted_read_memory( const void *addr, void *buffer, SIZE_T size ) DECLSPEC_HIDDEN;
+extern SIZE_T virtual_uninterrupted_write_memory( void *addr, const void *buffer, SIZE_T size ) DECLSPEC_HIDDEN;
 extern void VIRTUAL_SetForceExec( BOOL enable ) DECLSPEC_HIDDEN;
 extern void virtual_release_address_space(void) DECLSPEC_HIDDEN;
 extern void virtual_set_large_address_space(void) DECLSPEC_HIDDEN;
diff --git a/dlls/ntdll/signal_i386.c b/dlls/ntdll/signal_i386.c
index 7f01554..f97799c 100644
--- a/dlls/ntdll/signal_i386.c
+++ b/dlls/ntdll/signal_i386.c
@@ -1633,26 +1633,24 @@ struct atl_thunk
 static BOOL check_atl_thunk( EXCEPTION_RECORD *rec, CONTEXT *context )
 {
     const struct atl_thunk *thunk = (const struct atl_thunk *)rec->ExceptionInformation[1];
+    struct atl_thunk thunk_copy;
     BOOL ret = FALSE;
 
-    if (!virtual_is_valid_code_address( thunk, sizeof(*thunk) )) return FALSE;
+    if (virtual_uninterrupted_read_memory( thunk, &thunk_copy, sizeof(*thunk) ) != sizeof(*thunk))
+        return FALSE;
 
-    __TRY
+    if (thunk_copy.movl == 0x042444c7 && thunk_copy.jmp == 0xe9)
     {
-        if (thunk->movl == 0x042444c7 && thunk->jmp == 0xe9)
+        if (virtual_uninterrupted_write_memory( (DWORD *)context->Esp + 1,
+            &thunk_copy.this, sizeof(DWORD) ) == sizeof(DWORD))
         {
-            *((DWORD *)context->Esp + 1) = thunk->this;
-            context->Eip = (DWORD_PTR)(&thunk->func + 1) + thunk->func;
+            context->Eip = (DWORD_PTR)(&thunk->func + 1) + thunk_copy.func;
             TRACE( "emulating ATL thunk at %p, func=%08x arg=%08x\n",
-                   thunk, context->Eip, *((DWORD *)context->Esp + 1) );
+                   thunk, context->Eip, thunk_copy.this );
             ret = TRUE;
         }
     }
-    __EXCEPT_PAGE_FAULT
-    {
-        return FALSE;
-    }
-    __ENDTRY
+
     return ret;
 }
 
diff --git a/dlls/ntdll/virtual.c b/dlls/ntdll/virtual.c
index d2bb152..4c4c05d 100644
--- a/dlls/ntdll/virtual.c
+++ b/dlls/ntdll/virtual.c
@@ -1672,6 +1672,84 @@ BOOL virtual_check_buffer_for_write( void *ptr, SIZE_T size )
 
 
 /***********************************************************************
+ *           virtual_uninterrupted_read_memory
+ *
+ * Similar to NtReadVirtualMemory, but without wineserver calls. Moreover
+ * permissions are checked before accessing each page, to ensure that no
+ * exceptions can happen.
+ */
+SIZE_T virtual_uninterrupted_read_memory( const void *addr, void *buffer, SIZE_T size )
+{
+    struct file_view *view;
+    sigset_t sigset;
+    SIZE_T bytes_read = 0;
+
+    if (!size) return 0;
+
+    server_enter_uninterrupted_section( &csVirtual, &sigset );
+    if ((view = VIRTUAL_FindView( addr, size )))
+    {
+        if (!(view->protect & VPROT_SYSTEM))
+        {
+            void *page = ROUND_ADDR( addr, page_mask );
+            BYTE *p = view->prot + (((const char *)page - (const char *)view->base) >> page_shift);
+
+            while (bytes_read < size && (VIRTUAL_GetUnixProt( *p++ ) & PROT_READ))
+            {
+                SIZE_T block_size = min( size, page_size - ((UINT_PTR)addr & page_mask) );
+                memcpy( buffer, addr, block_size );
+
+                addr   = (const void *)((const char *)addr + block_size);
+                buffer = (void *)((char *)buffer + block_size);
+                bytes_read += block_size;
+            }
+        }
+    }
+    server_leave_uninterrupted_section( &csVirtual, &sigset );
+    return bytes_read;
+}
+
+
+/***********************************************************************
+ *           virtual_uninterrupted_write_memory
+ *
+ * Similar to NtWriteVirtualMemory, but without wineserver calls. Moreover
+ * permissions are checked before accessing each page, to ensure that no
+ * exceptions can happen.
+ */
+SIZE_T virtual_uninterrupted_write_memory( void *addr, const void *buffer, SIZE_T size )
+{
+    struct file_view *view;
+    sigset_t sigset;
+    SIZE_T bytes_written = 0;
+
+    if (!size) return 0;
+
+    server_enter_uninterrupted_section( &csVirtual, &sigset );
+    if ((view = VIRTUAL_FindView( addr, size )))
+    {
+        if (!(view->protect & VPROT_SYSTEM))
+        {
+            void *page = ROUND_ADDR( addr, page_mask );
+            BYTE *p = view->prot + (((const char *)page - (const char *)view->base) >> page_shift);
+
+            while (bytes_written < size && (VIRTUAL_GetUnixProt( *p++ ) & PROT_WRITE))
+            {
+                SIZE_T block_size = min( size, page_size - ((UINT_PTR)addr & page_mask) );
+                memcpy( addr, buffer, block_size );
+
+                addr   = (void *)((char *)addr + block_size);
+                buffer = (const void *)((const char *)buffer + block_size);
+                bytes_written += block_size;
+            }
+        }
+    }
+    server_leave_uninterrupted_section( &csVirtual, &sigset );
+    return bytes_written;
+}
+
+
+/***********************************************************************
  *           VIRTUAL_SetForceExec
  *
  * Whether to force exec prot on all views.




More information about the wine-cvs mailing list