如果在 Windows DLL 加载期间调用 `to_string( std::stacktrace::current() )` 为什么会挂起

问题描述 投票:0回答:1

我使用 Visual Studio 2022 版本 17.9.2,在应用程序的异常处理程序中,我开始使用新的 C++23 功能

to_string( std::stacktrace::current() )
记录调用堆栈。一般来说,它工作得很好,但如果在 DLL 加载期间发生异常(WIN32 API 调用
LoadLibraryW
),那么我的程序就会挂起。

即使没有异常,它也可以被重现,只要我的 DLL 有一个全局对象在其构造函数中调用

to_string
即可:

struct A {
    A() {
        (void)to_string( std::stacktrace::current() );
    }
} a_;

例如,在 Windows 11 上,程序与堆栈一起挂起,如下所示:

ntdll.dll!NtWaitForSingleObject()
KernelBase.dll!WaitForSingleObjectEx()
winnsi.dll!NsiRpcRegisterChangeNotificationEx()
winnsi.dll!NsiRpcRegisterChangeNotification()
IPHLPAPI.DLL!InternalRegisterChangeNotification()
IPHLPAPI.DLL!NotifyIpInterfaceChange()
cryptnet.dll!I_CryptNetGetConnectivity()
crypt32.dll!ChainGetConnectivity(void)
crypt32.dll!CChainCallContext::IsConnected(void)
crypt32.dll!CCertChainEngine::TriggerPreFetch()
crypt32.dll!CCertChainEngine::GetChainContext()
crypt32.dll!CertGetCertificateChain()
wintrust.dll!_WalkChain()
wintrust.dll!WintrustCertificateTrust()
wintrust.dll!I_VerifyTrust()
wintrust.dll!WinVerifyTrust()
dbgeng.dll!DbgWinVerifyTrust(struct HWND__ *,struct _GUID *,void *)
dbgeng.dll!VerifyFileSignature(unsigned short const *)
dbgeng.dll!ExtensionSecurityValidate()
dbgeng.dll!ExtensionInfo::Load(class DebugClient *,class TargetInfo *,unsigned short const *,bool)
dbgeng.dll!TargetInfo::AddSpecificExtensions(class DebugClient *)
dbgeng.dll!NotifyDebuggeeActivation(void)
dbgeng.dll!LiveUserTargetInfo::WaitForEvent(unsigned long,unsigned long,unsigned long,unsigned long *)
dbgeng.dll!WaitForAnyTarget(unsigned long,unsigned long,unsigned long,unsigned long,unsigned long *)
dbgeng.dll!RawWaitForEvent(class DebugClient *,unsigned long,unsigned long)
dbgeng.dll!DebugClient::WaitForEvent()
MyDLL.dll!`anonymous namespace'::dbg_eng_data::try_initialize() Line 113    C++
MyDLL.dll!__std_stacktrace_to_string(const void * const * const _Addresses, const unsigned __int64 _Size, void * const _Str, unsigned __int64(*)(unsigned __int64, void *, void *, unsigned __int64(*)(char *, unsigned __int64, void *) noexcept) _Fill) Line 306  C++
MyDLL.dll!std::to_string(const std::basic_stacktrace<std::allocator<std::stacktrace_entry>> &) Line 343 C++
MyDLL.dll!A::{ctor}() Line 265  C++
MyDLL.dll!`dynamic initializer for 'a_''() Line 267 C++
ucrtbase.dll!_initterm()
MyDLL.dll!dllmain_crt_process_attach(HINSTANCE__ * const instance, void * const reserved) Line 66   C++
...

这是 Windows 上

std::stacktrace
的已知限制吗?以及如何安全地使用它(例如检测它无法调用的时刻)?

windows visual-c++ dll stack-trace c++23
1个回答
0
投票

这并不是什么时候可以安全地调用 std::stacktrace::current 的问题。

大多数事情在 DllMain 期间都是不允许的,DllMain 是运行 DLL 的全局构造函数的地方。

Microsoft 概述了最佳实践,但简而言之,您在全局构造函数中几乎不应该执行任何操作。

在这种情况下,获取堆栈跟踪看起来涉及间接调用 WinVerifyTrust,这可能会调用创建线程,当持有加载程序锁时,这可能会导致死锁,如链接页面中更详细的描述。但这并不是“stacktrace::current”有特殊规则,而是“DLL 中的全局构造函数有特殊规则”。

© www.soinside.com 2019 - 2024. All rights reserved.