Fix access ordering issue in Module::AllocateDynamicEntry on arm64 (#53556)
authorJan Vorlicek <jan.vorlicek@volny.cz>
Wed, 2 Jun 2021 00:31:36 +0000 (02:31 +0200)
committerGitHub <noreply@github.com>
Wed, 2 Jun 2021 00:31:36 +0000 (02:31 +0200)
While stress testing runtime on Windows ARM64 to repro an issue, I have
hit an ordering issue in access to m_maxDynamicEntries and
m_pDynamicStaticsInfo in the Module::AllocateDynamicEntry.

In one of a few thousands of runs, I got a runtime crash when the
m_pDynamicStaticsInfo was NULL. I was able to repro this issue reliably
in the above mentioned number of runs. After the fix, it doesn't repro
anymore.

The newId was 1 at the time of the crash, the m_maxDynamicEntries was
around 128. So the crashing thread has got the id 1, then another thread
updated the m_maxDynamicEntries before the crashing thread executed the
`if (newId >= m_maxDynamicEntries)`, so the thread went right to the
`m_pDynamicStaticsInfo[newId].pEnclosingMT = pMT;`. The
m_pDynamicStaticInfo is written before the m_maxDynamicEntries, so it
was expected that it cannot be NULL, but the crashing thread has seen
the operations in an opposite order due to the CPU access reordering.

The fix is to add VolatileLoad for accessing the m_maxDynamicEntries and
VolatileStore to set it. That ensures that the writes will be visible in
the intended order.

src/coreclr/vm/ceeload.cpp

index e1003c8..d95c02a 100644 (file)
@@ -2618,7 +2618,7 @@ DWORD Module::AllocateDynamicEntry(MethodTable *pMT)
 
     DWORD newId = FastInterlockExchangeAdd((LONG*)&m_cDynamicEntries, 1);
 
-    if (newId >= m_maxDynamicEntries)
+    if (newId >= VolatileLoad(&m_maxDynamicEntries))
     {
         CrstHolder ch(&m_Crst);
 
@@ -2637,7 +2637,7 @@ DWORD Module::AllocateDynamicEntry(MethodTable *pMT)
                 memcpy(pNewDynamicStaticsInfo, m_pDynamicStaticsInfo, sizeof(DynamicStaticsInfo) * m_maxDynamicEntries);
 
             m_pDynamicStaticsInfo = pNewDynamicStaticsInfo;
-            m_maxDynamicEntries = maxDynamicEntries;
+            VolatileStore(&m_maxDynamicEntries, maxDynamicEntries);
         }
     }