我有以下在 Visual Studio 2015 中编译的类:
class MitigationPolicyChecker
{
public:
static auto& getInstance()
{
static MitigationPolicyChecker instance;
return instance;
}
MitigationPolicyChecker(MitigationPolicyChecker const&) = delete;
MitigationPolicyChecker(MitigationPolicyChecker&&) = delete;
MitigationPolicyChecker& operator=(MitigationPolicyChecker const&) = delete;
MitigationPolicyChecker& operator=(MitigationPolicyChecker&&) = delete;
bool IsDynamicCodeProhibited();
private:
MitigationPolicyChecker() = default;
~MitigationPolicyChecker() = default;
void GetDynamicCodePolicy();
bool m_bDynamicCodeProhibited = false;
};
该类旨在从 DllMain 运行并检查 NtDll 加载的图像中的结构,以检查是否启用了某个缓解策略。
在极少数情况下(<0.0001% 的 dll 加载次数),此单例会导致应用程序崩溃。但是,崩溃与班级试图做的事情或众所周知的 DllMain 中不允许的事情列表无关(据我所知)。它与单例自身的实例化有关。
WinDbg 中的调用堆栈如下所示:
00 OurDll!MitigationPolicyChecker::getInstance+0x1a
02 OurDll!Initialize+0x79
03 OurDll!DllMain+0x1c9
04 OurDll!dllmain_dispatch+0x74
05 ntdll!LdrpRunInitializeRoutines+0x1fe
06 ntdll!LdrGetProcedureAddressEx+0x2aa
07 ntdll!LdrpCorInitialize+0x1a1
08 ntdll!LdrpInitializeProcess+0x1816
09 ntdll! ?? ::FNODOBFM::`string'+0x22790
0a ntdll!LdrInitializeThunk+0xe
线路发生访问冲突static MitigationPolicyChecker instance
而崩溃的流水线是这里的最后一行,根据异常记录貌似rdx和rcx都是null(可能是线程本地存储失败??):
0:000> uf OurDll!MitigationPolicyChecker::getInstance
OurDll!MitigationPolicyChecker::getInstance:
8 000007fe`fbc38ed0 4883ec28 sub rsp,28h
9 000007fe`fbc38ed4 b804000000 mov eax,4
9 000007fe`fbc38ed9 8bc0 mov eax,eax
9 000007fe`fbc38edb 8b0dcf850b00 mov ecx,dword ptr [OurDll!_tls_index (000007fe`fbcf14b0)]
9 000007fe`fbc38ee1 65488b142558000000 mov rdx,qword ptr gs:[58h]
9 000007fe`fbc38eea 488b0cca mov rcx,qword ptr [rdx+rcx*8]
那么这里发生了什么?该进程在崩溃时只有一个线程处于活动状态,所以我不认为这是一个并发问题。据我所知:
- 静态对象应在首次访问时创建。
- 静态通常会在 DllMain 之前初始化,但在这种情况下,由于它是函数级别的静态,它会在调用时创建,并且由于它是 VS2015,它将被创建为“魔术静态”。
有什么我遗漏的东西使这不安全吗?