OSDN Git Service

* heap.cc (heap_init): Rewrite initial heap allocation to use addresses
authorcorinna <corinna>
Mon, 16 May 2011 10:27:12 +0000 (10:27 +0000)
committercorinna <corinna>
Mon, 16 May 2011 10:27:12 +0000 (10:27 +0000)
beyond 0x20000000.  Explain why and how.
* shared.cc (shared_info::heap_slop_size): Remove.
* shared_info.h (class shared_info): Remove heap_slop_inited and
heap_slop members.  Remove heap_slop_size declaration.
(CURR_SHARED_MAGIC): Update.
* wincap.cc: Throughout, drop heapslop.
* wincap.h (struct wincaps): Drop heapslop.

winsup/cygwin/ChangeLog
winsup/cygwin/heap.cc
winsup/cygwin/shared.cc
winsup/cygwin/shared_info.h
winsup/cygwin/wincap.cc
winsup/cygwin/wincap.h

index 8551e46..28188c2 100644 (file)
@@ -1,5 +1,16 @@
 2011-05-16  Corinna Vinschen  <corinna@vinschen.de>
 
+       * heap.cc (heap_init): Rewrite initial heap allocation to use addresses
+       beyond 0x20000000.  Explain why and how.
+       * shared.cc (shared_info::heap_slop_size): Remove.
+       * shared_info.h (class shared_info): Remove heap_slop_inited and
+       heap_slop members.  Remove heap_slop_size declaration.
+       (CURR_SHARED_MAGIC): Update.
+       * wincap.cc: Throughout, drop heapslop.
+       * wincap.h (struct wincaps): Drop heapslop.
+
+2011-05-16  Corinna Vinschen  <corinna@vinschen.de>
+
        * dcrt0.cc (child_info_fork::alloc_stack_hard_way): Check if the
        requested stack is application-provided within the user heap or an
        mmapped region.  If so, just use it.  Add comment to explain why.
index 21516d5..0fe170a 100644 (file)
@@ -17,6 +17,7 @@ details. */
 #include "dtable.h"
 #include "cygheap.h"
 #include "child_info.h"
+#include <sys/param.h>
 
 #define assert(x)
 
@@ -30,41 +31,85 @@ heap_init ()
 {
   const DWORD alloctype = MEM_RESERVE;
   /* If we're the forkee, we must allocate the heap at exactly the same place
-     as our parent.  If not, we don't care where it ends up.  */
+     as our parent.  If not, we (almost) don't care where it ends up.  */
 
   page_const = wincap.page_size ();
   if (!cygheap->user_heap.base)
     {
+      /* Starting with Vista, Windows performs heap ASLR.  This spoils
+        the entire region below 0x20000000 for us, because that region
+        is used by Windows to randomize heap and stack addresses.
+        Therefore we put our heap into a safe region starting at 0x20000000.
+        This should work right from the start in 99% of the cases.  But,
+        there's always a but.  Read on... */
+      uintptr_t start_address = 0x20000000L;
+      uintptr_t largest_found = 0;
+      size_t largest_found_size = 0;
+      SIZE_T ret;
+      MEMORY_BASIC_INFORMATION mbi;
+
       cygheap->user_heap.chunk = cygwin_shared->heap_chunk_size ();
-      /* For some obscure reason Vista and 2003 sometimes reserve space after
-        calls to CreateProcess overlapping the spot where the heap has been
-        allocated.  This apparently spoils fork.  The behaviour looks quite
-        arbitrary.  Experiments on Vista show a memory size of 0x37e000 or
-        0x1fd000 overlapping the usual heap by at most 0x1ed000.  So what
-        we do here is to allocate the heap with an extra slop of (by default)
-        0x400000 and set the appropriate pointers to the start of the heap
-        area + slop.  A forking child then creates its heap at the new start
-        address and without the slop factor.  Since this is not entirely
-        foolproof we add a registry setting "heap_slop_in_mb" so the slop
-        factor can be influenced by the user if the need arises. */
-      cygheap->user_heap.slop = cygwin_shared->heap_slop_size ();
-      while (cygheap->user_heap.chunk >= MINHEAP_SIZE)
+      do
        {
-         /* Initialize page mask and default heap size.  Preallocate a heap
-          * to assure contiguous memory.  */
-         cygheap->user_heap.base =
-           VirtualAlloc (NULL, cygheap->user_heap.chunk
-                               + cygheap->user_heap.slop,
-                         alloctype, PAGE_NOACCESS);
+         cygheap->user_heap.base = VirtualAlloc ((LPVOID) start_address,
+                                                 cygheap->user_heap.chunk,
+                                                 alloctype, PAGE_NOACCESS);
          if (cygheap->user_heap.base)
            break;
-         cygheap->user_heap.chunk -= 1 * 1024 * 1024;
+
+         /* Ok, so we are at the 1% which didn't work with 0x20000000 out
+            of the box.  What we do now is to search for the next free
+            region which matches our desired heap size.  While doing that,
+            we keep track of the largest region we found. */
+         start_address += wincap.allocation_granularity ();
+         while ((ret = VirtualQuery ((LPCVOID) start_address, &mbi,
+                                     sizeof mbi)) != 0)
+           {
+             if (mbi.State == MEM_FREE)
+               {
+                 if (mbi.RegionSize >= cygheap->user_heap.chunk)
+                   break;
+                 if (mbi.RegionSize > largest_found_size)
+                   {
+                     largest_found = (uintptr_t) mbi.BaseAddress;
+                     largest_found_size = mbi.RegionSize;
+                   }
+               }
+             /* Since VirtualAlloc only reserves at allocation granularity
+                boundaries, we round up here, too.  Otherwise we might end
+                up at a bogus page-aligned address. */
+             start_address = roundup2 (start_address + mbi.RegionSize,
+                                       wincap.allocation_granularity ());
+           }
+         if (!ret)
+           {
+             /* In theory this should not happen.  But if it happens, we have
+                collected the information about the largest available region
+                in the above loop.  So, next we squeeze the heap into that
+                region, unless it's smaller than the minimum size. */
+             if (largest_found_size >= MINHEAP_SIZE)
+               {
+                 cygheap->user_heap.chunk = largest_found_size;
+                 cygheap->user_heap.base =
+                       VirtualAlloc ((LPVOID) start_address,
+                                     cygheap->user_heap.chunk,
+                                     alloctype, PAGE_NOACCESS);
+               }
+             /* Last resort (but actually we are probably broken anyway):
+                Use the minimal heap size and let the system decide. */
+             if (!cygheap->user_heap.base)
+               {
+                 cygheap->user_heap.chunk = MINHEAP_SIZE;
+                 cygheap->user_heap.base =
+                       VirtualAlloc (NULL, cygheap->user_heap.chunk,
+                                     alloctype, PAGE_NOACCESS);
+               }
+           }
        }
+      while (!cygheap->user_heap.base && ret);
       if (cygheap->user_heap.base == NULL)
-       api_fatal ("unable to allocate heap, heap_chunk_size %p, slop %p, %E",
-                  cygheap->user_heap.chunk, cygheap->user_heap.slop);
-      cygheap->user_heap.base = (void *) ((char *) cygheap->user_heap.base
-                                                  + cygheap->user_heap.slop);
+       api_fatal ("unable to allocate heap, heap_chunk_size %p, %E",
+                  cygheap->user_heap.chunk);
       cygheap->user_heap.ptr = cygheap->user_heap.top = cygheap->user_heap.base;
       cygheap->user_heap.max = (char *) cygheap->user_heap.base
                               + cygheap->user_heap.chunk;
index 60173cd..0bfd4be 100644 (file)
@@ -441,27 +441,6 @@ memory_init (bool init_cygheap)
 }
 
 unsigned
-shared_info::heap_slop_size ()
-{
-  if (!heap_slop_inited)
-    {
-      /* Fetch from registry, first user then local machine.  */
-      for (int i = 0; i < 2; i++)
-       {
-         reg_key reg (i, KEY_READ, NULL);
-
-         if ((heap_slop = reg.get_dword (L"heap_slop_in_mb", 0)))
-           break;
-         heap_slop = wincap.heapslop ();
-       }
-      heap_slop <<= 20;
-      heap_slop_inited = true;
-    }
-
-  return heap_slop;
-}
-
-unsigned
 shared_info::heap_chunk_size ()
 {
   if (!heap_chunk)
index 5a083f8..feb8ff1 100644 (file)
@@ -30,7 +30,7 @@ public:
 /* Data accessible to all tasks */
 
 
-#define CURR_SHARED_MAGIC 0x7f4db5d3U
+#define CURR_SHARED_MAGIC 0xb41ae342U
 
 #define USER_VERSION   1
 #define CURR_USER_MAGIC 0x6112afb3U
@@ -44,8 +44,6 @@ class shared_info
   DWORD cb;
  public:
   DWORD heap_chunk;
-  bool heap_slop_inited;
-  DWORD heap_slop;
   DWORD sys_mount_table_counter;
   tty_list tty;
   LONG last_used_bindresvport;
@@ -55,7 +53,6 @@ class shared_info
   void initialize ();
   void init_obcaseinsensitive ();
   unsigned heap_chunk_size ();
-  unsigned heap_slop_size ();
   static void create ();
 };
 
index aac916b..b959d1e 100644 (file)
@@ -24,7 +24,6 @@ details. */
 #define wincap_minimal wincap_2000
 
 wincaps wincap_2000 __attribute__((section (".cygwin_dll_common"), shared)) = {
-  heapslop:0x0,
   max_sys_priv:SE_MANAGE_VOLUME_PRIVILEGE,
   is_server:false,
   has_physical_mem_access:true,
@@ -55,7 +54,6 @@ wincaps wincap_2000 __attribute__((section (".cygwin_dll_common"), shared)) = {
 };
 
 wincaps wincap_2000sp4 __attribute__((section (".cygwin_dll_common"), shared)) = {
-  heapslop:0x0,
   max_sys_priv:SE_CREATE_GLOBAL_PRIVILEGE,
   is_server:false,
   has_physical_mem_access:true,
@@ -86,7 +84,6 @@ wincaps wincap_2000sp4 __attribute__((section (".cygwin_dll_common"), shared)) =
 };
 
 wincaps wincap_xp __attribute__((section (".cygwin_dll_common"), shared)) = {
-  heapslop:0x0,
   max_sys_priv:SE_MANAGE_VOLUME_PRIVILEGE,
   is_server:false,
   has_physical_mem_access:true,
@@ -117,7 +114,6 @@ wincaps wincap_xp __attribute__((section (".cygwin_dll_common"), shared)) = {
 };
 
 wincaps wincap_xpsp1 __attribute__((section (".cygwin_dll_common"), shared)) = {
-  heapslop:0x0,
   max_sys_priv:SE_MANAGE_VOLUME_PRIVILEGE,
   is_server:false,
   has_physical_mem_access:true,
@@ -148,7 +144,6 @@ wincaps wincap_xpsp1 __attribute__((section (".cygwin_dll_common"), shared)) = {
 };
 
 wincaps wincap_xpsp2 __attribute__((section (".cygwin_dll_common"), shared)) = {
-  heapslop:0x0,
   max_sys_priv:SE_CREATE_GLOBAL_PRIVILEGE,
   is_server:false,
   has_physical_mem_access:true,
@@ -179,7 +174,6 @@ wincaps wincap_xpsp2 __attribute__((section (".cygwin_dll_common"), shared)) = {
 };
 
 wincaps wincap_2003 __attribute__((section (".cygwin_dll_common"), shared)) = {
-  heapslop:0x4,
   max_sys_priv:SE_CREATE_GLOBAL_PRIVILEGE,
   is_server:false,
   has_physical_mem_access:false,
@@ -210,7 +204,6 @@ wincaps wincap_2003 __attribute__((section (".cygwin_dll_common"), shared)) = {
 };
 
 wincaps wincap_vista __attribute__((section (".cygwin_dll_common"), shared)) = {
-  heapslop:0x4,
   max_sys_priv:SE_CREATE_SYMBOLIC_LINK_PRIVILEGE,
   is_server:false,
   has_physical_mem_access:false,
@@ -241,7 +234,6 @@ wincaps wincap_vista __attribute__((section (".cygwin_dll_common"), shared)) = {
 };
 
 wincaps wincap_7 __attribute__((section (".cygwin_dll_common"), shared)) = {
-  heapslop:0x4,
   max_sys_priv:SE_CREATE_SYMBOLIC_LINK_PRIVILEGE,
   is_server:false,
   has_physical_mem_access:false,
index 52af5b9..629a062 100644 (file)
@@ -14,7 +14,6 @@ details. */
 
 struct wincaps
 {
-  DWORD    heapslop;
   DWORD    max_sys_priv;
   unsigned is_server                                    : 1;
   unsigned has_physical_mem_access                      : 1;
@@ -64,7 +63,6 @@ public:
 
 #define IMPLEMENT(cap) cap() const { return ((wincaps *) this->caps)->cap; }
 
-  DWORD IMPLEMENT (heapslop)
   DWORD IMPLEMENT (max_sys_priv)
   bool  IMPLEMENT (is_server)
   bool  IMPLEMENT (has_physical_mem_access)