]> sourceware.org Git - newlib-cygwin.git/commitdiff
* dcrt0.cc (child_info_fork::alloc_stack): Correctly check if the
authorCorinna Vinschen <corinna@vinschen.de>
Fri, 16 Dec 2011 18:09:43 +0000 (18:09 +0000)
committerCorinna Vinschen <corinna@vinschen.de>
Fri, 16 Dec 2011 18:09:43 +0000 (18:09 +0000)
parent stack fits into the child stack.  Align comment.
* wow64.cc (wow64_eval_expected_main_stack): New function to fetch
expected addresses of main thread stack from PE/COFF image header
values.
(wow64_test_for_64bit_parent): Fix comment.  Check current stack
against real expected main thread stack addresses.
(wow64_revert_to_original_stack): Fix and add comments. Check memory
against real expected main thread stack addresses.  Use orignal stack
if reserved area is >= 256K.

winsup/cygwin/ChangeLog
winsup/cygwin/dcrt0.cc
winsup/cygwin/wow64.cc

index 6635f0d75bded545dbbe42e6ba4b9f8de84410cb..7167510b2170898fd14ec6cf9af2caa1e9613a17 100644 (file)
@@ -1,3 +1,16 @@
+2011-12-16  Corinna Vinschen  <vinschen@redhat.com>
+
+       * dcrt0.cc (child_info_fork::alloc_stack): Correctly check if the
+       parent stack fits into the child stack.  Align comment.
+       * wow64.cc (wow64_eval_expected_main_stack): New function to fetch
+       expected addresses of main thread stack from PE/COFF image header
+       values.
+       (wow64_test_for_64bit_parent): Fix comment.  Check current stack
+       against real expected main thread stack addresses.
+       (wow64_revert_to_original_stack): Fix and add comments. Check memory
+       against real expected main thread stack addresses.  Use orignal stack
+       if reserved area is >= 256K.
+
 2011-12-16  Christopher Faylor  <me.cygwin2011@cgf.cx>
 
        * gendef (sigdelayed): Remember to pop all of the saved registers.
index a3900372d54be2b69e2bc08e7f93dfbef94dd358..fe6d62e577abba7e82efaed269d6a33ab9fc7ea9 100644 (file)
@@ -452,10 +452,12 @@ child_info_fork::alloc_stack ()
      parent.  In that case the _tlsbase of the forked child is not the same
      as the _tlsbase of the parent (== stackbottom), but only because the
      stack of the parent has been slightly rearranged.  See comment in
-     wow64_revert_to_original_stack for details. We just check here if the
-     stack is in the usual range for the main thread stack. */
+     wow64_revert_to_original_stack for details. We check here if the
+     parent stack fits into the child stack. */
   if (_tlsbase != stackbottom
-      && (!wincap.is_wow64 () || stackbottom > (char *) 0x400000))
+      && (!wincap.is_wow64 ()
+         || stacktop < (char *) NtCurrentTeb ()->DeallocationStack
+         || stackbottom > _tlsbase))
     alloc_stack_hard_way (esp);
   else
     {
index 6908cf8a4ab774016962abf00c50722c16c9f7a0..20ea2e4672380cff4921257063b726f238f35dcc 100644 (file)
@@ -11,37 +11,73 @@ details. */
 #include "winsup.h"
 #include "cygtls.h"
 #include "ntdll.h"
+#include <sys/param.h>
 
 #define PTR_ADD(p,o)   ((PVOID)((PBYTE)(p)+(o)))
 
 bool NO_COPY wow64_has_64bit_parent = false;
 
+static void
+wow64_eval_expected_main_stack (PVOID &allocbase, PVOID &stackbase)
+{
+  PIMAGE_DOS_HEADER dosheader;
+  PIMAGE_NT_HEADERS32 ntheader;
+  DWORD size;
+
+  dosheader = (PIMAGE_DOS_HEADER) GetModuleHandle (NULL);
+  ntheader = (PIMAGE_NT_HEADERS32) ((PBYTE) dosheader + dosheader->e_lfanew);
+  /* The main thread stack is expected to be located at 0x30000, which is the
+     case for all observed NT systems to date, unless the stacksize requested
+     by the StackReserve field in the PE/COFF header is so big that the stack
+     doesn't fit in the area between 0x30000 and the start of the image.  In
+     case of a conflict, the OS allocates the stack right after the image. */
+  allocbase = (PVOID) 0x30000;
+  /* Stack size.  The OS always rounds the size up to allocation granularity
+     and it never allocates less than 256K. */
+  size = roundup2 (ntheader->OptionalHeader.SizeOfStackReserve,
+                  wincap.allocation_granularity ());
+  if (size < 256 * 1024)
+    size = 256 * 1024;
+  /* If the stack doesn't fit in the area before the image, it's allocated
+     right after the image, rounded up to allocation granularity boundary. */
+  if (PTR_ADD (allocbase, size) > (PVOID) ntheader->OptionalHeader.ImageBase)
+    allocbase = PTR_ADD (ntheader->OptionalHeader.ImageBase,
+                        ntheader->OptionalHeader.SizeOfImage);
+  allocbase = (PVOID) roundup2 ((uintptr_t) allocbase,
+                               wincap.allocation_granularity ());
+  stackbase = PTR_ADD (allocbase, size);
+  debug_printf ("expected allocbase: %p, stackbase: %p", allocbase, stackbase);
+}
+
 bool
 wow64_test_for_64bit_parent ()
 {
   /* On Windows XP 64 and 2003 64 there's a problem with processes running
-     under WOW64.  The first process started from a 64 bit process has an
-     unusual stack address for the main thread.  That is, an address which
-     is in the usual space occupied by the process image, but below the auto
-     load address of DLLs.  If this process forks, the child has its stack
-     in the usual memory slot again, thus we have to "alloc_stack_hard_way".
-     However, this fails in almost all cases because the stack slot of the
-     parent process is taken by something else in the child process.
-
-     If we encounter this situation, check if we really have been started
-     from a 64 bit process here.  If so, we note this fact in
-     wow64_has_64bit_parent so we can workaround the stack problem in
-     _dll_crt0.  See there for how we go along. */
+     under WOW64.  The first process started from a 64 bit process has its
+     main thread stack not where it should be.  Rather, it uses another
+     stack while the original stack is used for other purposes.
+     The problem is, the child has its stack in the usual spot again, thus
+     we have to "alloc_stack_hard_way".  However, this fails in almost all
+     cases because the stack slot of the parent process is taken by something
+     else in the child process.
+     What we do here is to check if the current stack is the excpected main
+     thread stack and if not, if we really have been started from a 64 bit
+     process here.  If so, we note this fact in wow64_has_64bit_parent so we
+     can workaround the stack problem in _dll_crt0.  See there for how we go
+     along. */
   NTSTATUS ret;
   PROCESS_BASIC_INFORMATION pbi;
   HANDLE parent;
+  PVOID allocbase, stackbase;
 
   ULONG wow64 = TRUE;   /* Opt on the safe side. */
 
-  /* First check if the stack is where it belongs.  If so, we don't have to
-     do anything special.  This is the case on Vista and later. */
-  if (&wow64 < (PULONG) 0x400000)
+  /* First check if the current stack is where it belongs.  If so, we don't
+     have to do anything special.  This is the case on Vista and later. */
+  wow64_eval_expected_main_stack (allocbase, stackbase);
+  if (&wow64 >= (PULONG) allocbase && &wow64 < (PULONG) stackbase)
     return false;
+
   /* Check if the parent is a native 64 bit process.  Unfortunately there's
      no simpler way to retrieve the parent process in NT, as far as I know.
      Hints welcome. */
@@ -67,39 +103,40 @@ wow64_revert_to_original_stack (PVOID &allocationbase)
      though the stack of the WOW64 process is at an unusual address, it appears
      that the "normal" stack has been created as usual.  It's partially in use
      by the 32->64 bit transition layer of the OS to help along the WOW64
-     process, but it's otherwise mostly unused.
-     The original stack is expected to be located at 0x30000, up to 0x230000.
-     The assumption here is that the default main thread stack size is 2 Megs,
-     but we expect lower stacksizes up to 1 Megs.  What we do here is to start
-     about in the middle, but below the 1 Megs stack size.  The stack is
-     allocated in a single call, so the entire stack has the same
-     AllocationBase. */
+     process, but it's otherwise mostly unused. */
   MEMORY_BASIC_INFORMATION mbi;
-  PVOID addr = (PVOID) 0x100000;
+  PVOID stackbase;
 
-  /* First fetch the AllocationBase. */
-  VirtualQuery (addr, &mbi, sizeof mbi);
-  allocationbase = mbi.AllocationBase;
-  /* At the start we expect a reserved region big enough still to host as
-     the main stack. 512K should be ok (knock on wood). */
+  wow64_eval_expected_main_stack (allocationbase, stackbase);
+
+  /* The stack is allocated in a single call, so the entire stack has the
+     same AllocationBase.  At the start we expect a reserved region big
+     enough still to host as the main stack. The OS apparently reserves
+     always at least 256K for the main thread stack.  We err on the side
+     of caution so we test here for a reserved region of at least 256K.
+     That should be enough (knock on wood). */
   VirtualQuery (allocationbase, &mbi, sizeof mbi);
-  if (mbi.State != MEM_RESERVE || mbi.RegionSize < 512 * 1024)
+  if (mbi.State != MEM_RESERVE || mbi.RegionSize < 256 * 1024)
     return NULL;
 
-  addr = PTR_ADD (mbi.BaseAddress, mbi.RegionSize);
-  /* Next we expect a guard page. */
+  /* Next we expect a guard page.  We fetch the size of the guard area since
+     to see how the OS is handling that.  Apparently the guard area on 64 bit
+     systems spans 2 pages. */
+  PVOID addr = PTR_ADD (mbi.BaseAddress, mbi.RegionSize);
   VirtualQuery (addr, &mbi, sizeof mbi);
   if (mbi.AllocationBase != allocationbase
       || mbi.State != MEM_COMMIT
       || !(mbi.Protect & PAGE_GUARD))
     return NULL;
-
   PVOID guardaddr = mbi.BaseAddress;
   SIZE_T guardsize = mbi.RegionSize;
+
+  /* Next we expect a committed R/W region, the in-use area of that stack.
+     This is just a sanity check. */
   addr = PTR_ADD (mbi.BaseAddress, mbi.RegionSize);
-  /* Next we expect a committed R/W region, the in-use area of that stack. */
   VirtualQuery (addr, &mbi, sizeof mbi);
   if (mbi.AllocationBase != allocationbase
+      || PTR_ADD (mbi.BaseAddress, mbi.RegionSize) != stackbase
       || mbi.State != MEM_COMMIT
       || mbi.Protect != PAGE_READWRITE)
     return NULL;
This page took 0.040777 seconds and 5 git commands to generate.