首页 > 解决方案 > 为什么这个单例从 DllMain 调用时偶尔会崩溃?

问题描述

我有以下在 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]

那么这里发生了什么?该进程在崩溃时只有一个线程处于活动状态,所以我不认为这是一个并发问题。据我所知:

  1. 静态对象应在首次访问时创建。
  2. 静态通常会在 DllMain 之前初始化,但在这种情况下,由于它是函数级别的静态,它会在调用时创建,并且由于它是 VS2015,它将被创建为“魔术静态”。

有什么我遗漏的东西使这不安全吗?

标签: c++windowsdllmain

解决方案


推荐阅读