Fix GC of other thread during native frames unwinding
authorJan Vorlicek <janvorli@microsoft.com>
Mon, 27 Apr 2015 00:28:22 +0000 (17:28 -0700)
committerJan Vorlicek <janvorli@microsoft.com>
Mon, 27 Apr 2015 00:42:00 +0000 (17:42 -0700)
This change fixes a problem when the GC is attempting to scan a stack
of another thread that have just performed the native stack segment
unwind, but didn't have a chance to update the exception tracker
to reflect the fact that the stack range from m_pInitialExplicitFrame
and m_pLimitFrame is in the reclaimed part of the stack and is waiting
for the GC to complete (it uses the GCX_COOP).
The GC then attempts to scan explicit frames in the already reclaimed
part of the stack and crashes.
Besides fixing the issue, I have noticed that we are missing GCX_COOP
to enter the cooperative mode when unwinding frame chain in the
UnwindManagedExceptionPass1. I have also found that we don't have the
GCX_COOP in the HandleHardwareException properly scoped.
So I have fixed those as well.

src/vm/exceptionhandling.cpp
src/vm/exceptionhandling.h

index e382b2f..2a5ad89 100644 (file)
@@ -4589,10 +4589,20 @@ VOID DECLSPEC_NORETURN UnwindManagedExceptionPass1(PAL_SEHException& ex)
 
             *currentFlags = firstPassFlags;
 
-            // Pop all frames that are below the block of native frames and that would be
-            // in the unwound part of the stack when UnwindManagedExceptionPass1 is resumed 
-            // at the next managed frame.
-            UnwindFrameChain(GetThread(), (VOID*)frameContext.Rsp);
+            {
+                GCX_COOP();
+                // Pop all frames that are below the block of native frames and that would be
+                // in the unwound part of the stack when UnwindManagedExceptionPass1 is resumed 
+                // at the next managed frame.
+                UnwindFrameChain(GetThread(), (VOID*)frameContext.Rsp);
+
+                // We are going to reclaim the stack range that was scanned by the exception tracker
+                // until now. We need to reset the explicit frames range so that if GC fires before
+                // we recreate the tracker at the first managed frame after unwinding the native 
+                // frames, it doesn't attempt to scan the reclaimed stack range.
+                ExceptionTracker* pTracker = GetThread()->GetExceptionState()->GetCurrentExceptionTracker();
+                pTracker->ResetUnwoundExplicitFramesRange();
+            }
 
             // Now we need to unwind the native frames until we reach managed frames again or the exception is
             // handled in the native code.
@@ -4956,8 +4966,10 @@ VOID PALAPI HandleHardwareException(PAL_SEHException* ex)
 #if defined(WIN64EXCEPTIONS)
             *((&fef)->GetGSCookiePtr()) = GetProcessGSCookie();
 #endif // WIN64EXCEPTIONS
-            GCX_COOP();
-            fef.InitAndLink(&ex->ContextRecord);
+            {
+                GCX_COOP();     // Must be cooperative to modify frame chain.
+                fef.InitAndLink(&ex->ContextRecord);
+            }
 
 #ifdef _AMD64_
             // It is possible that an overflow was mapped to a divide-by-zero exception. 
index c8581e3..72db357 100644 (file)
@@ -298,6 +298,13 @@ public:
         return m_pInitialExplicitFrame;
     }
 
+    // Reset the range of explicit frames that covers already unwound frames.
+    void ResetUnwoundExplicitFramesRange()
+    {
+        m_pInitialExplicitFrame = NULL;
+        m_pLimitFrame = NULL;
+    }
+
     // Determines if we have unwound to the specified parent method frame.
     // Currently this is only used for funclet skipping.
     static bool IsUnwoundToTargetParentFrame(CrawlFrame * pCF, StackFrame sfParent);