* 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.
This commit is contained in:
Corinna Vinschen 2011-12-16 18:09:43 +00:00
parent 56d06b9af3
commit 6eee66418c
3 changed files with 88 additions and 36 deletions

View 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> 2011-12-16 Christopher Faylor <me.cygwin2011@cgf.cx>
* gendef (sigdelayed): Remember to pop all of the saved registers. * gendef (sigdelayed): Remember to pop all of the saved registers.

View File

@ -452,10 +452,12 @@ child_info_fork::alloc_stack ()
parent. In that case the _tlsbase of the forked child is not the same 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 as the _tlsbase of the parent (== stackbottom), but only because the
stack of the parent has been slightly rearranged. See comment in stack of the parent has been slightly rearranged. See comment in
wow64_revert_to_original_stack for details. We just check here if the wow64_revert_to_original_stack for details. We check here if the
stack is in the usual range for the main thread stack. */ parent stack fits into the child stack. */
if (_tlsbase != stackbottom if (_tlsbase != stackbottom
&& (!wincap.is_wow64 () || stackbottom > (char *) 0x400000)) && (!wincap.is_wow64 ()
|| stacktop < (char *) NtCurrentTeb ()->DeallocationStack
|| stackbottom > _tlsbase))
alloc_stack_hard_way (esp); alloc_stack_hard_way (esp);
else else
{ {

View File

@ -11,37 +11,73 @@ details. */
#include "winsup.h" #include "winsup.h"
#include "cygtls.h" #include "cygtls.h"
#include "ntdll.h" #include "ntdll.h"
#include <sys/param.h>
#define PTR_ADD(p,o) ((PVOID)((PBYTE)(p)+(o))) #define PTR_ADD(p,o) ((PVOID)((PBYTE)(p)+(o)))
bool NO_COPY wow64_has_64bit_parent = false; 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 bool
wow64_test_for_64bit_parent () wow64_test_for_64bit_parent ()
{ {
/* On Windows XP 64 and 2003 64 there's a problem with processes running /* 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 under WOW64. The first process started from a 64 bit process has its
unusual stack address for the main thread. That is, an address which main thread stack not where it should be. Rather, it uses another
is in the usual space occupied by the process image, but below the auto stack while the original stack is used for other purposes.
load address of DLLs. If this process forks, the child has its stack The problem is, the child has its stack in the usual spot again, thus
in the usual memory slot again, thus we have to "alloc_stack_hard_way". we have to "alloc_stack_hard_way". However, this fails in almost all
However, this fails in almost all cases because the stack slot of the cases because the stack slot of the parent process is taken by something
parent process is taken by something else in the child process. else in the child process.
What we do here is to check if the current stack is the excpected main
If we encounter this situation, check if we really have been started thread stack and if not, if we really have been started from a 64 bit
from a 64 bit process here. If so, we note this fact in process here. If so, we note this fact in wow64_has_64bit_parent so we
wow64_has_64bit_parent so we can workaround the stack problem in can workaround the stack problem in _dll_crt0. See there for how we go
_dll_crt0. See there for how we go along. */ along. */
NTSTATUS ret; NTSTATUS ret;
PROCESS_BASIC_INFORMATION pbi; PROCESS_BASIC_INFORMATION pbi;
HANDLE parent; HANDLE parent;
PVOID allocbase, stackbase;
ULONG wow64 = TRUE; /* Opt on the safe side. */ ULONG wow64 = TRUE; /* Opt on the safe side. */
/* First check if the stack is where it belongs. If so, we don't have to /* First check if the current stack is where it belongs. If so, we don't
do anything special. This is the case on Vista and later. */ have to do anything special. This is the case on Vista and later. */
if (&wow64 < (PULONG) 0x400000) wow64_eval_expected_main_stack (allocbase, stackbase);
if (&wow64 >= (PULONG) allocbase && &wow64 < (PULONG) stackbase)
return false; return false;
/* Check if the parent is a native 64 bit process. Unfortunately there's /* 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. no simpler way to retrieve the parent process in NT, as far as I know.
Hints welcome. */ 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 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 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 by the 32->64 bit transition layer of the OS to help along the WOW64
process, but it's otherwise mostly unused. 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. */
MEMORY_BASIC_INFORMATION mbi; MEMORY_BASIC_INFORMATION mbi;
PVOID addr = (PVOID) 0x100000; PVOID stackbase;
/* First fetch the AllocationBase. */ wow64_eval_expected_main_stack (allocationbase, stackbase);
VirtualQuery (addr, &mbi, sizeof mbi);
allocationbase = mbi.AllocationBase; /* The stack is allocated in a single call, so the entire stack has the
/* At the start we expect a reserved region big enough still to host as same AllocationBase. At the start we expect a reserved region big
the main stack. 512K should be ok (knock on wood). */ 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); 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; return NULL;
addr = PTR_ADD (mbi.BaseAddress, mbi.RegionSize); /* Next we expect a guard page. We fetch the size of the guard area since
/* Next we expect a guard page. */ 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); VirtualQuery (addr, &mbi, sizeof mbi);
if (mbi.AllocationBase != allocationbase if (mbi.AllocationBase != allocationbase
|| mbi.State != MEM_COMMIT || mbi.State != MEM_COMMIT
|| !(mbi.Protect & PAGE_GUARD)) || !(mbi.Protect & PAGE_GUARD))
return NULL; return NULL;
PVOID guardaddr = mbi.BaseAddress; PVOID guardaddr = mbi.BaseAddress;
SIZE_T guardsize = mbi.RegionSize; 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); 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); VirtualQuery (addr, &mbi, sizeof mbi);
if (mbi.AllocationBase != allocationbase if (mbi.AllocationBase != allocationbase
|| PTR_ADD (mbi.BaseAddress, mbi.RegionSize) != stackbase
|| mbi.State != MEM_COMMIT || mbi.State != MEM_COMMIT
|| mbi.Protect != PAGE_READWRITE) || mbi.Protect != PAGE_READWRITE)
return NULL; return NULL;