Fix DynamicMethodDesc::Destroy vs code heap enumeration race (#713)
authorJan Vorlicek <janvorli@microsoft.com>
Tue, 10 Dec 2019 11:40:49 +0000 (12:40 +0100)
committerGitHub <noreply@github.com>
Tue, 10 Dec 2019 11:40:49 +0000 (12:40 +0100)
There is a race between DynamicMethodDesc::Destroy called from
the finalizer thread and the MethodDescs enumeration called from
ETW::MethodLog::SendEventsForJitMethods at process exit.
DynamicMethodDesc::Destroy cleanos up its members m_pSig and
m_pszMethodName and then it calls GetLCGMethodResolver()->Destroy();
That calls EEJitManager::FreeCodeMemory, which tries to take the
m_CodeHeapCritSec lock. But this lock is already held by
the ETW::MethodLog::SendEventsForJitMethods.
So the iterator can see half-destroyed DynamicMethodDesc and
a crash happens when trying to get the dynamic method name
from the m_pszMethodName for the ETW event purposes.

The fix is to call the GetLCGMethodResolver()->Destroy() before
destroying the m_pSig and m_pszMethodName.

src/coreclr/src/vm/dynamicmethod.cpp

index a9d350c..b25dfb7 100644 (file)
@@ -849,22 +849,27 @@ void DynamicMethodDesc::Destroy()
 
     _ASSERTE(IsDynamicMethod());
     LoaderAllocator *pLoaderAllocator = GetLoaderAllocator();
-
     LOG((LF_BCL, LL_INFO1000, "Level3 - Destroying DynamicMethod {0x%p}\n", this));
-    if (!m_pSig.IsNull())
+
+    // The m_pSig and m_pszMethodName need to be destroyed after the GetLCGMethodResolver()->Destroy() call
+    // otherwise the EEJitManager::CodeHeapIterator could return DynamicMethodDesc with these members NULLed, but
+    // the nibble map for the corresponding code memory indicating that this DynamicMethodDesc is still alive.
+    PCODE pSig = m_pSig.GetValue();
+    PTR_CUTF8 pszMethodName = m_pszMethodName.GetValue();
+
+    GetLCGMethodResolver()->Destroy();
+    // The current DynamicMethodDesc storage is destroyed at this point
+
+    if (pszMethodName != NULL)
     {
-        delete[] (BYTE*)m_pSig.GetValue();
-        m_pSig.SetValueMaybeNull(NULL);
+        delete[] pszMethodName;
     }
-    m_cSig = 0;
-    if (!m_pszMethodName.IsNull())
+
+    if (pSig != NULL)
     {
-        delete[] m_pszMethodName.GetValue();
-        m_pszMethodName.SetValueMaybeNull(NULL);
+        delete[] (BYTE*)pSig;
     }
 
-    GetLCGMethodResolver()->Destroy();
-
     if (pLoaderAllocator->IsCollectible())
     {
         if (pLoaderAllocator->Release())