kernel: use VirtualAlloc in GlobalAlloc16 to make such memory executable

Mikołaj Zalewski mikolaj at zalewski.pl
Tue Jul 10 09:10:19 CDT 2007


  We currently allocate memory for GlobalAlloc16 with HeapAlloc. That 
makes the memory non-executable what's a problem as memory for NE code 
segments is allocated with it. With this patch we allocate the memory 
with VirtualAlloc with the full permissions (for 16-bit code the access 
may be limited by the LDT entries). The memory is reserved in multiples 
for 64kB so we can realloc in place blocks of memory up to 64kB. AFAIK 
there can be up to 8192 segments so we shouldn't have a problem with 
running out of address space.
  This patch is not enough to run Win16 programs on NX protected systems 
as there is also some code placed in the UMB by winedos/himem.c. I have 
a patch that makes the DOS area executable (see bug #8744) but I will 
have to look if that's what Windows does. In the mean time I'd like to 
know if there are some comments about this patch.
-------------- next part --------------
From c2fff2dc18ca77d867d73b3ae9d008cb81217177 Mon Sep 17 00:00:00 2001
From: =?utf-8?q?Miko=C5=82aj_Zalewski?= <mikolaj at zalewski.pl>
Date: Mon, 9 Jul 2007 19:04:18 +0200
Subject: [PATCH] kernel: use VirtualAlloc in GlobalAlloc16 to make such memory executable

---
 dlls/kernel32/global16.c |  126 ++++++++++++++++++++++++++++++++++++++++-----
 dlls/kernel32/local16.c  |   13 +----
 dlls/kernel32/toolhelp.h |    2 +
 3 files changed, 117 insertions(+), 24 deletions(-)

diff --git a/dlls/kernel32/global16.c b/dlls/kernel32/global16.c
index b17c610..748985c 100644
--- a/dlls/kernel32/global16.c
+++ b/dlls/kernel32/global16.c
@@ -70,7 +70,8 @@ typedef struct
 static GLOBALARENA *pGlobalArena;
 static int globalArenaSize;
 
-#define GLOBAL_MAX_ALLOC_SIZE 0x00ff0000  /* Largest allocation is 16M - 64K */
+#define GLOBAL_MAX_ALLOC_SIZE     0x00ff0000  /* Largest allocation is 16M - 64K */
+#define GLOBAL_RESERVE_GRANUALITY 0x0000ffff  /* reserved space will be rounded up 64K */
 
 #define VALID_HANDLE(handle) (((handle)>>__AHSHIFT)<globalArenaSize)
 #define GET_ARENA_PTR(handle)  (pGlobalArena + ((handle) >> __AHSHIFT))
@@ -93,6 +94,105 @@ static inline UINT      DOSMEM_ResizeBlock(void *ptr, UINT size, BOOL exact)
     return winedos.ResizeDosBlock ? winedos.ResizeDosBlock(ptr, size, TRUE) : 0;
 }
 
+static inline DWORD get_alloc_size(DWORD size)
+{
+    DWORD page_mask = getpagesize() - 1;
+    return (size + page_mask) & (~page_mask);
+}
+
+static inline DWORD get_reserve_size(DWORD size)
+{
+    return (size + GLOBAL_RESERVE_GRANUALITY) & (~GLOBAL_RESERVE_GRANUALITY);
+}
+
+/* Allocate a block of global memory. We allocate the block with full read/write/execute
+ * access - the access will be restricted on selectors level. The need for the execute
+ * permission is why we can't use HeapAlloc
+ */
+static inline void *GLOBAL_VirtualAlloc(DWORD size)
+{
+    return GLOBAL_VirtualAllocEx(size, 0);
+}
+
+/* Allocate a block of global memory the 'size' bytes allocated followed by at least
+ * 'reserved' bytes of memory that is only reserved. Functionality needed by Local32Init16
+ * in local16.c. You should not call GLOBAL_VirtualRealloc for blocks with reserved > 0
+ */
+void *GLOBAL_VirtualAllocEx(DWORD size, DWORD reserved)
+{
+    int reserve = get_reserve_size(size + reserved);
+    int alloc = get_alloc_size(size);
+    void *base;
+    
+    base = VirtualAlloc(NULL, reserve, MEM_RESERVE, PAGE_EXECUTE_READWRITE);
+    if (base == NULL)
+        return NULL;
+
+    if (VirtualAlloc(base, alloc, MEM_COMMIT, PAGE_EXECUTE_READWRITE) == NULL)
+    {
+        VirtualFree(base, 0, MEM_RELEASE);
+        return NULL;
+    }
+
+    return base;
+}
+
+/* Frees the memory allocated with GLOBAL_VirtualAlloc[Ex]. Also used in Local32Init16
+ */
+void GLOBAL_VirtualFree(void *base)
+{
+    VirtualFree(base, 0, MEM_RELEASE);
+}
+
+/* Reallocates a block of memory allocated with GLOBAL_VirtualAlloc. Accepted flags are
+ * HEAP_REALLOC_IN_PLACE_ONLY and HEAP_ZERO_MEMORY.
+ */
+static void *GLOBAL_VirtualReAlloc(void *base, DWORD old_size, DWORD new_size, DWORD flags)
+{
+    int old_reserve = get_reserve_size(old_size);
+    int new_reserve = get_reserve_size(new_size);
+    int old_alloc, new_alloc;
+    
+    if (old_reserve != new_reserve)
+    {
+        void *new_base;
+
+        TRACE("copying block\n");
+        if (flags & HEAP_REALLOC_IN_PLACE_ONLY)
+        {
+            ERR("Can't realloc segment in place and moving disabled\n");
+            return NULL;
+        }
+
+        new_base = GLOBAL_VirtualAlloc(new_size);
+        if (new_base == NULL)
+            return NULL;
+        memcpy(new_base, base, min(old_size, new_size));
+        GLOBAL_VirtualFree(base);
+        return new_base;
+    }
+    
+    old_alloc = get_alloc_size(old_size);
+    new_alloc = get_alloc_size(new_size);
+
+    if (old_alloc <= new_alloc)
+    {
+        TRACE("commit more memory (%d -> %d\n)\n", old_alloc, new_alloc);
+        if (old_alloc < new_alloc)
+            if (VirtualAlloc((char *)base + old_alloc, new_alloc - old_alloc, MEM_COMMIT, PAGE_EXECUTE_READWRITE) == NULL)
+                return NULL;
+
+        if (flags & HEAP_ZERO_MEMORY)
+            memset((char *)base + old_size, old_alloc - old_size, 0);
+    } else
+    {
+        TRACE("decommit memory (%d -> %d\n)\n", old_alloc, new_alloc);
+        VirtualFree((char *)base + new_alloc, old_alloc - new_alloc, MEM_DECOMMIT);
+    }
+
+    return base;
+}
+
 /***********************************************************************
  *           GLOBAL_GetArena
  *
@@ -235,7 +335,7 @@ HGLOBAL16 GLOBAL_Alloc( UINT16 flags, DWORD size, HGLOBAL16 hOwner, unsigned cha
     size = (size + 0x1f) & ~0x1f;
 
     /* Allocate the linear memory */
-    ptr = HeapAlloc( GetProcessHeap(), 0, size );
+    ptr = GLOBAL_VirtualAlloc( size );
       /* FIXME: free discardable blocks and try again? */
     if (!ptr) return 0;
 
@@ -244,7 +344,7 @@ HGLOBAL16 GLOBAL_Alloc( UINT16 flags, DWORD size, HGLOBAL16 hOwner, unsigned cha
     handle = GLOBAL_CreateBlock( flags, ptr, size, hOwner, selflags );
     if (!handle)
     {
-        HeapFree( GetProcessHeap(), 0, ptr );
+        GLOBAL_VirtualFree( ptr );
         return 0;
     }
 
@@ -315,7 +415,7 @@ HGLOBAL16 WINAPI GlobalReAlloc16(
         if (pArena->flags & GA_DOSMEM)
             DOSMEM_FreeBlock( (void *)pArena->base );
         else
-            HeapFree( GetProcessHeap(), 0, (void *)pArena->base );
+            GLOBAL_VirtualFree( (void *)pArena->base );
         pArena->base = 0;
 
         /* Note: we rely on the fact that SELECTOR_ReallocBlock won't
@@ -374,13 +474,11 @@ HGLOBAL16 WINAPI GlobalReAlloc16(
          */
 
 	if (ptr)
-            newptr = HeapReAlloc( GetProcessHeap(),
-		(pArena->pageLockCount > 0) ? HEAP_REALLOC_IN_PLACE_ONLY : 0, 
-                              ptr, size );
+            newptr = GLOBAL_VirtualReAlloc( ptr, oldsize, size, 
+		((pArena->pageLockCount > 0) ? HEAP_REALLOC_IN_PLACE_ONLY : 0) |
+		(flags & GMEM_ZEROINIT ? HEAP_ZERO_MEMORY : 0));
 	else
-            newptr = HeapAlloc( GetProcessHeap(),
-		(pArena->pageLockCount > 0) ? HEAP_REALLOC_IN_PLACE_ONLY : 0, 
-                              size );
+            newptr = GLOBAL_VirtualAlloc( size );
 
     }
 
@@ -392,7 +490,7 @@ HGLOBAL16 WINAPI GlobalReAlloc16(
             if (pArena->flags & GA_DOSMEM)
                 DOSMEM_FreeBlock( (void *)pArena->base );
             else
-                HeapFree( GetProcessHeap(), 0, ptr );
+                GLOBAL_VirtualFree( ptr );
             SELECTOR_FreeBlock( sel );
             memset( pArena, 0, sizeof(GLOBALARENA) );
         }
@@ -408,7 +506,7 @@ HGLOBAL16 WINAPI GlobalReAlloc16(
         if (pArena->flags & GA_DOSMEM)
             DOSMEM_FreeBlock( (void *)pArena->base );
         else
-            HeapFree( GetProcessHeap(), 0, ptr );
+            GLOBAL_VirtualFree( ptr );
         memset( pArena, 0, sizeof(GLOBALARENA) );
         return 0;
     }
@@ -419,7 +517,7 @@ HGLOBAL16 WINAPI GlobalReAlloc16(
         if (pArena->flags & GA_DOSMEM)
             DOSMEM_FreeBlock( (void *)pArena->base );
         else
-            HeapFree( GetProcessHeap(), 0, ptr );
+            GLOBAL_VirtualFree( ptr );
         SELECTOR_FreeBlock( sel );
         return 0;
     }
@@ -463,7 +561,7 @@ HGLOBAL16 WINAPI GlobalFree16(
 
     TRACE("%04x\n", handle );
     if (!GLOBAL_FreeBlock( handle )) return handle;  /* failed */
-    HeapFree( GetProcessHeap(), 0, ptr );
+    GLOBAL_VirtualFree( ptr );
     return 0;
 }
 
diff --git a/dlls/kernel32/local16.c b/dlls/kernel32/local16.c
index 87f11ed..306c169 100644
--- a/dlls/kernel32/local16.c
+++ b/dlls/kernel32/local16.c
@@ -1786,19 +1786,12 @@ HANDLE WINAPI Local32Init16( WORD segment, DWORD tableSize,
 
     /* Allocate memory and initialize heap */
 
-    if ( !(base = VirtualAlloc( NULL, totSize, MEM_RESERVE, PAGE_READWRITE )) )
+    if ( !(base = GLOBAL_VirtualAllocEx( segSize + HTABLE_SIZE, heapSize )) )
         return 0;
 
-    if ( !VirtualAlloc( base, segSize + HTABLE_PAGESIZE,
-                        MEM_COMMIT, PAGE_READWRITE ) )
-    {
-        VirtualFree( base, 0, MEM_RELEASE );
-        return 0;
-    }
-
     if (!(heap = RtlCreateHeap( 0, base + segSize + HTABLE_SIZE, heapSize, 0x10000, NULL, NULL )))
     {
-        VirtualFree( base, 0, MEM_RELEASE );
+        GLOBAL_VirtualFree( base );
         return 0;
     }
 
@@ -1857,7 +1850,7 @@ HANDLE WINAPI Local32Init16( WORD segment, DWORD tableSize,
         LPBYTE oldBase = (LPBYTE)GetSelectorBase( segment );
         memcpy( base, oldBase, segSize );
         GLOBAL_MoveBlock( segment, base, totSize );
-        HeapFree( GetProcessHeap(), 0, oldBase );
+        GLOBAL_VirtualFree( oldBase );
     }
 
     return (HANDLE)header;
diff --git a/dlls/kernel32/toolhelp.h b/dlls/kernel32/toolhelp.h
index 6506aae..55b5fc1 100644
--- a/dlls/kernel32/toolhelp.h
+++ b/dlls/kernel32/toolhelp.h
@@ -103,6 +103,8 @@ BOOL16 WINAPI GlobalEntryModule16( GLOBALENTRY *pGlobal, HMODULE16 hModule,
                                  WORD wSeg );
 
 /* FIXME: Wine internal functions */
+extern void *GLOBAL_VirtualAllocEx( DWORD size, DWORD reserve );
+extern void GLOBAL_VirtualFree( void *base );
 extern HGLOBAL16 GLOBAL_CreateBlock( UINT16 flags, void *ptr, DWORD size,
                                      HGLOBAL16 hOwner, unsigned char selflags );
 extern BOOL16 GLOBAL_FreeBlock( HGLOBAL16 handle );
-- 
1.4.4.2


More information about the wine-patches mailing list