Alexandre Julliard : ntdll: Add helper functions for verifying write access to a memory range.

Alexandre Julliard julliard at winehq.org
Fri Sep 22 15:49:06 CDT 2017


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

Author: Alexandre Julliard <julliard at winehq.org>
Date:   Fri Sep 22 14:56:56 2017 +0200

ntdll: Add helper functions for verifying write access to a memory range.

Signed-off-by: Alexandre Julliard <julliard at winehq.org>

---

 dlls/ntdll/virtual.c | 62 ++++++++++++++++++++++++++++++++++++----------------
 1 file changed, 43 insertions(+), 19 deletions(-)

diff --git a/dlls/ntdll/virtual.c b/dlls/ntdll/virtual.c
index 1e6b8a7..d36dd7d 100644
--- a/dlls/ntdll/virtual.c
+++ b/dlls/ntdll/virtual.c
@@ -838,9 +838,10 @@ static inline int mprotect_exec( void *base, size_t size, int unix_prot )
 static void mprotect_range( void *base, size_t size, BYTE set, BYTE clear )
 {
     size_t i, count;
-    char *addr = base;
+    char *addr = ROUND_ADDR( base, page_mask );
     int prot, next;
 
+    size = ROUND_SIZE( base, size );
     prot = VIRTUAL_GetUnixProt( (get_page_vprot( addr ) & ~clear ) | set );
     for (count = i = 1; i < size >> page_shift; i++, count++)
     {
@@ -925,6 +926,19 @@ static NTSTATUS set_protection( struct file_view *view, void *base, SIZE_T size,
 
 
 /***********************************************************************
+ *           update_write_watches
+ */
+static void update_write_watches( void *base, size_t size, size_t accessed_size )
+{
+    TRACE( "updating watch %p-%p-%p\n", base, (char *)base + accessed_size, (char *)base + size );
+    /* clear write watch flag on accessed pages */
+    set_page_vprot_bits( base, accessed_size, 0, VPROT_WRITEWATCH );
+    /* restore page protections on the entire range */
+    mprotect_range( base, size, 0, 0 );
+}
+
+
+/***********************************************************************
  *           reset_write_watches
  *
  * Reset write watches in a memory range.
@@ -1800,6 +1814,30 @@ NTSTATUS virtual_handle_fault( LPCVOID addr, DWORD err, BOOL on_signal_stack )
 }
 
 
+/***********************************************************************
+ *           check_write_access
+ *
+ * Check if the memory range is writable, temporarily disabling write watches if necessary.
+ */
+static NTSTATUS check_write_access( void *base, size_t size, BOOL *has_write_watch )
+{
+    size_t i;
+    char *addr = ROUND_ADDR( base, page_mask );
+
+    size = ROUND_SIZE( base, size );
+    for (i = 0; i < size; i += page_size)
+    {
+        BYTE vprot = get_page_vprot( addr + i );
+        if (vprot & VPROT_WRITEWATCH) *has_write_watch = TRUE;
+        if (!(VIRTUAL_GetUnixProt( vprot & ~VPROT_WRITEWATCH ) & PROT_WRITE))
+            return STATUS_INVALID_USER_BUFFER;
+    }
+    if (*has_write_watch)
+        mprotect_range( addr, size, 0, VPROT_WRITEWATCH );  /* temporarily enable write access */
+    return STATUS_SUCCESS;
+}
+
+
 
 /***********************************************************************
  *           virtual_is_valid_code_address
@@ -1963,32 +2001,18 @@ SIZE_T virtual_uninterrupted_read_memory( const void *addr, void *buffer, SIZE_T
  */
 NTSTATUS virtual_uninterrupted_write_memory( void *addr, const void *buffer, SIZE_T size )
 {
-    struct file_view *view;
+    BOOL has_write_watch = FALSE;
     sigset_t sigset;
-    NTSTATUS ret = STATUS_ACCESS_VIOLATION;
+    NTSTATUS ret;
 
     if (!size) return STATUS_SUCCESS;
 
     server_enter_uninterrupted_section( &csVirtual, &sigset );
-    if ((view = VIRTUAL_FindView( addr, size )) && !(view->protect & VPROT_SYSTEM))
+    if (!(ret = check_write_access( addr, size, &has_write_watch )))
     {
-        char *page = ROUND_ADDR( addr, page_mask );
-        size_t i, total = ROUND_SIZE( addr, size );
-
-        for (i = 0; i < total; i += page_size)
-        {
-            int prot = VIRTUAL_GetUnixProt( get_page_vprot( page + i ) & ~VPROT_WRITEWATCH );
-            if (!(prot & PROT_WRITE)) goto done;
-        }
-        if (view->protect & VPROT_WRITEWATCH)  /* enable write access by clearing write watches */
-        {
-            set_page_vprot_bits( addr, size, 0, VPROT_WRITEWATCH );
-            mprotect_range( addr, size, 0, 0 );
-        }
         memcpy( addr, buffer, size );
-        ret = STATUS_SUCCESS;
+        if (has_write_watch) update_write_watches( addr, size, size );
     }
-done:
     server_leave_uninterrupted_section( &csVirtual, &sigset );
     return ret;
 }




More information about the wine-cvs mailing list