From 8a14e51901a40c5bfaf09915f88249173d9b2b05 Mon Sep 17 00:00:00 2001 From: Corinna Vinschen Date: Thu, 3 Dec 2015 13:02:55 +0100 Subject: [PATCH] Always move 64 bit main thread stack to defined pthread stack area x86_64 only: * dcrt0.cc (_dll_crt0): Always move stack to pthread stack area. Explain why. * miscfuncs.cc (create_new_main_thread_stack): New function to create OS-like stack for main thread in pthread stack area. * miscfuncs.cc (create_new_main_thread_stack): Declare. Signed-off-by: Corinna Vinschen --- winsup/cygwin/ChangeLog | 9 +++++++++ winsup/cygwin/dcrt0.cc | 29 +++++++++++++++++++++++++++ winsup/cygwin/miscfuncs.cc | 41 ++++++++++++++++++++++++++++++++++++++ winsup/cygwin/miscfuncs.h | 4 ++++ 4 files changed, 83 insertions(+) diff --git a/winsup/cygwin/ChangeLog b/winsup/cygwin/ChangeLog index 149270a9c..803cb4f5c 100644 --- a/winsup/cygwin/ChangeLog +++ b/winsup/cygwin/ChangeLog @@ -1,3 +1,12 @@ +2015-12-03 Corinna Vinschen + + x86_64 only: + * dcrt0.cc (_dll_crt0): Always move stack to pthread stack area. + Explain why. + * miscfuncs.cc (create_new_main_thread_stack): New function to create + OS-like stack for main thread in pthread stack area. + * miscfuncs.cc (create_new_main_thread_stack): Declare. + 2015-12-03 Corinna Vinschen * dcrt0.cc (child_info_fork::alloc_stack): Fix formatting. diff --git a/winsup/cygwin/dcrt0.cc b/winsup/cygwin/dcrt0.cc index a34f64365..5865426e1 100644 --- a/winsup/cygwin/dcrt0.cc +++ b/winsup/cygwin/dcrt0.cc @@ -1093,6 +1093,35 @@ _dll_crt0 () /* Fall back to respawn if wow64_revert_to_original_stack fails. */ wow64_respawn_process (); } +#else + /* Starting with Windows 10 rel 1511, the main stack of an application is + not reproducible if a 64 bit process has been started from a 32 bit + process. Given that we have enough virtual address space on 64 bit + anyway, we now move the main thread stack to the stack area reserved for + pthread stacks. This allows a reproducible stack space under our own + control and avoids collision with the OS. */ + if (!in_forkee && !dynamically_loaded) + { + /* Must be static since it's referenced after the stack and frame + pointer registers have been changed. */ + static PVOID allocationbase; + + PVOID stackaddr = create_new_main_thread_stack (allocationbase); + if (stackaddr) + { + /* 2nd half of the stack move. Set stack pointer to new address. + Don't set frame pointer to 0 since x86_64 uses the stack while + evaluating NtCurrentTeb (). */ + __asm__ ("\n\ + movq %[ADDR], %%rsp \n\ + movq %%rsp, %%rbp \n" + : : [ADDR] "r" (stackaddr)); + /* Now we're back on the new stack. Free up space taken by the + former main thread stack and set DeallocationStack correctly. */ + VirtualFree (NtCurrentTeb ()->DeallocationStack, 0, MEM_RELEASE); + NtCurrentTeb ()->DeallocationStack = allocationbase; + } + } #endif /* !__x86_64__ */ _feinitialise (); #ifndef __x86_64__ diff --git a/winsup/cygwin/miscfuncs.cc b/winsup/cygwin/miscfuncs.cc index cb3459630..320a3c2f3 100644 --- a/winsup/cygwin/miscfuncs.cc +++ b/winsup/cygwin/miscfuncs.cc @@ -760,6 +760,47 @@ public: }; thread_allocator thr_alloc NO_COPY; + +/* Just set up a system-like main thread stack from the pthread stack area + maintained by the thr_alloc class. See the description in the x86_64-only + code in _dll_crt0 to understand why we have to do this. */ +PVOID +create_new_main_thread_stack (PVOID &allocationbase) +{ + PIMAGE_DOS_HEADER dosheader; + PIMAGE_NT_HEADERS ntheader; + SIZE_T stacksize; + ULONG guardsize; + ULONG commitsize; + PBYTE stacklimit; + + dosheader = (PIMAGE_DOS_HEADER) GetModuleHandle (NULL); + ntheader = (PIMAGE_NT_HEADERS) + ((PBYTE) dosheader + dosheader->e_lfanew); + stacksize = ntheader->OptionalHeader.SizeOfStackReserve; + stacksize = roundup2 (stacksize, wincap.allocation_granularity ()); + + allocationbase + = thr_alloc.alloc (ntheader->OptionalHeader.SizeOfStackReserve); + guardsize = wincap.def_guard_page_size (); + commitsize = ntheader->OptionalHeader.SizeOfStackCommit; + commitsize = roundup2 (commitsize, wincap.page_size ()); + if (commitsize > stacksize - guardsize - wincap.page_size ()) + commitsize = stacksize - guardsize - wincap.page_size (); + stacklimit = (PBYTE) allocationbase + stacksize - commitsize - guardsize; + /* Setup guardpage. */ + if (!VirtualAlloc (stacklimit, guardsize, + MEM_COMMIT, PAGE_READWRITE | PAGE_GUARD)) + return NULL; + /* Setup committed region. */ + stacklimit += guardsize; + if (!VirtualAlloc (stacklimit, commitsize, MEM_COMMIT, PAGE_READWRITE)) + return NULL; + NtCurrentTeb()->Tib.StackBase = ((PBYTE) allocationbase + stacksize); + NtCurrentTeb()->Tib.StackLimit = stacklimit; + _main_tls = &_my_tls; + return stacklimit - 64; +} #endif HANDLE WINAPI diff --git a/winsup/cygwin/miscfuncs.h b/winsup/cygwin/miscfuncs.h index c9248f6f7..8ff85d970 100644 --- a/winsup/cygwin/miscfuncs.h +++ b/winsup/cygwin/miscfuncs.h @@ -70,6 +70,10 @@ ssize_t __reg3 check_iovec (const struct iovec *, int, bool); #define check_iovec_for_read(a, b) check_iovec ((a), (b), false) #define check_iovec_for_write(a, b) check_iovec ((a), (b), true) +#ifdef __x86_64__ +extern PVOID create_new_main_thread_stack (PVOID &allocationbase); +#endif + extern "C" DWORD WINAPI pthread_wrapper (PVOID arg); extern "C" HANDLE WINAPI CygwinCreateThread (LPTHREAD_START_ROUTINE thread_func, PVOID thread_arg, PVOID stackaddr,