#429 Static OpenSSLInitializer instance causes Windows deadlocks

Platform_Specific
closed
Peter Schojer
Crypto (16)
5
2012-09-14
2011-03-02
Michael M. Welch
No

Reference the following web page, on which Microsoft states that it is un-thread-safe to invoke any CRT functions inside of static initializers, and that doing so can lead to deadlocks in multi-threaded applications when such initializers are invoked during a DllMain(): http://msdn.microsoft.com/en-us/library/ms682583%28v=vs.85%29.aspx

Poco's Crypto module contains a static instance of the OpenSSLInitializer class. Therefore, the constructor of this static instance is run during a DllMain(). That constructor, on at least one occasion runs code that includes invoking a CRT function and can thus create this deadlock situation. The specific occasion I have observed is:

OpenSSLInitializer::OpenSSLInitializer() -> OpenSSLInitializer::initialize() -> OPENSSL_config() -> CONF_modules_load_file() -> CONF_get1_default_config_file() -> getenv()

getenv() is a CRT function and on Windows, it includes blocking on acquiring the _ENV_LOCK lock. When getenv() is run in a DllMain() context, the running thread of course already has the library load lock at this point. But in my application, there are times when some other thread concurrently already has the _ENV_LOCK and needs to load a library to complete its task, thus needing to acquire the library load lock. Thus, the deadlock. And again, this is the exact type of thing Microsoft warns about on that web page where they say that static initialization code should not be doing anything that will invoke CRT functions.

For all I know, there might be many more such violations over the course of the OpenSSLInitializer constructor. I haven't really looked. This getenv() is the one causing my current deadlock, though.

Discussion

  • By the way, a very similar issue exists in the Net module because of the static instance of NetworkInitializer in DNS.cpp. On Windows, that constructor leads to a call to WSAStartup(), which is again documented by Microsoft as being potentially subject to a similar deadlock.issue as is found with getenv(). In this case, though, the work-around is already in place through the POCO_NET_NO_AUTOMATIC_WSASTARTUP compiler switch. But I don't see anything that documents using that switch to avoid the deadlock problem. The 1.3.6 Release Notes which refer to the addition of that switch seem to indicate is was added for a different problem. If there were some way to document it as a means to avoid the deadlock problem, that might be helpful to some users.

    My solution to the OpenSSLInitializer problem, in fact, was to add a similar compiler switch on which I #ifdef in OpenSSLInitializer.cpp, thus allowing me to re-compile to get rid of static instance therein. That seems to be working.

     
  • fixed in 1.4.2, rev. 1455