chriskohlhoff / asio

Asio C++ Library
http://think-async.com/Asio
4.96k stars 1.22k forks source link

boost::asio::detail::winsock_init_base::startup can deadlock as it's called from DllMain if a Windows DLL includes it #496

Open keith-horton opened 4 years ago

keith-horton commented 4 years ago

If a Windows DLL includes the boost asio Networking headers, the following functions are invoked when the DLL gets loaded into the process. The below shows the stack when run with Application Verifier + the Networking provider enabled (I wrote the Networking provider :))

Child-SP RetAddr Call Site

00 0000001a27daeb50 00007ffaad683976 vrfcore!VerifierStopMessageEx+0x826 01 0000001a27daeeb0 00007ffaad68ce3d vfnet!StopIfCalledWithinDllMain+0x10a 02 0000001a27daef00 00007ffa86098017 vfnet!VfHookWSAStartup+0x2d 03 (Inline Function) ---------------- test!boost::asio::detail::winsock_init_base::startup+0x22 [...\boost\asio\detail\impl\winsock_init.ipp @ 39] 04 0000001a27daef30 00007ffa860dc61b test!boost::asio::detail::winsock_init<2,0>::winsock_init<2,0>+0x3b [...\boost\asio\detail\winsock_init.hpp @ 57] 05 0000001a27daf110 00007ffa8609f8fe test!_initterm+0x43 06 0000001a27daf140 00007ffa8609fa78 test!dllmain_crt_process_attach+0xae 07 0000001a27daf170 00007ffaacdb46d3 test!dllmain_dispatch+0x74 08 0000001a27daf1d0 00007ffab18cabf0 verifier!AVrfpStandardDllEntryPointRoutine+0xf3 09 0000001a27daf250 00007ffaacd27549 vrfcore!VfCoreStandardDllEntryPointRoutine+0x180 0a 0000001a27daf2e0 00007ffadeea8c93 vfbasics!AVrfpStandardDllEntryPointRoutine+0xe9 0b 0000001a27daf360 00007ffadeee2675 ntdll!LdrpCallInitRoutine+0x67 0c 0000001a27daf3d0 00007ffadeee242a ntdll!LdrpInitializeNode+0x1b1 0d 0000001a27daf510 00007ffadeee24b0 ntdll!LdrpInitializeGraphRecurse+0x42 0e 0000001a27daf550 00007ffadeee24b0 ntdll!LdrpInitializeGraphRecurse+0xc8 0f 0000001a27daf590 00007ffadef48db2 ntdll!LdrpInitializeGraphRecurse+0xc8 10 (Inline Function) ---------------- ntdll!LdrpInitializeGraph+0x1e 11 0000001a27daf5d0 00007ffadef37af2 ntdll!LdrpInitializeProcess+0x1d26 12 0000001a27daf970 00007ffadeee8f08 ntdll!_LdrpInitialize+0x4ebce 13 0000001a27dafa00 00007ffadeee8eae ntdll!LdrpInitialize+0x40 14 0000001a27dafa30 0000000000000000 ntdll!LdrInitializeThunk+0xe

Here we can see that winsock_init_base::startup is trying to call WSAStartup - but this is being loaded by the C-runtime during a global init - which means WSAStartup is being called while in DllMain. Which means this can randomly deadlock that process on the ntdll LoaderLock :(

We did document this in the WSAStartup documentation. It does make it more difficult if one is a Dll as there would need to be a different way to call WSAStartup (e.g. an InitOnce call the first time it's invoked).

We've been working with a lot of apps over the past 10 years or so to stop doing this as we debugged this many times. Which is why I wrote the Networking provider in Application Verifier.

Thanks.

chriskohlhoff commented 4 years ago

FYI: https://github.com/chriskohlhoff/asio/blob/master/asio/include/asio/detail/winsock_init.hpp#L72

(O for world where WSAStartup was not a thing...)