From 883ea27df0f7d9a5836feb493c35026381c55b2b Mon Sep 17 00:00:00 2001 From: Corinna Vinschen Date: Mon, 16 May 2011 10:27:14 +0000 Subject: [PATCH] * 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. --- winsup/cygwin/ChangeLog | 11 +++++ winsup/cygwin/heap.cc | 95 +++++++++++++++++++++++++++---------- winsup/cygwin/shared.cc | 21 -------- winsup/cygwin/shared_info.h | 5 +- winsup/cygwin/wincap.cc | 8 ---- winsup/cygwin/wincap.h | 2 - 6 files changed, 82 insertions(+), 60 deletions(-) diff --git a/winsup/cygwin/ChangeLog b/winsup/cygwin/ChangeLog index 8551e462c..28188c204 100644 --- a/winsup/cygwin/ChangeLog +++ b/winsup/cygwin/ChangeLog @@ -1,3 +1,14 @@ +2011-05-16 Corinna Vinschen + + * 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 * dcrt0.cc (child_info_fork::alloc_stack_hard_way): Check if the diff --git a/winsup/cygwin/heap.cc b/winsup/cygwin/heap.cc index 21516d5c9..0fe170a89 100644 --- a/winsup/cygwin/heap.cc +++ b/winsup/cygwin/heap.cc @@ -17,6 +17,7 @@ details. */ #include "dtable.h" #include "cygheap.h" #include "child_info.h" +#include #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; diff --git a/winsup/cygwin/shared.cc b/winsup/cygwin/shared.cc index 60173cda6..0bfd4be8d 100644 --- a/winsup/cygwin/shared.cc +++ b/winsup/cygwin/shared.cc @@ -440,27 +440,6 @@ memory_init (bool init_cygheap) user_info::create (false); /* Initialize per-user shared memory */ } -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 () { diff --git a/winsup/cygwin/shared_info.h b/winsup/cygwin/shared_info.h index 5a083f8fa..feb8ff1da 100644 --- a/winsup/cygwin/shared_info.h +++ b/winsup/cygwin/shared_info.h @@ -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 (); }; diff --git a/winsup/cygwin/wincap.cc b/winsup/cygwin/wincap.cc index aac916ba4..b959d1e57 100644 --- a/winsup/cygwin/wincap.cc +++ b/winsup/cygwin/wincap.cc @@ -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, diff --git a/winsup/cygwin/wincap.h b/winsup/cygwin/wincap.h index 52af5b96b..629a062cd 100644 --- a/winsup/cygwin/wincap.h +++ b/winsup/cygwin/wincap.h @@ -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)