diff options
Diffstat (limited to 'src/mongo/util/net/ssl_manager.cpp')
| -rw-r--r-- | src/mongo/util/net/ssl_manager.cpp | 64 |
1 files changed, 45 insertions, 19 deletions
diff --git a/src/mongo/util/net/ssl_manager.cpp b/src/mongo/util/net/ssl_manager.cpp index 187c0c105e2..4bb0befb4b0 100644 --- a/src/mongo/util/net/ssl_manager.cpp +++ b/src/mongo/util/net/ssl_manager.cpp @@ -37,6 +37,7 @@ #include <boost/thread/thread.hpp> #include <iostream> #include <sstream> +#include <stack> #include <string> #include <vector> @@ -171,21 +172,28 @@ const STACK_OF(X509_EXTENSION) * X509_get0_extensions(const X509* peerCert) { * OpenSSL before version 1.1.0 requires applications provide a callback which emits a thread * identifier. This ID is used to store thread specific ERR information. When a thread is * terminated, it must call ERR_remove_state or ERR_remove_thread_state. These functions may - * themselves invoke the application provided callback. + * themselves invoke the application provided callback. These IDs are stored in a hashtable with + * a questionable hash function. They must be uniformly distributed to prevent collisions. */ class SSLThreadInfo { public: static unsigned long getID() { - enforceCleanupOnShutdown(); + struct ThreadLocalState { + bool firstCall; + unsigned long id; + }; + static MONGO_TRIVIALLY_CONSTRUCTIBLE_THREAD_LOCAL ThreadLocalState state{true, 0}; + if (state.firstCall) { + state.id = _idManager.reserveID(); + unsigned long id = state.id; + boost::this_thread::at_thread_exit([id] { + ERR_remove_state(0); + _idManager.releaseID(id); + }); + state.firstCall = false; + } -#ifdef _WIN32 - return GetCurrentThreadId(); -#else - static_assert(sizeof(void*) == sizeof(unsigned long), - "OpenSSL needs the address of a thread-unique object to be castable to" - "unsigned long"); - return reinterpret_cast<unsigned long>(&errno); -#endif + return state.id; } static void lockingCallback(int mode, int type, const char* file, int line) { @@ -208,21 +216,39 @@ public: private: SSLThreadInfo() = delete; - // When called, ensures that this thread will, on termination, call ERR_remove_state. - static void enforceCleanupOnShutdown() { - static MONGO_TRIVIALLY_CONSTRUCTIBLE_THREAD_LOCAL bool firstCall = true; - if (firstCall) { - boost::this_thread::at_thread_exit([] { ERR_remove_state(0); }); - firstCall = false; - } - } - // Note: see SERVER-8734 for why we are using a recursive mutex here. // Once the deadlock fix in OpenSSL is incorporated into most distros of // Linux, this can be changed back to a nonrecursive mutex. static std::vector<std::unique_ptr<stdx::recursive_mutex>> _mutex; + + class ThreadIDManager { + public: + unsigned long reserveID() { + stdx::unique_lock<stdx::mutex> lock(_idMutex); + if (!_idLast.empty()) { + unsigned long ret = _idLast.top(); + _idLast.pop(); + return ret; + } + return ++_idNext; + } + + void releaseID(unsigned long id) { + stdx::unique_lock<stdx::mutex> lock(_idMutex); + _idLast.push(id); + } + + private: + // Machinery for producing IDs that are unique for the life of a thread. + stdx::mutex _idMutex; // Protects _idNext and _idLast. + unsigned long _idNext = 0; // Stores the next thread ID to use, if none already allocated. + std::stack<unsigned long, std::vector<unsigned long>> + _idLast; // Stores old thread IDs, for reuse. + }; + static ThreadIDManager _idManager; }; std::vector<std::unique_ptr<stdx::recursive_mutex>> SSLThreadInfo::_mutex; +SSLThreadInfo::ThreadIDManager SSLThreadInfo::_idManager; // We only want to free SSL_CTX objects if they have been populated. OpenSSL seems to perform this // check before freeing them, but because it does not document this, we should protect ourselves. |
