diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native/openssl.c b/src/libraries/Native/Unix/System.Security.Cryptography.Native/openssl.c index 1a9ea04839756..21820f8f443a1 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native/openssl.c +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native/openssl.c @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#include "pal_err.h" #include "pal_types.h" #include "pal_utilities.h" #include "pal_safecrt.h" @@ -1263,6 +1264,23 @@ static int32_t EnsureOpenSsl10Initialized() #define OPENSSL_INIT_NO_ATEXIT 0x00080000L #endif +pthread_mutex_t g_err_mutex = PTHREAD_MUTEX_INITIALIZER; +int volatile g_err_unloaded = 0; + +static void HandleShutdown() +{ + // Generally, a mutex to set a boolean is overkill, but this lock + // ensures that there are no callers already inside the string table + // when the unload (possibly) executes. + int result = pthread_mutex_lock(&g_err_mutex); + assert(!result && "Acquiring the error string table mutex failed."); + + g_err_unloaded = 1; + + result = pthread_mutex_unlock(&g_err_mutex); + assert(!result && "Releasing the error string table mutex failed."); +} + static int32_t EnsureOpenSsl11Initialized() { // In OpenSSL 1.0 we call OPENSSL_add_all_algorithms_conf() and ERR_load_crypto_strings(), @@ -1279,6 +1297,10 @@ static int32_t EnsureOpenSsl11Initialized() OPENSSL_INIT_LOAD_SSL_STRINGS, NULL); + // As a fallback for when the NO_ATEXIT isn't respected, register a later + // atexit handler, so we will indicate that we're in the shutdown state + // and stop asking problematic questions from other threads. + atexit(HandleShutdown); return 0; } diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native/pal_err.c b/src/libraries/Native/Unix/System.Security.Cryptography.Native/pal_err.c index 252e7be59ce29..e38aaf71f7aec 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native/pal_err.c +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native/pal_err.c @@ -34,10 +34,48 @@ uint64_t CryptoNative_ErrPeekLastError() const char* CryptoNative_ErrReasonErrorString(uint64_t error) { - return ERR_reason_error_string((unsigned long)error); + const char* errStr = NULL; + +#ifdef NEED_OPENSSL_1_1 + int result = pthread_mutex_lock(&g_err_mutex); + assert(!result && "Acquiring the error string table mutex failed."); + + if (!g_err_unloaded) + { +#endif + errStr = ERR_reason_error_string((unsigned long)error); +#ifdef NEED_OPENSSL_1_1 + } + + result = pthread_mutex_unlock(&g_err_mutex); + assert(!result && "Releasing the error string table mutex failed."); +#endif + + return errStr; } void CryptoNative_ErrErrorStringN(uint64_t e, char* buf, int32_t len) { - ERR_error_string_n((unsigned long)e, buf, Int32ToSizeT(len)); +#ifdef NEED_OPENSSL_1_1 + int result = pthread_mutex_lock(&g_err_mutex); + assert(!result && "Acquiring the error string table mutex failed."); + + if (!g_err_unloaded) + { +#endif + ERR_error_string_n((unsigned long)e, buf, Int32ToSizeT(len)); +#ifdef NEED_OPENSSL_1_1 + } + else + { + // If there's no string table, just make it be the empty string. + if (buf != NULL && len > 0) + { + buf[0] = 0; + } + } + + result = pthread_mutex_unlock(&g_err_mutex); + assert(!result && "Releasing the error string table mutex failed."); +#endif } diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native/pal_err.h b/src/libraries/Native/Unix/System.Security.Cryptography.Native/pal_err.h index 3610c263d0af1..1c7470251e411 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native/pal_err.h +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native/pal_err.h @@ -6,6 +6,13 @@ #include #include "opensslshim.h" +#include + +#ifdef NEED_OPENSSL_1_1 +extern pthread_mutex_t g_err_mutex; +extern int volatile g_err_unloaded; +#endif + /* Shims the ERR_clear_error method. */