Chuyu-Team / YY-Thunks

Fix DecodePointer, EncodePointer,RegDeleteKeyEx etc. APIs not found in Windows XP RTM.
MIT License
497 stars 102 forks source link

某些情况下导致死锁问题(感谢 冯立夫) #7

Closed mingkuang-Chuyu closed 3 years ago

mingkuang-Chuyu commented 3 years ago

Vsitual Studio 2017 + Windows XP模式 + MT 运行环境:Windows 7 x64

死锁场景: 线程A:Thread创建回调 -->DLL Load锁定 --> tdp初始化 --> 等待locale锁 线程B:locale锁定 --> _Atexit --> EncodePointer(YY-Thunks) --> 等待 DLL Load锁

mingkuang-Chuyu commented 3 years ago

https://github.com/Chuyu-Team/YY-Thunks/releases/tag/v1.0.3-Beta1

已经修复此问题,YY-Thunks将预加载所有try_get函数。

lygstate commented 8 months ago

是不是只需要确保 kernel32 加载即可

mingkuang-Chuyu commented 8 months ago

解决这个死锁缺少只要加载kernel32 即可。但是在其他场景加载DLL时这任然可能出现其他死锁。

以最坏情况,也只是跟原始行为一样而已。在没有更好的解决方案之前,性能 以及 死锁风险里我选择避免死锁风险。

lygstate commented 8 months ago

死锁场景:

线程A:Thread创建回调 -->DLL Load锁定 --> tdp初始化 --> 等待locale锁

`DLL Load锁定` 这个是YY-Thunks里面的load吗?

线程B:locale锁定 --> _Atexit --> EncodePointer(YY-Thunks) --> 等待 DLL Load锁

从这个感觉是不是把 try_get_module 枷锁即可

try_get_module()
{
enter_critical_section
do get module
exit_critical_section
}
lygstate commented 8 months ago

感觉问题的根源在于 Dynamic-Link库最佳做法

以及 https://www.zhihu.com/question/22720399/answer/22401573 也就是要避免在 DllMain 里面调用 LoadLibraryLoadLibraryEx 所以正确的做法是 对于 ntdll 和kernel32 使用 GetModuleHandleW, 对于其它的dll,在需要使用的时候在加载

mingkuang-Chuyu commented 8 months ago

死锁场景:

线程A:Thread创建回调 -->DLL Load锁定 --> tdp初始化 --> 等待locale锁

`DLL Load锁定` 这个是YY-Thunks里面的load吗?

线程B:locale锁定 --> _Atexit --> EncodePointer(YY-Thunks) --> 等待 DLL Load锁

从这个感觉是不是把 try_get_module 枷锁即可

try_get_module()
{
enter_critical_section
do get module
exit_critical_section
}

你是认真的吗?

mingkuang-Chuyu commented 8 months ago

感觉问题的根源在于 Dynamic-Link库最佳做法

以及 https://www.zhihu.com/question/22720399/answer/22401573 也就是要避免在 DllMain 里面调用 LoadLibraryLoadLibraryEx 所以正确的做法是 对于 ntdll 和kernel32 使用 GetModuleHandleW, 对于其它的dll,在需要使用的时候在加载

最佳实践?我们只能从中选择现在没有遇到问题的路……

即使是微软CRT,任然也任然在DLLMain里LoadDLL。

lygstate commented 8 months ago

感觉问题的根源在于 Dynamic-Link库最佳做法 以及 https://www.zhihu.com/question/22720399/answer/22401573 也就是要避免在 DllMain 里面调用 LoadLibraryLoadLibraryEx 所以正确的做法是 对于 ntdll 和kernel32 使用 GetModuleHandleW, 对于其它的dll,在需要使用的时候在加载

  • 对于Win7,GetModuleHandleW任然会入锁,并不会变得更好,此外不用GetModuleHandleW改成用VirtualQuery魔法拿到的也是个不稳定的kernelbase,不适合YY-Thunks的场景。

    • 唯一安全的是DllMain里调用GetModuleHandle,这个是安全的。因为当前线程已经取得锁,不会引入更多锁。
  • 此外需要时使用,这可能会产生死锁,死锁的原因就在于CRT会初始化时锁定了locale。一旦其他locale锁定代码里依赖复杂的代码这又会导致无法控制,出现死锁风险。
  • 某些进程拥有沙箱,按需加载也会导致按需时加载失败。

最佳实践?我们只能从中选择现在没有遇到问题的路……

因为遇到问题了,我有个c++测试程序在win7下,在load pdh.DLL 和 shell32.dll的时候直接蹦了

即使是微软CRT,任然也任然在DLLMain里LoadDLL。

微软不是有delay laod 机制吗 那个也是在调用的时候才加载的吧,

CRT 我看用的是 get module handle 对于 ntdll 和 kernel32,其它的 我至少调试的时候没发现 dllmain 里面有 load dll,

lygstate commented 8 months ago

ucrt 是通过__acrt_eagerly_load_locale_apis 来解决锁的问题,并且在 DllMain里面并没有加载任何库

// This function simply attempts to get each of the locale-related APIs.  This
// allows a caller to "pre-load" the modules in which these APIs are hosted.  We
// use this in the _wsetlocale implementation to avoid calls to LoadLibrary while
// the locale lock is held.
extern "C" void __cdecl __acrt_eagerly_load_locale_apis()
{
    try_get_AreFileApisANSI();
    try_get_CompareStringEx();
    try_get_EnumSystemLocalesEx();
    try_get_GetDateFormatEx();
    try_get_GetLocaleInfoEx();
    try_get_GetTimeFormatEx();
    try_get_GetUserDefaultLocaleName();
    try_get_IsValidLocaleName();
    try_get_LCMapStringEx();
    try_get_LCIDToLocaleName();
    try_get_LocaleNameToLCID();
}
mingkuang-Chuyu commented 8 months ago

麻烦你调试一下 __acrt_eagerly_load_locale_apis 在什么时机被加载,虽然不是真的DllMain,但是也差不多,在CRT DllMain的入口。

mingkuang-Chuyu commented 8 months ago

感觉问题的根源在于 Dynamic-Link库最佳做法 以及 https://www.zhihu.com/question/22720399/answer/22401573 也就是要避免在 DllMain 里面调用 LoadLibraryLoadLibraryEx 所以正确的做法是 对于 ntdll 和kernel32 使用 GetModuleHandleW, 对于其它的dll,在需要使用的时候在加载

  • 对于Win7,GetModuleHandleW任然会入锁,并不会变得更好,此外不用GetModuleHandleW改成用VirtualQuery魔法拿到的也是个不稳定的kernelbase,不适合YY-Thunks的场景。

    • 唯一安全的是DllMain里调用GetModuleHandle,这个是安全的。因为当前线程已经取得锁,不会引入更多锁。
  • 此外需要时使用,这可能会产生死锁,死锁的原因就在于CRT会初始化时锁定了locale。一旦其他locale锁定代码里依赖复杂的代码这又会导致无法控制,出现死锁风险。
  • 某些进程拥有沙箱,按需加载也会导致按需时加载失败。

最佳实践?我们只能从中选择现在没有遇到问题的路……

因为遇到问题了,我有个c++测试程序在win7下,在load pdh.DLL 和 shell32.dll的时候直接蹦了

即使是微软CRT,任然也任然在DLLMain里LoadDLL。

微软不是有delay laod 机制吗 那个也是在调用的时候才加载的吧,

CRT 我看用的是 get module handle 对于 ntdll 和 kernel32,其它的 我至少调试的时候没发现 dllmain 里面有 load dll,

@lygstate 如果你发现崩溃,那么可以单独创建崩溃Bug,我们可以想办法解决。

最后我并不是说现在YY-Thunks这样做是合理的,我也不是说你说的没有道理,主要问题在于,延迟加载同样也是不合理的…… 但是描述问题我们任然需要实事求是。

嗯,大致就这样吧。如果YY-Thunks遇到崩溃等问题可以再提Bug。