diff options
author | Hamayama <hamay1010@gmail.com> | 2019-02-08 00:51:25 +0300 |
---|---|---|
committer | Jonathan Chambers <jonathan@unity3d.com> | 2022-03-16 20:49:48 +0300 |
commit | be26f03dee11d1d5ee04940dc6e4ac5d02e06922 (patch) | |
tree | 22e52632a2deb9c5cb795fc0aa7c9d434e20c6b1 | |
parent | b8a4d0dfcd7545e2a422237db0af75f72cf10e96 (diff) |
Fix GetThreadContext stale register values use if WoW64 (Win32)
Issue #262 (bdwgc).
* misc.c [MSWIN32 && !MSWINRT_FLAVOR && !MSWIN_XBOX1]
(GC_win32_MessageBoxA): Do not define unless SMALL_CONFIG.
* misc.c [MSWIN32 && !_WIN64 && GC_WIN32_THREADS && CHECK_NOT_WOW64]
(GC_init): Do not call IsWow64Process() and GC_win32_MessageBoxA().
* win32_threads.c [I386] (isWow64): New static variable.
* win32_threads.c [I386] (GC_push_stack_for): If isWow64 then set also
CONTEXT_EXCEPTION_REQUEST and CONTEXT_SEGMENTS bits in ContextFlags;
if isWow64, and CONTEXT_EXCEPTION_REPORTING and
CONTEXT_EXCEPTION_ACTIVE are set on return from GetThreadContext then
call GetThreadSelectorEntry and use StackLimit of FS selector to set
sp local variable (instead of context.Esp); add comment.
* win32_threads.c [I386 && DEBUG_THREADS] (GC_push_stack_for): Call
GC_log_printf() to report TIB stack limit/base and the case when
CONTEXT_EXCEPTION_REQUEST is not supported.
* win32_threads.c [I386] (GC_thr_init): Set isWow64 by IsWow64Process()
if the later is available.
-rw-r--r-- | misc.c | 29 | ||||
-rw-r--r-- | win32_threads.c | 79 |
2 files changed, 78 insertions, 30 deletions
@@ -812,9 +812,8 @@ GC_API int GC_CALL GC_is_init_called(void) } #endif -#if defined(MSWIN32) && !defined(MSWINRT_FLAVOR) && (!defined(SMALL_CONFIG) \ - || (!defined(_WIN64) && defined(GC_WIN32_THREADS) \ - && defined(CHECK_NOT_WOW64))) && !defined(_XBOX_ONE) +#if defined(MSWIN32) && !defined(MSWINRT_FLAVOR) && !defined(MSWIN_XBOX1) \ + && !defined(SMALL_CONFIG) STATIC void GC_win32_MessageBoxA(const char *msg, const char *caption, unsigned flags) { @@ -902,30 +901,6 @@ GC_API void GC_CALL GC_init(void) initial_heap_sz = MINHINCR * HBLKSIZE; # endif -# if defined(MSWIN32) && !defined(_WIN64) && defined(GC_WIN32_THREADS) \ - && defined(CHECK_NOT_WOW64) && !defined(_XBOX_ONE) - { - /* Windows: running 32-bit GC on 64-bit system is broken! */ - /* WoW64 bug affects SuspendThread, no workaround exists. */ - HMODULE hK32 = GetModuleHandle(TEXT("kernel32.dll")); - if (hK32) { - FARPROC pfn = GetProcAddress(hK32, "IsWow64Process"); - BOOL bIsWow64 = FALSE; - if (pfn - && (*(BOOL (WINAPI*)(HANDLE, BOOL*))pfn)(GetCurrentProcess(), - &bIsWow64) - && bIsWow64) { - GC_win32_MessageBoxA("This program uses BDWGC garbage collector" - " compiled for 32-bit but running on 64-bit Windows.\n" - "This is known to be broken due to a design flaw" - " in Windows itself! Expect erratic behavior...", - "32-bit program running on 64-bit system", - MB_ICONWARNING | MB_OK); - } - } - } -# endif - DISABLE_CANCEL(cancel_state); /* Note that although we are nominally called with the */ /* allocation lock held, the allocation lock is now */ diff --git a/win32_threads.c b/win32_threads.c index 252335cd..a3f02158 100644 --- a/win32_threads.c +++ b/win32_threads.c @@ -1410,6 +1410,10 @@ static GC_bool may_be_in_stack(ptr_t s) && !(last_info.Protect & PAGE_GUARD); } +#if defined(I386) + static BOOL isWow64; /* Is running 32-bit code on Win64? */ +#endif + STATIC word GC_push_stack_for(GC_thread thread, DWORD me) { ptr_t sp, stack_min; @@ -1422,8 +1426,25 @@ STATIC word GC_push_stack_for(GC_thread thread, DWORD me) } else if ((sp = thread -> thread_blocked_sp) == NULL) { /* Use saved sp value for blocked threads. */ /* For unblocked threads call GetThreadContext(). */ - /* we cache context when suspending the thread since it may require looping */ - CONTEXT context = thread->saved_context; + CONTEXT context; +# if defined(I386) +# ifndef CONTEXT_EXCEPTION_ACTIVE +# define CONTEXT_EXCEPTION_ACTIVE 0x08000000 +# define CONTEXT_EXCEPTION_REQUEST 0x40000000 +# define CONTEXT_EXCEPTION_REPORTING 0x80000000 +# endif + + if (isWow64) { + context.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL + | CONTEXT_EXCEPTION_REQUEST + | CONTEXT_SEGMENTS; + } else +# endif + /* else */ { + context.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL; + } + if (!GetThreadContext(THREAD_HANDLE(thread), &context)) + ABORT("GetThreadContext failed"); /* Push all registers that might point into the heap. Frame */ /* pointer registers are included in case client code was */ @@ -1433,7 +1454,45 @@ STATIC word GC_push_stack_for(GC_thread thread, DWORD me) # define PUSH4(r1,r2,r3,r4) (PUSH2(r1,r2), PUSH2(r3,r4)) # if defined(I386) PUSH4(Edi,Esi,Ebx,Edx), PUSH2(Ecx,Eax), PUSH1(Ebp); - sp = (ptr_t)context.Esp; + /* WoW64 workaround. */ + if (isWow64 + && (context.ContextFlags & CONTEXT_EXCEPTION_REPORTING) != 0 + && (context.ContextFlags & (CONTEXT_EXCEPTION_ACTIVE + /* | CONTEXT_SERVICE_ACTIVE */)) != 0) { + LDT_ENTRY selector; + PNT_TIB tib; + + if (!GetThreadSelectorEntry(THREAD_HANDLE(thread), context.SegFs, + &selector)) + ABORT("GetThreadSelectorEntry failed"); + tib = (PNT_TIB)(selector.BaseLow + | (selector.HighWord.Bits.BaseMid << 16) + | (selector.HighWord.Bits.BaseHi << 24)); + /* GetThreadContext() might return stale register values, so */ + /* we scan the entire stack region (down to the stack limit). */ + /* There is no 100% guarantee that all the registers are pushed */ + /* but we do our best (the proper solution would be to fix it */ + /* inside Windows OS). */ + sp = (ptr_t)tib->StackLimit; +# ifdef DEBUG_THREADS + GC_log_printf("TIB stack limit/base: %p .. %p\n", + (void *)tib->StackLimit, (void *)tib->StackBase); +# endif + GC_ASSERT(!((word)thread->stack_base + COOLER_THAN (word)tib->StackBase)); + } else { +# ifdef DEBUG_THREADS + { + static GC_bool logged; + if (isWow64 && !logged + && (context.ContextFlags & CONTEXT_EXCEPTION_REPORTING) == 0) { + GC_log_printf("CONTEXT_EXCEPTION_REQUEST not supported\n"); + logged = TRUE; + } + } +# endif + sp = (ptr_t)context.Esp; + } # elif defined(X86_64) PUSH4(Rax,Rcx,Rdx,Rbx); PUSH2(Rbp, Rsi); PUSH1(Rdi); PUSH4(R8, R9, R10, R11); PUSH4(R12, R13, R14, R15); @@ -2466,6 +2525,20 @@ GC_INNER void GC_thr_init(void) } # endif +# if defined(I386) + /* Set isWow64 flag. */ + { + HMODULE hK32 = GetModuleHandle(TEXT("kernel32.dll")); + if (hK32) { + FARPROC pfn = GetProcAddress(hK32, "IsWow64Process"); + if (pfn + && !(*(BOOL (WINAPI*)(HANDLE, BOOL*))pfn)(GetCurrentProcess(), + &isWow64)) + isWow64 = FALSE; /* IsWow64Process failed */ + } + } +# endif + /* Add the initial thread, so we can stop it. */ # ifdef GC_ASSERTIONS sb_result = |