* 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:
parent
56d06b9af3
commit
6eee66418c
|
@ -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.
|
||||||
|
|
|
@ -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
|
||||||
{
|
{
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in New Issue