diff --git a/crypto/include/internal/cryptlib_int.h b/crypto/include/internal/cryptlib_int.h index b3529dcaf9..e810ef02eb 100644 --- a/crypto/include/internal/cryptlib_int.h +++ b/crypto/include/internal/cryptlib_int.h @@ -14,6 +14,8 @@ typedef void (*ossl_thread_stop_handler_fn)(OPENSSL_CTX *ctx); int ossl_init_thread_start(OPENSSL_CTX *ctx, ossl_thread_stop_handler_fn handfn); +int init_thread(void); +void cleanup_thread(void); /* * OPENSSL_INIT flags. The primary list of these is in crypto.h. Flags below diff --git a/crypto/init.c b/crypto/init.c index 7512a1e9d6..fd24e4f50b 100644 --- a/crypto/init.c +++ b/crypto/init.c @@ -31,58 +31,6 @@ static int stopped = 0; -typedef struct thread_event_handler_st THREAD_EVENT_HANDLER; -struct thread_event_handler_st { - OPENSSL_CTX *ctx; - ossl_thread_stop_handler_fn handfn; - THREAD_EVENT_HANDLER *next; -}; - -/* - * Since per-thread-specific-data destructors are not universally - * available, i.e. not on Windows, only below CRYPTO_THREAD_LOCAL key - * is assumed to have destructor associated. And then an effort is made - * to call this single destructor on non-pthread platform[s]. - * - * Initial value is "impossible". It is used as guard value to shortcut - * destructor for threads terminating before libcrypto is initialized or - * after it's de-initialized. Access to the key doesn't have to be - * serialized for the said threads, because they didn't use libcrypto - * and it doesn't matter if they pick "impossible" or derefernce real - * key value and pull NULL past initialization in the first thread that - * intends to use libcrypto. - */ -static union { - long sane; - CRYPTO_THREAD_LOCAL value; -} destructor_key = { -1 }; - -static void ossl_init_thread_stop(THREAD_EVENT_HANDLER **hands); - -static void ossl_init_thread_destructor(void *hands) -{ - ossl_init_thread_stop((THREAD_EVENT_HANDLER **)hands); -} - -static THREAD_EVENT_HANDLER **ossl_init_get_thread_local(int alloc) -{ - THREAD_EVENT_HANDLER **hands = - CRYPTO_THREAD_get_local(&destructor_key.value); - - if (alloc) { - if (hands == NULL - && (hands = OPENSSL_zalloc(sizeof(*hands))) != NULL - && !CRYPTO_THREAD_set_local(&destructor_key.value, hands)) { - OPENSSL_free(hands); - return NULL; - } - } else { - CRYPTO_THREAD_set_local(&destructor_key.value, NULL); - } - - return hands; -} - typedef struct ossl_init_stop_st OPENSSL_INIT_STOP; struct ossl_init_stop_st { void (*handler)(void); @@ -96,8 +44,6 @@ static CRYPTO_ONCE base = CRYPTO_ONCE_STATIC_INIT; static int base_inited = 0; DEFINE_RUN_ONCE_STATIC(ossl_init_base) { - CRYPTO_THREAD_LOCAL key; - if (ossl_trace_init() == 0) return 0; @@ -105,13 +51,14 @@ DEFINE_RUN_ONCE_STATIC(ossl_init_base) #ifndef OPENSSL_NO_CRYPTO_MDEBUG ossl_malloc_setup_failures(); #endif - if (!CRYPTO_THREAD_init_local(&key, ossl_init_thread_destructor)) - return 0; + if ((init_lock = CRYPTO_THREAD_lock_new()) == NULL) goto err; OPENSSL_cpuid_setup(); - destructor_key.value = key; + if (!init_thread()) + return 0; + base_inited = 1; return 1; @@ -120,7 +67,6 @@ err: CRYPTO_THREAD_lock_free(init_lock); init_lock = NULL; - CRYPTO_THREAD_cleanup_local(&key); return 0; } @@ -424,60 +370,9 @@ DEFINE_RUN_ONCE_STATIC(ossl_init_zlib) } #endif -static void ossl_init_thread_stop(THREAD_EVENT_HANDLER **hands) -{ - THREAD_EVENT_HANDLER *curr, *prev = NULL; - - /* Can't do much about this */ - if (hands == NULL) - return; - - curr = *hands; - while (curr != NULL) { - curr->handfn(curr->ctx); - prev = curr; - curr = curr->next; - OPENSSL_free(prev); - } - - OPENSSL_free(hands); -} - -void OPENSSL_thread_stop(void) -{ - if (destructor_key.sane != -1) - ossl_init_thread_stop(ossl_init_get_thread_local(0)); -} - -int ossl_init_thread_start(OPENSSL_CTX *ctx, ossl_thread_stop_handler_fn handfn) -{ - THREAD_EVENT_HANDLER **hands; - THREAD_EVENT_HANDLER *hand; - - if (!OPENSSL_init_crypto(0, NULL)) - return 0; - - hands = ossl_init_get_thread_local(1); - - if (hands == NULL) - return 0; - - hand = OPENSSL_malloc(sizeof(*hand)); - if (hand == NULL) - return 0; - - hand->handfn = handfn; - hand->ctx = ctx; - hand->next = *hands; - *hands = hand; - - return 1; -} - void OPENSSL_cleanup(void) { OPENSSL_INIT_STOP *currhandler, *lasthandler; - CRYPTO_THREAD_LOCAL key; /* * TODO(3.0): This function needs looking at with a view to moving most/all @@ -497,7 +392,7 @@ void OPENSSL_cleanup(void) * Thread stop may not get automatically called by the thread library for * the very last thread in some situations, so call it directly. */ - ossl_init_thread_stop(ossl_init_get_thread_local(0)); + OPENSSL_thread_stop(); currhandler = stop_handlers; while (currhandler != NULL) { @@ -533,9 +428,7 @@ void OPENSSL_cleanup(void) err_free_strings_int(); } - key = destructor_key.value; - destructor_key.sane = -1; - CRYPTO_THREAD_cleanup_local(&key); + cleanup_thread(); /* * Note that cleanup order is important: diff --git a/crypto/initthread.c b/crypto/initthread.c new file mode 100644 index 0000000000..6e56d47338 --- /dev/null +++ b/crypto/initthread.c @@ -0,0 +1,129 @@ +/* + * Copyright 2019 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include "internal/cryptlib_int.h" + +typedef struct thread_event_handler_st THREAD_EVENT_HANDLER; +struct thread_event_handler_st { + OPENSSL_CTX *ctx; + ossl_thread_stop_handler_fn handfn; + THREAD_EVENT_HANDLER *next; +}; + +/* + * Since per-thread-specific-data destructors are not universally + * available, i.e. not on Windows, only below CRYPTO_THREAD_LOCAL key + * is assumed to have destructor associated. And then an effort is made + * to call this single destructor on non-pthread platform[s]. + * + * Initial value is "impossible". It is used as guard value to shortcut + * destructor for threads terminating before libcrypto is initialized or + * after it's de-initialized. Access to the key doesn't have to be + * serialized for the said threads, because they didn't use libcrypto + * and it doesn't matter if they pick "impossible" or derefernce real + * key value and pull NULL past initialization in the first thread that + * intends to use libcrypto. + */ +static union { + long sane; + CRYPTO_THREAD_LOCAL value; +} destructor_key = { -1 }; + +static void ossl_init_thread_stop(THREAD_EVENT_HANDLER **hands); + +static void ossl_init_thread_destructor(void *hands) +{ + ossl_init_thread_stop((THREAD_EVENT_HANDLER **)hands); +} + +int init_thread(void) +{ + + if (!CRYPTO_THREAD_init_local(&destructor_key.value, + ossl_init_thread_destructor)) + return 0; + + return 1; +} + +void cleanup_thread(void) +{ + CRYPTO_THREAD_cleanup_local(&destructor_key.value); + destructor_key.sane = -1; +} + +static THREAD_EVENT_HANDLER **ossl_init_get_thread_local(int alloc) +{ + THREAD_EVENT_HANDLER **hands = + CRYPTO_THREAD_get_local(&destructor_key.value); + + if (alloc) { + if (hands == NULL + && (hands = OPENSSL_zalloc(sizeof(*hands))) != NULL + && !CRYPTO_THREAD_set_local(&destructor_key.value, hands)) { + OPENSSL_free(hands); + return NULL; + } + } else { + CRYPTO_THREAD_set_local(&destructor_key.value, NULL); + } + + return hands; +} + +static void ossl_init_thread_stop(THREAD_EVENT_HANDLER **hands) +{ + THREAD_EVENT_HANDLER *curr, *prev = NULL; + + /* Can't do much about this */ + if (hands == NULL) + return; + + curr = *hands; + while (curr != NULL) { + curr->handfn(curr->ctx); + prev = curr; + curr = curr->next; + OPENSSL_free(prev); + } + + OPENSSL_free(hands); +} + +void OPENSSL_thread_stop(void) +{ + if (destructor_key.sane != -1) + ossl_init_thread_stop(ossl_init_get_thread_local(0)); +} + +int ossl_init_thread_start(OPENSSL_CTX *ctx, ossl_thread_stop_handler_fn handfn) +{ + THREAD_EVENT_HANDLER **hands; + THREAD_EVENT_HANDLER *hand; + + if (!OPENSSL_init_crypto(0, NULL)) + return 0; + + hands = ossl_init_get_thread_local(1); + + if (hands == NULL) + return 0; + + hand = OPENSSL_malloc(sizeof(*hand)); + if (hand == NULL) + return 0; + + hand->handfn = handfn; + hand->ctx = ctx; + hand->next = *hands; + *hands = hand; + + return 1; +}