[sgen] Don't suspend thread in wow64 transition
authorVlad Brezae <brezaevlad@gmail.com>
Wed, 7 Dec 2016 00:06:00 +0000 (02:06 +0200)
committerVlad Brezae <brezaevlad@gmail.com>
Tue, 13 Dec 2016 20:55:36 +0000 (22:55 +0200)
The stack pointer provided by GetThreadContext is not reliable.

mono/utils/mono-threads-posix.c
mono/utils/mono-threads-windows.c
mono/utils/mono-threads.c
mono/utils/mono-threads.h

index 5447a8fe93f412b9eef902ff644c9246197a7830..66746ad3dfa2896823229ab1e97335b19eb32351 100644 (file)
@@ -133,6 +133,17 @@ mono_threads_platform_create_thread (MonoThreadStart thread_fn, gpointer thread_
        return 0;
 }
 
+void
+mono_threads_platform_init (void)
+{
+}
+
+gboolean
+mono_threads_platform_in_critical_region (MonoNativeThreadId tid)
+{
+       return FALSE;
+}
+
 gboolean
 mono_threads_platform_yield (void)
 {
index f3f160b30c93e2a0146638caff40639a338b45f7..2a28e9b5a13ab6fac7ed0e76c751e68101d43e48 100644 (file)
@@ -289,6 +289,56 @@ mono_threads_platform_get_stack_bounds (guint8 **staddr, size_t *stsize)
 
 }
 
+#if SIZEOF_VOID_P == 4 && G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
+typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL);
+static gboolean is_wow64 = FALSE;
+#endif
+
+/* We do this at init time to avoid potential races with module opening */
+void
+mono_threads_platform_init (void)
+{
+#if SIZEOF_VOID_P == 4 && G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
+       LPFN_ISWOW64PROCESS is_wow64_func = (LPFN_ISWOW64PROCESS) GetProcAddress (GetModuleHandle (TEXT ("kernel32")), "IsWow64Process");
+       if (is_wow64_func)
+               is_wow64_func (GetCurrentProcess (), &is_wow64);
+#endif
+}
+
+/*
+ * When running x86 process under x64 system syscalls are done through WoW64. This
+ * needs to do a transition from x86 mode to x64 so it can syscall into the x64 system.
+ * Apparently this transition invalidates the ESP that we would get from calling
+ * GetThreadContext, so we would fail to scan parts of the thread stack. We attempt
+ * to query whether the thread is in such a transition so we try to restart it later.
+ * We check CONTEXT_EXCEPTION_ACTIVE for this, which is highly undocumented.
+ */
+gboolean
+mono_threads_platform_in_critical_region (MonoNativeThreadId tid)
+{
+       gboolean ret = FALSE;
+#if SIZEOF_VOID_P == 4 && G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
+/* FIXME On cygwin these are not defined */
+#if defined(CONTEXT_EXCEPTION_REQUEST) && defined(CONTEXT_EXCEPTION_REPORTING) && defined(CONTEXT_EXCEPTION_ACTIVE)
+       if (is_wow64) {
+               HANDLE handle = OpenThread (THREAD_ALL_ACCESS, FALSE, tid);
+               if (handle) {
+                       CONTEXT context;
+                       ZeroMemory (&context, sizeof (CONTEXT));
+                       context.ContextFlags = CONTEXT_EXCEPTION_REQUEST;
+                       if (GetThreadContext (handle, &context)) {
+                               if ((context.ContextFlags & CONTEXT_EXCEPTION_REPORTING) &&
+                                               (context.ContextFlags & CONTEXT_EXCEPTION_ACTIVE))
+                                       ret = TRUE;
+                       }
+                       CloseHandle (handle);
+               }
+       }
+#endif
+#endif
+       return ret;
+}
+
 gboolean
 mono_threads_platform_yield (void)
 {
index e344ec1ed1f11728d88fe90db446891adaaae43a..3ee65dcfa870158754b8464cc98611f0802be98b 100644 (file)
@@ -711,6 +711,7 @@ mono_threads_init (MonoThreadInfoCallbacks *callbacks, size_t info_size)
        mono_thread_smr_init ();
        mono_threads_suspend_init ();
        mono_threads_coop_init ();
+       mono_threads_platform_init ();
 
 #if defined(__MACH__)
        mono_mach_init (thread_info_key);
@@ -831,6 +832,9 @@ is_thread_in_critical_region (MonoThreadInfo *info)
        gpointer stack_start;
        MonoThreadUnwindState *state;
 
+       if (mono_threads_platform_in_critical_region (mono_thread_info_get_tid (info)))
+               return TRUE;
+
        /* Are we inside a system critical region? */
        if (info->inside_critical_region)
                return TRUE;
index e14bf5e0999fe302ca710fca3d8585341780c1d4..a5db1c0c4e58bb8ab84904661400b97f758cf4e3 100644 (file)
@@ -480,6 +480,8 @@ gint mono_threads_suspend_get_abort_signal (void);
 
 int mono_threads_platform_create_thread (MonoThreadStart thread_fn, gpointer thread_data, gsize* const stack_size, MonoNativeThreadId *out_tid);
 void mono_threads_platform_get_stack_bounds (guint8 **staddr, size_t *stsize);
+void mono_threads_platform_init (void);
+gboolean mono_threads_platform_in_critical_region (MonoNativeThreadId tid);
 gboolean mono_threads_platform_yield (void);
 void mono_threads_platform_exit (gsize exit_code);