Merge pull request #3359 from ntherning/sgen-avoid-scanning-stack-guard-page-on-windows
authorNiklas Therning <niklas@therning.org>
Fri, 5 Aug 2016 17:25:39 +0000 (19:25 +0200)
committerGitHub <noreply@github.com>
Fri, 5 Aug 2016 17:25:39 +0000 (19:25 +0200)
Prevent SGen from scanning the guard page used by Windows to dynamically grow the stack

mono/metadata/sgen-mono.c

index 33e01a8fb28717c98abc9e1f0a1978f156a81676..5c3d772c9facfac1cf717ac952192f0324739f8d 100644 (file)
@@ -2404,6 +2404,21 @@ sgen_client_scan_thread_data (void *start_nursery, void *end_nursery, gboolean p
                g_assert (info->client_info.stack_end);
 
                aligned_stack_start = (void*)(mword) ALIGN_TO ((mword)info->client_info.stack_start, SIZEOF_VOID_P);
+#ifdef HOST_WIN32
+               /* Windows uses a guard page before the committed stack memory pages to detect when the
+                  stack needs to be grown. If we suspend a thread just after a function prolog has
+                  decremented the stack pointer to point into the guard page but before the thread has
+                  been able to read or write to that page, starting the stack scan at aligned_stack_start
+                  will raise a STATUS_GUARD_PAGE_VIOLATION and the process will crash. This code uses
+                  VirtualQuery() to determine whether stack_start points into the guard page and then
+                  updates aligned_stack_start to point at the next non-guard page. */
+               MEMORY_BASIC_INFORMATION mem_info;
+               SIZE_T result = VirtualQuery(info->client_info.stack_start, &mem_info, sizeof(mem_info));
+               g_assert (result != 0);
+               if (mem_info.Protect & PAGE_GUARD) {
+                       aligned_stack_start = ((char*) mem_info.BaseAddress) + mem_info.RegionSize;
+               }
+#endif
 
                g_assert (info->client_info.suspend_done);
                SGEN_LOG (3, "Scanning thread %p, range: %p-%p, size: %zd, pinned=%zd", info, info->client_info.stack_start, info->client_info.stack_end, (char*)info->client_info.stack_end - (char*)info->client_info.stack_start, sgen_get_pinned_count ());