From: Corinna Vinschen Date: Thu, 3 Dec 2015 21:56:44 +0000 (+0100) Subject: On 64 bit, only create new thread stack if started from 32 bit process on affected... X-Git-Tag: newlib-2_3_0~37 X-Git-Url: https://sourceware.org/git/?a=commitdiff_plain;h=12743c2d5d2721f3a80b4d7671a349be03c1f520;p=newlib-cygwin.git On 64 bit, only create new thread stack if started from 32 bit process on affected platforms * dcrt0.cc (dll_crt0_0): On 64 bit, set wow64_needs_stack_adjustment if not started from a 64 bit process. (_dll_crt0): Enable wow64_needs_stack_adjustment branch on 64 bit as well. Remove 64 bit only code. Introduce CREATE_STACK and FIX_STACK macros to conditionalize the code. Rearrange and partially rewrite comments. * wincap.h (wincaps::has_3264_stack_broken): New element. * wincap.cc: Implement above element throughout. (wincapc::init): Set has_3264_stack_broken to false on 32 bit. * wow64.cc: Enable functionality on 64 bit architecture, except for wow64_revert_to_original_stack. Enhance comments to explain. (wow64_eval_expected_main_stack): Make 64 bit clean. (wow64_test_for_64bit_parent): Ditto. * wow64.h: Export wow64_revert_to_original_stack on 32 bit only, everything else on all architectures. Signed-off-by: Corinna Vinschen --- diff --git a/winsup/cygwin/ChangeLog b/winsup/cygwin/ChangeLog index 59e8cce60..747e8765e 100644 --- a/winsup/cygwin/ChangeLog +++ b/winsup/cygwin/ChangeLog @@ -1,3 +1,21 @@ +2015-12-03 Corinna Vinschen + + * dcrt0.cc (dll_crt0_0): On 64 bit, set wow64_needs_stack_adjustment + if not started from a 64 bit process. + (_dll_crt0): Enable wow64_needs_stack_adjustment branch on 64 bit + as well. Remove 64 bit only code. Introduce CREATE_STACK and + FIX_STACK macros to conditionalize the code. Rearrange and + partially rewrite comments. + * wincap.h (wincaps::has_3264_stack_broken): New element. + * wincap.cc: Implement above element throughout. + (wincapc::init): Set has_3264_stack_broken to false on 32 bit. + * wow64.cc: Enable functionality on 64 bit architecture, except for + wow64_revert_to_original_stack. Enhance comments to explain. + (wow64_eval_expected_main_stack): Make 64 bit clean. + (wow64_test_for_64bit_parent): Ditto. + * wow64.h: Export wow64_revert_to_original_stack on 32 bit only, + everything else on all architectures. + 2015-12-03 Corinna Vinschen * fhandler_process.cc (thread_info::thread_info): Accommodate the fact diff --git a/winsup/cygwin/dcrt0.cc b/winsup/cygwin/dcrt0.cc index 5865426e1..26b7ec3b4 100644 --- a/winsup/cygwin/dcrt0.cc +++ b/winsup/cygwin/dcrt0.cc @@ -782,6 +782,11 @@ dll_crt0_0 () description in wow64_test_for_64bit_parent. */ if (wincap.wow64_has_secondary_stack ()) wow64_needs_stack_adjustment = wow64_test_for_64bit_parent (); +#else + /* Windows 10 1511 has a stack move when a 64 bit process is started from + a 32 bit process, just as it was vice versa in XP/2003. */ + if (wincap.has_3264_stack_broken ()) + wow64_needs_stack_adjustment = !wow64_test_for_64bit_parent (); #endif /* !__x86_64__ */ } else @@ -1062,67 +1067,50 @@ __cygwin_exit_return: \n\ extern "C" void __stdcall _dll_crt0 () { -#ifndef __x86_64__ +#ifdef __x86_64__ + /* Handle 64 bit process on Windows 10 rel 1511 which has been started from + 32 bit WOW64 process. See comment in wow64_test_for_64bit_parent for a + problem description. Unfortunately the areas the stacks would have to + be moved to are both taken by "something else"(tm) in both, forker and + forkee, so we can't use the wow64_revert_to_original_stack method as in + the 32 bit case. Rather, we move the main thread stack to the stack area + reserved for pthread stacks for this process. */ +#define CREATE_STACK(a) create_new_main_thread_stack(a) +#define FIX_STACK(s) __asm__ ("\n" \ + "movq %[ADDR], %%rsp \n" \ + "movq %%rsp, %%rbp \n" \ + : : [ADDR] "r" (s)) +#else /* Handle WOW64 process on XP/2K3 which has been started from native 64 bit process. See comment in wow64_test_for_64bit_parent for a full problem description. */ +#define CREATE_STACK(a) wow64_revert_to_original_stack(a) +#define FIX_STACK(s) __asm__ ("\n" \ + "movl %[ADDR], %%esp \n" \ + "xorl %%ebp, %%ebp \n" \ + : : [ADDR] "r" (s)) +#endif if (wow64_needs_stack_adjustment && !dynamically_loaded) { /* Must be static since it's referenced after the stack and frame pointer registers have been changed. */ - static PVOID allocationbase = 0; + static PVOID allocationbase; - /* Check if we just move the stack. If so, wow64_revert_to_original_stack - returns a non-NULL, 16 byte aligned address. See comments in - wow64_revert_to_original_stack for the gory details. */ - PVOID stackaddr = wow64_revert_to_original_stack (allocationbase); + PVOID stackaddr = CREATE_STACK (allocationbase); if (stackaddr) { /* 2nd half of the stack move. Set stack pointer to new address. Set frame pointer to 0. */ - __asm__ ("\n\ - movl %[ADDR], %%esp \n\ - xorl %%ebp, %%ebp \n" - : : [ADDR] "r" (stackaddr)); + FIX_STACK (stackaddr); /* Now we're back on the original stack. Free up space taken by the former main thread stack and set DeallocationStack correctly. */ VirtualFree (NtCurrentTeb ()->DeallocationStack, 0, MEM_RELEASE); NtCurrentTeb ()->DeallocationStack = allocationbase; } else - /* Fall back to respawn if wow64_revert_to_original_stack fails. */ + /* Fall back to respawning if creating a new 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__ main_environ = user_data->envptr; diff --git a/winsup/cygwin/wincap.cc b/winsup/cygwin/wincap.cc index 4bf62d725..6da4b0b5a 100644 --- a/winsup/cygwin/wincap.cc +++ b/winsup/cygwin/wincap.cc @@ -52,6 +52,7 @@ wincaps wincap_xpsp2 __attribute__((section (".cygwin_dll_common"), shared)) = { has_processor_groups:false, has_broken_prefetchvm:false, has_new_pebteb_region:false, + has_3264_stack_broken:false, }; wincaps wincap_2003 __attribute__((section (".cygwin_dll_common"), shared)) = { @@ -86,6 +87,7 @@ wincaps wincap_2003 __attribute__((section (".cygwin_dll_common"), shared)) = { has_processor_groups:false, has_broken_prefetchvm:false, has_new_pebteb_region:false, + has_3264_stack_broken:false, }; wincaps wincap_vista __attribute__((section (".cygwin_dll_common"), shared)) = { @@ -120,6 +122,7 @@ wincaps wincap_vista __attribute__((section (".cygwin_dll_common"), shared)) = { has_processor_groups:false, has_broken_prefetchvm:false, has_new_pebteb_region:false, + has_3264_stack_broken:false, }; wincaps wincap_7 __attribute__((section (".cygwin_dll_common"), shared)) = { @@ -154,6 +157,7 @@ wincaps wincap_7 __attribute__((section (".cygwin_dll_common"), shared)) = { has_processor_groups:true, has_broken_prefetchvm:false, has_new_pebteb_region:false, + has_3264_stack_broken:false, }; wincaps wincap_8 __attribute__((section (".cygwin_dll_common"), shared)) = { @@ -188,6 +192,7 @@ wincaps wincap_8 __attribute__((section (".cygwin_dll_common"), shared)) = { has_processor_groups:true, has_broken_prefetchvm:false, has_new_pebteb_region:false, + has_3264_stack_broken:false, }; wincaps wincap_10 __attribute__((section (".cygwin_dll_common"), shared)) = { @@ -222,6 +227,7 @@ wincaps wincap_10 __attribute__((section (".cygwin_dll_common"), shared)) = { has_processor_groups:true, has_broken_prefetchvm:true, has_new_pebteb_region:false, + has_3264_stack_broken:false, }; wincaps wincap_10_1511 __attribute__((section (".cygwin_dll_common"), shared)) = { @@ -256,6 +262,7 @@ wincaps wincap_10_1511 __attribute__((section (".cygwin_dll_common"), shared)) = has_processor_groups:true, has_broken_prefetchvm:false, has_new_pebteb_region:true, + has_3264_stack_broken:true, }; wincapc wincap __attribute__((section (".cygwin_dll_common"), shared)); @@ -320,6 +327,10 @@ wincapc::init () target process on 64 bit XP/2003 in native 64 bit mode only. Reset the flag here for 32 bit. */ ((wincaps *)caps)->has_broken_rtl_query_process_debug_information = false; + /* Windows 10 1511 has a stack move when a 64 bit process is started from + a 32 bit process, just as it was vice versa in XP/2003. Reset the flag + here for 32 bit. */ + ((wincaps *)caps)->has_3264_stack_broken = false; if (NT_SUCCESS (NtQueryInformationProcess (NtCurrentProcess (), ProcessWow64Information, &wow64, sizeof wow64, NULL)) diff --git a/winsup/cygwin/wincap.h b/winsup/cygwin/wincap.h index 4508974f5..05a73f925 100644 --- a/winsup/cygwin/wincap.h +++ b/winsup/cygwin/wincap.h @@ -45,6 +45,7 @@ struct wincaps unsigned has_processor_groups : 1; unsigned has_broken_prefetchvm : 1; unsigned has_new_pebteb_region : 1; + unsigned has_3264_stack_broken : 1; }; class wincapc @@ -104,6 +105,7 @@ public: bool IMPLEMENT (has_processor_groups) bool IMPLEMENT (has_broken_prefetchvm) bool IMPLEMENT (has_new_pebteb_region) + bool IMPLEMENT (has_3264_stack_broken) #undef IMPLEMENT }; diff --git a/winsup/cygwin/wow64.cc b/winsup/cygwin/wow64.cc index 240baa898..f91a6d855 100644 --- a/winsup/cygwin/wow64.cc +++ b/winsup/cygwin/wow64.cc @@ -8,10 +8,6 @@ This software is a copyrighted work licensed under the terms of the Cygwin license. Please consult the file "CYGWIN_LICENSE" for details. */ -#ifndef __x86_64__ -/* WOW64 only plays a role in the 32 bit version. Don't use any of this - in the 64 bit version. */ - #include "winsup.h" #include "cygtls.h" #include "ntdll.h" @@ -25,11 +21,11 @@ static void wow64_eval_expected_main_stack (PVOID &allocbase, PVOID &stackbase) { PIMAGE_DOS_HEADER dosheader; - PIMAGE_NT_HEADERS32 ntheader; - DWORD size; + PIMAGE_NT_HEADERS ntheader; + SIZE_T size; dosheader = (PIMAGE_DOS_HEADER) GetModuleHandle (NULL); - ntheader = (PIMAGE_NT_HEADERS32) ((PBYTE) dosheader + dosheader->e_lfanew); + ntheader = (PIMAGE_NT_HEADERS) ((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 up to Server 2003 R2, unless the stacksize requested by the StackReserve field in the PE/COFF header is @@ -41,8 +37,15 @@ wow64_eval_expected_main_stack (PVOID &allocbase, PVOID &stackbase) stack address on Vista/2008 64 bit is 0x80000 and on W7/2K8R2 64 bit it is 0x90000. However, this is no problem because the system sticks to that address for all WOW64 processes, not only for the first one - started from a 64 bit parent. */ + started from a 64 bit parent. + + On 64 bit W10 1511 the stack starts at 0x400000 by default. See comment + in wow64_test_for_64bit_parent. */ +#ifdef __x86_64__ + allocbase = (PVOID) 0x400000; +#else allocbase = (PVOID) 0x30000; +#endif /* Stack size. The OS always rounds the size up to allocation granularity and it never allocates less than 256K. */ size = roundup2 (ntheader->OptionalHeader.SizeOfStackReserve, @@ -71,11 +74,19 @@ wow64_test_for_64bit_parent () 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 + What we do here is to check if the current stack is the expected 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_needs_stack_adjustment so we can workaround the stack problem in _dll_crt0. See there for how we go along. */ + + /* Amazing but true: Starting with Windows 10 1511 this problem has been + reintroduced, just in the opposite direction: If a 64 bit process is + created from a 32 bit WOW64 process, the main thread stack in the 64 + bit child gets moved to another location than the default. In the + forked child, the stack is back where it usually is when started from + another 64 bit process. Therefore we have to be able to recognize + this scenarion now on 64 bit as well. We I don't believe it... */ NTSTATUS ret; PROCESS_BASIC_INFORMATION pbi; HANDLE parent; @@ -86,7 +97,7 @@ wow64_test_for_64bit_parent () /* 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) + if (&wow64 >= (PULONG_PTR) allocbase && &wow64 < (PULONG_PTR) stackbase) return false; /* Check if the parent is a native 64 bit process. Unfortunately there's @@ -107,6 +118,8 @@ wow64_test_for_64bit_parent () return !wow64; } +#ifndef __x86_64__ + PVOID wow64_revert_to_original_stack (PVOID &allocationbase) { @@ -177,6 +190,8 @@ wow64_revert_to_original_stack (PVOID &allocationbase) return PTR_ADD (NtCurrentTeb()->Tib.StackBase, -16); } +#endif /* !__x86_64__ */ + /* Respawn WOW64 process. This is only called if we can't reuse the original stack. See comment in wow64_revert_to_original_stack for details. See _dll_crt0 for the call of this function. @@ -211,5 +226,3 @@ wow64_respawn_process () TerminateProcess (GetCurrentProcess (), ret); ExitProcess (ret); } - -#endif /* !__x86_64__ */ diff --git a/winsup/cygwin/wow64.h b/winsup/cygwin/wow64.h index a0b5b318d..150561c8c 100644 --- a/winsup/cygwin/wow64.h +++ b/winsup/cygwin/wow64.h @@ -1,6 +1,6 @@ /* wow64.h - Copyright 2011, 2012 Red Hat, Inc. + Copyright 2011, 2012, 2015 Red Hat, Inc. This file is part of Cygwin. @@ -8,14 +8,12 @@ This software is a copyrighted work licensed under the terms of the Cygwin license. Please consult the file "CYGWIN_LICENSE" for details. */ -#ifndef __x86_64__ -/* WOW64 only plays a role in the 32 bit version. Don't use any of this - in the 64 bit version. */ - extern bool NO_COPY wow64_needs_stack_adjustment; - extern bool wow64_test_for_64bit_parent (); -extern PVOID wow64_revert_to_original_stack (PVOID &allocationbase); extern void wow64_respawn_process () __attribute__ ((noreturn)); +#ifndef __x86_64__ + +extern PVOID wow64_revert_to_original_stack (PVOID &allocationbase); + #endif /* !__x86_64__ */