Convert thread stop handling into a publish/subscribe model

In later commits this will allow providers to subscribe to thread stop
events. We will need this in the FIPS module. We also make thread stop
handling OPENSSL_CTX aware (different OPENSSL_CTXs may have different
thread local data that needs cleaning up).

Reviewed-by: Richard Levitte <levitte@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/9040)
This commit is contained in:
Matt Caswell 2019-05-24 11:45:48 +01:00
parent d4c051cef3
commit 242f84d06a
8 changed files with 64 additions and 75 deletions

View File

@ -30,11 +30,13 @@
static CRYPTO_THREAD_LOCAL ctxkey;
static CRYPTO_THREAD_LOCAL poolkey;
static void async_delete_thread_state(OPENSSL_CTX *ctx);
static async_ctx *async_ctx_new(void)
{
async_ctx *nctx;
if (!ossl_init_thread_start(OPENSSL_INIT_THREAD_ASYNC))
if (!ossl_init_thread_start(NULL, async_delete_thread_state))
return NULL;
nctx = OPENSSL_malloc(sizeof(*nctx));
@ -326,7 +328,7 @@ int ASYNC_init_thread(size_t max_size, size_t init_size)
if (!OPENSSL_init_crypto(OPENSSL_INIT_ASYNC, NULL))
return 0;
if (!ossl_init_thread_start(OPENSSL_INIT_THREAD_ASYNC))
if (!ossl_init_thread_start(NULL, async_delete_thread_state))
return 0;
pool = OPENSSL_zalloc(sizeof(*pool));
@ -374,7 +376,8 @@ err:
return 0;
}
void async_delete_thread_state(void)
/* OPENSSL_CTX ignored for now */
static void async_delete_thread_state(OPENSSL_CTX *ctx)
{
async_pool *pool = (async_pool *)CRYPTO_THREAD_get_local(&poolkey);
@ -393,7 +396,7 @@ void ASYNC_cleanup_thread(void)
if (!OPENSSL_init_crypto(OPENSSL_INIT_ASYNC, NULL))
return;
async_delete_thread_state();
async_delete_thread_state(NULL);
}
ASYNC_JOB *ASYNC_get_current_job(void)

View File

@ -688,7 +688,8 @@ const char *ERR_reason_error_string(unsigned long e)
return ((p == NULL) ? NULL : p->string);
}
void err_delete_thread_state(void)
/* OPENSSL_CTX ignored for now */
static void err_delete_thread_state(OPENSSL_CTX *ctx)
{
ERR_STATE *state = CRYPTO_THREAD_get_local(&err_thread_local);
if (state == NULL)
@ -740,7 +741,7 @@ ERR_STATE *ERR_get_state(void)
return NULL;
}
if (!ossl_init_thread_start(OPENSSL_INIT_THREAD_ERR_STATE)
if (!ossl_init_thread_start(NULL, err_delete_thread_state)
|| !CRYPTO_THREAD_set_local(&err_thread_local, state)) {
ERR_STATE_free(state);
CRYPTO_THREAD_set_local(&err_thread_local, NULL);

View File

@ -11,5 +11,4 @@
int async_init(void);
void async_deinit(void);
void async_delete_thread_state(void);

View File

@ -11,13 +11,9 @@
/* This file is not scanned by mkdef.pl, whereas cryptlib.h is */
struct thread_local_inits_st {
int async;
int err_state;
int rand;
};
int ossl_init_thread_start(uint64_t opts);
typedef void (*ossl_thread_stop_handler_fn)(OPENSSL_CTX *ctx);
int ossl_init_thread_start(OPENSSL_CTX *ctx,
ossl_thread_stop_handler_fn handfn);
/*
* OPENSSL_INIT flags. The primary list of these is in crypto.h. Flags below
@ -27,11 +23,6 @@ int ossl_init_thread_start(uint64_t opts);
# define OPENSSL_INIT_ZLIB 0x00010000L
# define OPENSSL_INIT_BASE_ONLY 0x00040000L
/* OPENSSL_INIT_THREAD flags */
# define OPENSSL_INIT_THREAD_ASYNC 0x01
# define OPENSSL_INIT_THREAD_ERR_STATE 0x02
# define OPENSSL_INIT_THREAD_RAND 0x04
int ossl_trace_init(void);
void ossl_trace_cleanup(void);
void ossl_malloc_setup_failures(void);

View File

@ -12,7 +12,6 @@
int err_load_crypto_strings_int(void);
void err_cleanup(void);
void err_delete_thread_state(void);
int err_shelve_state(void **);
void err_unshelve_state(void *);

View File

@ -24,7 +24,6 @@
typedef struct rand_pool_st RAND_POOL;
void rand_cleanup_int(void);
void drbg_delete_thread_state(void);
void rand_fork(void);
/* Hardware-based seeding functions. */

View File

@ -31,6 +31,13 @@
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
@ -50,30 +57,30 @@ static union {
CRYPTO_THREAD_LOCAL value;
} destructor_key = { -1 };
static void ossl_init_thread_stop(struct thread_local_inits_st *locals);
static void ossl_init_thread_stop(THREAD_EVENT_HANDLER **hands);
static void ossl_init_thread_destructor(void *local)
static void ossl_init_thread_destructor(void *hands)
{
ossl_init_thread_stop((struct thread_local_inits_st *)local);
ossl_init_thread_stop((THREAD_EVENT_HANDLER **)hands);
}
static struct thread_local_inits_st *ossl_init_get_thread_local(int alloc)
static THREAD_EVENT_HANDLER **ossl_init_get_thread_local(int alloc)
{
struct thread_local_inits_st *local =
THREAD_EVENT_HANDLER **hands =
CRYPTO_THREAD_get_local(&destructor_key.value);
if (alloc) {
if (local == NULL
&& (local = OPENSSL_zalloc(sizeof(*local))) != NULL
&& !CRYPTO_THREAD_set_local(&destructor_key.value, local)) {
OPENSSL_free(local);
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 local;
return hands;
}
typedef struct ossl_init_stop_st OPENSSL_INIT_STOP;
@ -417,28 +424,23 @@ DEFINE_RUN_ONCE_STATIC(ossl_init_zlib)
}
#endif
static void ossl_init_thread_stop(struct thread_local_inits_st *locals)
static void ossl_init_thread_stop(THREAD_EVENT_HANDLER **hands)
{
THREAD_EVENT_HANDLER *curr, *prev = NULL;
/* Can't do much about this */
if (locals == NULL)
if (hands == NULL)
return;
if (locals->async) {
OSSL_TRACE(INIT, "async_delete_thread_state()\n");
async_delete_thread_state();
curr = *hands;
while (curr != NULL) {
curr->handfn(curr->ctx);
prev = curr;
curr = curr->next;
OPENSSL_free(prev);
}
if (locals->err_state) {
OSSL_TRACE(INIT, "err_delete_thread_state()\n");
err_delete_thread_state();
}
if (locals->rand) {
OSSL_TRACE(INIT, "drbg_delete_thread_state()\n");
drbg_delete_thread_state();
}
OPENSSL_free(locals);
OPENSSL_free(hands);
}
void OPENSSL_thread_stop(void)
@ -447,38 +449,27 @@ void OPENSSL_thread_stop(void)
ossl_init_thread_stop(ossl_init_get_thread_local(0));
}
int ossl_init_thread_start(uint64_t opts)
int ossl_init_thread_start(OPENSSL_CTX *ctx, ossl_thread_stop_handler_fn handfn)
{
struct thread_local_inits_st *locals;
THREAD_EVENT_HANDLER **hands;
THREAD_EVENT_HANDLER *hand;
if (!OPENSSL_init_crypto(0, NULL))
return 0;
locals = ossl_init_get_thread_local(1);
hands = ossl_init_get_thread_local(1);
if (locals == NULL)
if (hands == NULL)
return 0;
if (opts & OPENSSL_INIT_THREAD_ASYNC) {
OSSL_TRACE(INIT,
"ossl_init_thread_start: "
"marking thread for async\n");
locals->async = 1;
}
hand = OPENSSL_malloc(sizeof(*hand));
if (hand == NULL)
return 0;
if (opts & OPENSSL_INIT_THREAD_ERR_STATE) {
OSSL_TRACE(INIT,
"ossl_init_thread_start: "
"marking thread for err_state\n");
locals->err_state = 1;
}
if (opts & OPENSSL_INIT_THREAD_RAND) {
OSSL_TRACE(INIT,
"ossl_init_thread_start: "
"marking thread for rand\n");
locals->rand = 1;
}
hand->handfn = handfn;
hand->ctx = ctx;
hand->next = *hands;
*hands = hand;
return 1;
}

View File

@ -158,6 +158,14 @@ static void *drbg_ossl_ctx_new(OPENSSL_CTX *libctx)
if (dgbl == NULL)
return NULL;
#ifndef FIPS_MODE
/*
* We need to ensure that base libcrypto thread handling has been
* initialised.
*/
OPENSSL_init_crypto(0, NULL);
#endif
if (!CRYPTO_THREAD_init_local(&dgbl->private_drbg, NULL))
goto err1;
@ -1137,10 +1145,8 @@ err:
return NULL;
}
void drbg_delete_thread_state(void)
static void drbg_delete_thread_state(OPENSSL_CTX *ctx)
{
/* TODO(3.0): Other PRs will pass the ctx as a param to this function */
OPENSSL_CTX *ctx = NULL;
DRBG_GLOBAL *dgbl = drbg_get_global(ctx);
RAND_DRBG *drbg;
@ -1332,7 +1338,7 @@ RAND_DRBG *OPENSSL_CTX_get0_public_drbg(OPENSSL_CTX *ctx)
drbg = CRYPTO_THREAD_get_local(&dgbl->public_drbg);
if (drbg == NULL) {
if (!ossl_init_thread_start(OPENSSL_INIT_THREAD_RAND))
if (!ossl_init_thread_start(NULL, drbg_delete_thread_state))
return NULL;
drbg = drbg_setup(ctx, dgbl->master_drbg, RAND_DRBG_TYPE_PUBLIC);
CRYPTO_THREAD_set_local(&dgbl->public_drbg, drbg);
@ -1359,7 +1365,7 @@ RAND_DRBG *OPENSSL_CTX_get0_private_drbg(OPENSSL_CTX *ctx)
drbg = CRYPTO_THREAD_get_local(&dgbl->private_drbg);
if (drbg == NULL) {
if (!ossl_init_thread_start(OPENSSL_INIT_THREAD_RAND))
if (!ossl_init_thread_start(NULL, drbg_delete_thread_state))
return NULL;
drbg = drbg_setup(ctx, dgbl->master_drbg, RAND_DRBG_TYPE_PRIVATE);
CRYPTO_THREAD_set_local(&dgbl->private_drbg, drbg);