From 065015c3bc777193f4d44ca7969995a9ec563ce2 Mon Sep 17 00:00:00 2001
From: Andrew Wesie <awesie@gmail.com>
Date: Fri, 24 Apr 2020 14:55:14 -0500
Subject: [PATCH] ntdll: Track if a WRITECOPY page has been modified.

Once a WRITECOPY page is modified, it should be mapped as if it is a normal
read-write page.

Signed-off-by: Andrew Wesie <awesie@gmail.com>
---
 dlls/ntdll/unix/virtual.c | 25 +++++++++++++++++++------
 1 file changed, 19 insertions(+), 6 deletions(-)

diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c
index f0ec65d12704..98c5aad578c6 100644
--- a/dlls/ntdll/unix/virtual.c
+++ b/dlls/ntdll/unix/virtual.c
@@ -97,6 +97,7 @@ struct file_view
 #define VPROT_GUARD      0x10
 #define VPROT_COMMITTED  0x20
 #define VPROT_WRITEWATCH 0x40
+#define VPROT_WRITTEN    0x80
 /* per-mapping protection flags */
 #define VPROT_SYSTEM     0x0200  /* system view (underlying mmap not under our control) */
 
@@ -820,7 +821,7 @@ static int get_unix_prot( BYTE vprot )
 #if defined(__i386__)
         if (vprot & VPROT_WRITECOPY)
         {
-            if (experimental_WRITECOPY())
+            if (experimental_WRITECOPY() && !(vprot & VPROT_WRITTEN))
                 prot = (prot & ~PROT_WRITE) | PROT_READ;
             else
                 prot |= PROT_WRITE | PROT_READ;
@@ -1406,7 +1407,11 @@ static NTSTATUS create_view( struct file_view **view_ret, void *base, size_t siz
  */
 static DWORD get_win32_prot( BYTE vprot, unsigned int map_prot )
 {
-    DWORD ret = VIRTUAL_Win32Flags[vprot & 0x0f];
+    DWORD ret;
+
+    if ((vprot & VPROT_WRITECOPY) && (vprot & VPROT_WRITTEN))
+        vprot = (vprot & ~VPROT_WRITECOPY) | VPROT_WRITE;
+    ret = VIRTUAL_Win32Flags[vprot & 0x0f];
     if (vprot & VPROT_GUARD) ret |= PAGE_GUARD;
     if (map_prot & SEC_NOCACHE) ret |= PAGE_NOCACHE;
     return ret;
@@ -1517,7 +1522,7 @@ static BOOL set_vprot( struct file_view *view, void *base, size_t size, BYTE vpr
     if (view->protect & VPROT_WRITEWATCH)
     {
         /* each page may need different protections depending on write watch flag */
-        set_page_vprot_bits( base, size, vprot & ~VPROT_WRITEWATCH, ~vprot & ~VPROT_WRITEWATCH );
+        set_page_vprot_bits( base, size, vprot & ~VPROT_WRITEWATCH, ~vprot & ~(VPROT_WRITEWATCH|VPROT_WRITTEN) );
         mprotect_range( base, size, 0, 0 );
         return TRUE;
     }
@@ -1533,10 +1538,18 @@ static BOOL set_vprot( struct file_view *view, void *base, size_t size, BYTE vpr
         return TRUE;
     }
 
+    /* check that we can map this memory with PROT_WRITE since we cannot fail later */
+    if (vprot & VPROT_WRITECOPY)
+        unix_prot |= PROT_WRITE;
+
     if (mprotect_exec( base, size, unix_prot )) /* FIXME: last error */
         return FALSE;
 
-    set_page_vprot( base, size, vprot );
+    /* each page may need different protections depending on writecopy */
+    set_page_vprot_bits( base, size, vprot, ~vprot & ~VPROT_WRITTEN );
+    if (vprot & VPROT_WRITECOPY)
+        mprotect_range( base, size, 0, 0 );
+
     return TRUE;
 }
 
@@ -2928,7 +2941,7 @@ NTSTATUS virtual_handle_fault( void *addr, DWORD err, void *stack )
         }
         if (vprot & VPROT_WRITECOPY)
         {
-            set_page_vprot_bits( page, page_size, VPROT_WRITE, VPROT_WRITECOPY );
+            set_page_vprot_bits( page, page_size, VPROT_WRITE | VPROT_WRITTEN, VPROT_WRITECOPY );
             mprotect_range( page, page_size, 0, 0 );
         }
         /* ignore fault if page is writable now */
@@ -3873,7 +3886,7 @@ static NTSTATUS get_basic_memory_info( HANDLE process, LPCVOID addr,
         else if (view->protect & (SEC_FILE | SEC_RESERVE | SEC_COMMIT)) info->Type = MEM_MAPPED;
         else info->Type = MEM_PRIVATE;
         for (ptr = base; ptr < base + range_size; ptr += page_size)
-            if ((get_page_vprot( ptr ) ^ vprot) & ~VPROT_WRITEWATCH) break;
+            if ((get_page_vprot( ptr ) ^ vprot) & ~(VPROT_WRITEWATCH|VPROT_WRITTEN)) break;
         info->RegionSize = ptr - base;
     }
     server_leave_uninterrupted_section( &virtual_mutex, &sigset );
-- 
2.20.1

