Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
mingkuang-Chuyu opened this issue Nov 13, 2020 · 11 comments
Closed

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

mingkuang-Chuyu opened this issue Nov 13, 2020 · 11 comments
Labels
类型:问题 程序不工作,或者遇到错误。 进度:处理完成

Comments

@mingkuang-Chuyu
Copy link
Collaborator

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
Copy link
Collaborator Author

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

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

@lygstate
Copy link

lygstate commented Nov 3, 2023

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

@mingkuang-Chuyu
Copy link
Collaborator Author

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

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

@lygstate
Copy link

lygstate commented Nov 5, 2023

死锁场景:

线程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
Copy link

lygstate commented Nov 6, 2023

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

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

@mingkuang-Chuyu
Copy link
Collaborator Author

死锁场景:

线程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
Copy link
Collaborator Author

感觉问题的根源在于 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锁定代码里依赖复杂的代码这又会导致无法控制,出现死锁风险。
  • 某些进程拥有沙箱,按需加载也会导致按需时加载失败。

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

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

@lygstate
Copy link

lygstate commented Nov 7, 2023

感觉问题的根源在于 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
Copy link

lygstate commented Nov 7, 2023

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
Copy link
Collaborator Author

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

@mingkuang-Chuyu
Copy link
Collaborator Author

mingkuang-Chuyu commented Nov 8, 2023

感觉问题的根源在于 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,我们可以想办法解决。

  • 微软确实有延迟加载,但是不是意味着没有问题,这里的根本原因,延迟加载期间,无法保证locale锁到底处于那种状态。另外延迟加载也可能导致其他某种加载失败,比如DLL搜索顺序被意外更改。
  • 微软CRT的里加载DLL的函数名字确实叫 get module handle啥的,但是内部实现是LoadLibraryExW
  • 另外LoadDLL的不止ntdll,kernel32。还有各种API-Set、advapi32、user32啥的。
  • __acrt_eagerly_load_locale_apis的发生在广义的DllMain里,你总不能因为调用链路上没有DllMain所以说CRT DllMain没有LoadDll吧?如果是这样,那么YY-Thunks加载工作也不在DllMain 里……

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

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
类型:问题 程序不工作,或者遇到错误。 进度:处理完成
Projects
None yet
Development

No branches or pull requests

2 participants