* 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.
This commit is contained in:
Corinna Vinschen 2011-05-16 10:27:14 +00:00
parent 6d6cfa4840
commit 883ea27df0
6 changed files with 82 additions and 60 deletions

View File

@ -1,3 +1,14 @@
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

View 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;

View File

@ -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 ()
{

View 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 ();
};

View 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,

View 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)