From: Nikos Mavrogiannopoulos Date: Sat, 30 Nov 2013 10:50:22 +0000 (+0100) Subject: gnutls_global_init() and gnutls_global_deinit() are thread-safe. X-Git-Tag: gnutls_3_3_0pre0~521 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=18ab6ffcb476062379ff46700edf0aaf56c7e240;p=thirdparty%2Fgnutls.git gnutls_global_init() and gnutls_global_deinit() are thread-safe. They utilize static mutex initializers. --- diff --git a/lib/gnutls_global.c b/lib/gnutls_global.c index 6dbc534dc0..e9ac000832 100644 --- a/lib/gnutls_global.c +++ b/lib/gnutls_global.c @@ -165,6 +165,7 @@ gnutls_global_set_mem_functions(gnutls_alloc_function alloc_func, } +GNUTLS_STATIC_MUTEX(global_init_mutex); static int _gnutls_init = 0; /** @@ -183,21 +184,16 @@ static int _gnutls_init = 0; * function can be called many times, but will only do something the * first time. * - * Note! This function is not thread safe. If two threads call this - * function simultaneously, they can cause a race between checking - * the global counter and incrementing it, causing both threads to - * execute the library initialization code. That could lead to a - * memory leak or even a crash. To handle this, your application should - * invoke this function after aquiring a thread mutex. - * * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, * otherwise a negative error code is returned. **/ int gnutls_global_init(void) { - int result = 0; - int res, level; + int ret = 0, res; + int level; const char* e; + + GNUTLS_STATIC_MUTEX_LOCK(global_init_mutex); if (_gnutls_init++) goto out; @@ -211,15 +207,18 @@ int gnutls_global_init(void) _gnutls_debug_log("Enabled GnuTLS logging...\n"); } - if (gl_sockets_startup(SOCKETS_1_1)) - return gnutls_assert_val(GNUTLS_E_FILE_ERROR); + if (gl_sockets_startup(SOCKETS_1_1)) { + ret = gnutls_assert_val(GNUTLS_E_FILE_ERROR); + goto out; + } bindtextdomain(PACKAGE, LOCALEDIR); - res = gnutls_crypto_init(); - if (res != 0) { + ret = gnutls_crypto_init(); + if (ret < 0) { gnutls_assert(); - return GNUTLS_E_CRYPTO_INIT_FAILED; + ret = GNUTLS_E_CRYPTO_INIT_FAILED; + goto out; } _gnutls_register_accel_crypto(); @@ -234,43 +233,44 @@ int gnutls_global_init(void) ("Checking for libtasn1 failed: %s < %s\n", asn1_check_version(NULL), GNUTLS_MIN_LIBTASN1_VERSION); - return GNUTLS_E_INCOMPATIBLE_LIBTASN1_LIBRARY; + ret = GNUTLS_E_INCOMPATIBLE_LIBTASN1_LIBRARY; + goto out; } res = asn1_array2tree(pkix_asn1_tab, &_gnutls_pkix1_asn, NULL); if (res != ASN1_SUCCESS) { - result = _gnutls_asn2err(res); + ret = _gnutls_asn2err(res); goto out; } res = asn1_array2tree(gnutls_asn1_tab, &_gnutls_gnutls_asn, NULL); if (res != ASN1_SUCCESS) { - result = _gnutls_asn2err(res); + ret = _gnutls_asn2err(res); goto out; } /* Initialize the random generator */ - result = _gnutls_rnd_init(); - if (result < 0) { + ret = _gnutls_rnd_init(); + if (ret < 0) { gnutls_assert(); goto out; } /* Initialize the default TLS extensions */ - result = _gnutls_ext_init(); - if (result < 0) { + ret = _gnutls_ext_init(); + if (ret < 0) { gnutls_assert(); goto out; } - result = gnutls_mutex_init(&_gnutls_file_mutex); - if (result < 0) { + ret = gnutls_mutex_init(&_gnutls_file_mutex); + if (ret < 0) { gnutls_assert(); goto out; } - result = gnutls_system_global_init(); - if (result < 0) { + ret = gnutls_system_global_init(); + if (ret < 0) { gnutls_assert(); goto out; } @@ -281,7 +281,8 @@ int gnutls_global_init(void) _gnutls_cryptodev_init(); out: - return result; + GNUTLS_STATIC_MUTEX_UNLOCK(global_init_mutex); + return ret; } /** @@ -290,12 +291,13 @@ int gnutls_global_init(void) * This function deinitializes the global data, that were initialized * using gnutls_global_init(). * - * Note! This function is not thread safe. See the discussion for - * gnutls_global_init() for more information. **/ void gnutls_global_deinit(void) { + GNUTLS_STATIC_MUTEX_LOCK(global_init_mutex); if (_gnutls_init == 1) { + _gnutls_init = 0; + GNUTLS_STATIC_MUTEX_UNLOCK(global_init_mutex); gl_sockets_cleanup(); gnutls_crypto_deinit(); _gnutls_rnd_deinit(); @@ -309,15 +311,14 @@ void gnutls_global_deinit(void) gnutls_pkcs11_deinit(); #endif gnutls_mutex_deinit(&_gnutls_file_mutex); + GNUTLS_STATIC_MUTEX_DEINIT(global_init_mutex); + } else { + if (_gnutls_init > 0) + _gnutls_init--; + GNUTLS_STATIC_MUTEX_UNLOCK(global_init_mutex); } - _gnutls_init--; } -/* These functions should be elsewere. Kept here for - * historical reasons. - */ - - /** * gnutls_check_version: * @req_version: version string to compare with, or %NULL. diff --git a/lib/locks.h b/lib/locks.h index ba190a1fcd..68d73e4ae9 100644 --- a/lib/locks.h +++ b/lib/locks.h @@ -25,10 +25,59 @@ #include #include +#include extern mutex_init_func gnutls_mutex_init; extern mutex_deinit_func gnutls_mutex_deinit; extern mutex_lock_func gnutls_mutex_lock; extern mutex_unlock_func gnutls_mutex_unlock; +#if defined(HAVE_WIN32_LOCKS) +# include + +/* Idea based based on comment 2 at: + * http://stackoverflow.com/questions/3555859/is-it-possible-to-do-static-initialization-of-mutexes-in-windows + */ +# define GNUTLS_STATIC_MUTEX(mutex) \ + static CRITICAL_SECTION *mutex = NULL + +# define GNUTLS_STATIC_MUTEX_LOCK(mutex) \ + if (mutex == NULL) { \ + CRITICAL_SECTION *mutex##tmp = malloc(sizeof(CRITICAL_SECTION)); \ + InitializeCriticalSection(mutex##tmp); \ + if (InterlockedCompareExchangePointer((PVOID*)&mutex, (PVOID)mutex##tmp, NULL) != NULL) { \ + DeleteCriticalSection(mutex##tmp); \ + free(mutex##tmp); \ + } \ + } \ + EnterCriticalSection(mutex) + +# define GNUTLS_STATIC_MUTEX_UNLOCK(mutex) \ + LeaveCriticalSection(mutex) + +# define GNUTLS_STATIC_MUTEX_DEINIT(mutex) \ + DeleteCriticalSection(mutex); \ + free(mutex); mutex = NULL + +#elif defined(HAVE_PTHREAD_LOCKS) +# include +# define GNUTLS_STATIC_MUTEX(mutex) \ + static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER + +# define GNUTLS_STATIC_MUTEX_LOCK(mutex) \ + pthread_mutex_lock(&mutex) + +# define GNUTLS_STATIC_MUTEX_UNLOCK(mutex) \ + pthread_mutex_unlock(&mutex) + +# define GNUTLS_STATIC_MUTEX_DEINIT(mutex) \ + pthread_mutex_destroy(&mutex) + +#else +# define GNUTLS_STATIC_MUTEX(mutex) +# define GNUTLS_STATIC_MUTEX_LOCK(mutex) +# define GNUTLS_STATIC_MUTEX_UNLOCK(mutex) +# define GNUTLS_STATIC_MUTEX_DEINIT(mutex) +#endif + #endif