[PATCH v5 6/6] ntdll: Workaround NtAllocateVirtualMemory with zero_bits set to 1

Rémi Bernon rbernon at codeweavers.com
Wed May 29 07:04:44 CDT 2019


Implement the correct zero_bits behavior for this single case:
* Limit the search in reserved areas to the lower 2G range,
* Pass the MAP_32BIT flag to mmap as a fallback.

LuaJIT <= v2.0.5 for example, when running in 64bit, allocates its
memory in the lower 2GB memory region by using the zero_bits parameter.

This will fix this particular scenario, while trying to minimize the
changes on all the other cases.

Signed-off-by: Rémi Bernon <rbernon at codeweavers.com>
---
 dlls/ntdll/tests/virtual.c |  1 -
 dlls/ntdll/virtual.c       | 35 +++++++++++++++++++++++++++--------
 2 files changed, 27 insertions(+), 9 deletions(-)

diff --git a/dlls/ntdll/tests/virtual.c b/dlls/ntdll/tests/virtual.c
index 59fa77b30e6..c0d280ce402 100644
--- a/dlls/ntdll/tests/virtual.c
+++ b/dlls/ntdll/tests/virtual.c
@@ -57,7 +57,6 @@ static void test_AllocateVirtualMemory(void)
     zero_bits = 1;
     status = pNtAllocateVirtualMemory(GetCurrentProcess(), &addr2, 1, &size,
                                       MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
-    todo_wine
     ok((status == STATUS_SUCCESS || broken(status == STATUS_INVALID_PARAMETER_3) /* winxp */) &&
        ((UINT_PTR)addr2 >> (32 - zero_bits)) == 0,
        "NtAllocateVirtualMemory returned %08x, addr2: %p\n", status, addr2);
diff --git a/dlls/ntdll/virtual.c b/dlls/ntdll/virtual.c
index a3ed96ff77f..84c9ec97ed3 100644
--- a/dlls/ntdll/virtual.c
+++ b/dlls/ntdll/virtual.c
@@ -1083,7 +1083,7 @@ static NTSTATUS map_fixed_area( void *base, size_t size, unsigned int vprot )
  * The csVirtual section must be held by caller.
  */
 static NTSTATUS map_view( struct file_view **view_ret, void *base, size_t size, size_t mask,
-                          int top_down, unsigned int vprot )
+                          int top_down, unsigned int vprot, size_t zero_bits )
 {
     void *ptr;
     NTSTATUS status;
@@ -1100,11 +1100,26 @@ static NTSTATUS map_view( struct file_view **view_ret, void *base, size_t size,
     {
         size_t view_size = size + mask + 1;
         struct alloc_area alloc;
+        int flags = 0;
 
         alloc.size = size;
         alloc.mask = mask;
         alloc.top_down = top_down;
         alloc.limit = user_space_limit;
+
+#if defined(__x86_64__) && defined(MAP_32BIT)
+        /* HACK: only works for zero_bits == 1, this is a simple workaround
+         * for some 64bit code that tries to allocate memory in the lower
+         * 2GB segment using zero_bits parameter.
+         */
+        assert(zero_bits <= 1);
+        if (zero_bits == 1)
+        {
+            alloc.limit = (void*)(((~(UINT_PTR)0) >> (32 + zero_bits)) & ~0xffff);
+            flags = MAP_32BIT;
+        }
+#endif
+
         if (wine_mmap_enum_reserved_areas( alloc_reserved_area_callback, &alloc, top_down ))
         {
             ptr = alloc.result;
@@ -1116,7 +1131,7 @@ static NTSTATUS map_view( struct file_view **view_ret, void *base, size_t size,
 
         for (;;)
         {
-            if ((ptr = wine_anon_mmap( NULL, view_size, VIRTUAL_GetUnixProt(vprot), 0 )) == (void *)-1)
+            if ((ptr = wine_anon_mmap( NULL, view_size, VIRTUAL_GetUnixProt(vprot), flags )) == (void *)-1)
             {
                 if (errno == ENOMEM) return STATUS_NO_MEMORY;
                 return STATUS_INVALID_PARAMETER;
@@ -1284,7 +1299,7 @@ static NTSTATUS allocate_dos_memory( struct file_view **view, unsigned int vprot
         if (addr != low_64k)
         {
             if (addr != (void *)-1) munmap( addr, dosmem_size - 0x10000 );
-            return map_view( view, NULL, dosmem_size, 0xffff, 0, vprot );
+            return map_view( view, NULL, dosmem_size, 0xffff, 0, vprot, 0 );
         }
     }
 
@@ -1388,11 +1403,11 @@ static NTSTATUS map_image( HANDLE hmapping, ACCESS_MASK access, int fd, SIZE_T m
 
     if (base >= (char *)address_space_start)  /* make sure the DOS area remains free */
         status = map_view( &view, base, total_size, mask, FALSE, SEC_IMAGE | SEC_FILE |
-                           VPROT_COMMITTED | VPROT_READ | VPROT_EXEC | VPROT_WRITECOPY );
+                           VPROT_COMMITTED | VPROT_READ | VPROT_EXEC | VPROT_WRITECOPY, 0 );
 
     if (status != STATUS_SUCCESS)
         status = map_view( &view, NULL, total_size, mask, FALSE, SEC_IMAGE | SEC_FILE |
-                           VPROT_COMMITTED | VPROT_READ | VPROT_EXEC | VPROT_WRITECOPY );
+                           VPROT_COMMITTED | VPROT_READ | VPROT_EXEC | VPROT_WRITECOPY, 0 );
 
     if (status != STATUS_SUCCESS) goto error;
 
@@ -1713,7 +1728,7 @@ NTSTATUS virtual_map_section( HANDLE handle, PVOID *addr_ptr, ULONG zero_bits, S
     get_vprot_flags( protect, &vprot, sec_flags & SEC_IMAGE );
     vprot |= sec_flags;
     if (!(sec_flags & SEC_RESERVE)) vprot |= VPROT_COMMITTED;
-    res = map_view( &view, *addr_ptr, size, mask, FALSE, vprot );
+    res = map_view( &view, *addr_ptr, size, mask, FALSE, vprot, 0 );
     if (res)
     {
         server_leave_uninterrupted_section( &csVirtual, &sigset );
@@ -1946,7 +1961,7 @@ NTSTATUS virtual_alloc_thread_stack( TEB *teb, SIZE_T reserve_size, SIZE_T commi
     server_enter_uninterrupted_section( &csVirtual, &sigset );
 
     if ((status = map_view( &view, NULL, size + extra_size, 0xffff, 0,
-                            VPROT_READ | VPROT_WRITE | VPROT_COMMITTED )) != STATUS_SUCCESS)
+                            VPROT_READ | VPROT_WRITE | VPROT_COMMITTED, 0 )) != STATUS_SUCCESS)
         goto done;
 
 #ifdef VALGRIND_STACK_REGISTER
@@ -2467,7 +2482,11 @@ NTSTATUS WINAPI NtAllocateVirtualMemory( HANDLE process, PVOID *ret, ULONG zero_
     TRACE("%p %p %08lx %x %08x\n", process, *ret, size, type, protect );
 
     if (!size) return STATUS_INVALID_PARAMETER;
+#if defined(__x86_64__) && !defined(MAP_32BIT)
     if (zero_bits)
+#else
+    if (zero_bits > 1)
+#endif
     {
         FIXME("Unimplemented zero_bits handling\n");
         return STATUS_INVALID_PARAMETER_3;
@@ -2569,7 +2588,7 @@ NTSTATUS virtual_alloc_aligned( PVOID *ret, ULONG zero_bits, SIZE_T *size_ptr,
 
             if (vprot & VPROT_WRITECOPY) status = STATUS_INVALID_PAGE_PROTECTION;
             else if (is_dos_memory) status = allocate_dos_memory( &view, vprot );
-            else status = map_view( &view, base, size, mask, type & MEM_TOP_DOWN, vprot );
+            else status = map_view( &view, base, size, mask, type & MEM_TOP_DOWN, vprot, zero_bits );
 
             if (status == STATUS_SUCCESS) base = view->base;
         }
-- 
2.20.1




More information about the wine-devel mailing list