2 * Copyright 2019-2021 The OpenSSL Project Authors. All Rights Reserved.
4 * Licensed under the Apache License 2.0 (the "License"). You may not use
5 * this file except in compliance with the License. You can obtain a copy
6 * in the file LICENSE in the source distribution or at
7 * https://www.openssl.org/source/license.html
10 #include <openssl/crypto.h>
11 #include <openssl/core_dispatch.h>
12 #include "crypto/cryptlib.h"
13 #include "prov/providercommon.h"
14 #include "internal/thread_once.h"
17 #include "prov/provider_ctx.h"
20 * Thread aware code may want to be told about thread stop events. We register
21 * to hear about those thread stop events when we see a new thread has started.
22 * We call the ossl_init_thread_start function to do that. In the FIPS provider
23 * we have our own copy of ossl_init_thread_start, which cascades notifications
24 * about threads stopping from libcrypto to all the code in the FIPS provider
25 * that needs to know about it.
27 * The FIPS provider tells libcrypto about which threads it is interested in
28 * by calling "c_thread_start" which is a function pointer created during
29 * provider initialisation (i.e. OSSL_init_provider).
31 extern OSSL_FUNC_core_thread_start_fn
*c_thread_start
;
34 typedef struct thread_event_handler_st THREAD_EVENT_HANDLER
;
35 struct thread_event_handler_st
{
38 OSSL_thread_stop_handler_fn handfn
;
39 THREAD_EVENT_HANDLER
*next
;
43 DEFINE_SPECIAL_STACK_OF(THREAD_EVENT_HANDLER_PTR
, THREAD_EVENT_HANDLER
*)
45 typedef struct global_tevent_register_st GLOBAL_TEVENT_REGISTER
;
46 struct global_tevent_register_st
{
47 STACK_OF(THREAD_EVENT_HANDLER_PTR
) *skhands
;
51 static GLOBAL_TEVENT_REGISTER
*glob_tevent_reg
= NULL
;
53 static CRYPTO_ONCE tevent_register_runonce
= CRYPTO_ONCE_STATIC_INIT
;
55 DEFINE_RUN_ONCE_STATIC(create_global_tevent_register
)
57 glob_tevent_reg
= OPENSSL_zalloc(sizeof(*glob_tevent_reg
));
58 if (glob_tevent_reg
== NULL
)
61 glob_tevent_reg
->skhands
= sk_THREAD_EVENT_HANDLER_PTR_new_null();
62 glob_tevent_reg
->lock
= CRYPTO_THREAD_lock_new();
63 if (glob_tevent_reg
->skhands
== NULL
|| glob_tevent_reg
->lock
== NULL
) {
64 sk_THREAD_EVENT_HANDLER_PTR_free(glob_tevent_reg
->skhands
);
65 CRYPTO_THREAD_lock_free(glob_tevent_reg
->lock
);
66 OPENSSL_free(glob_tevent_reg
);
67 glob_tevent_reg
= NULL
;
74 static GLOBAL_TEVENT_REGISTER
*get_global_tevent_register(void)
76 if (!RUN_ONCE(&tevent_register_runonce
, create_global_tevent_register
))
78 return glob_tevent_reg
;
83 static int init_thread_push_handlers(THREAD_EVENT_HANDLER
**hands
);
84 static void init_thread_remove_handlers(THREAD_EVENT_HANDLER
**handsin
);
85 static void init_thread_destructor(void *hands
);
86 static int init_thread_deregister(void *arg
, int all
);
88 static void init_thread_stop(void *arg
, THREAD_EVENT_HANDLER
**hands
);
90 static THREAD_EVENT_HANDLER
**
91 init_get_thread_local(CRYPTO_THREAD_LOCAL
*local
, int alloc
, int keep
)
93 THREAD_EVENT_HANDLER
**hands
= CRYPTO_THREAD_get_local(local
);
98 if ((hands
= OPENSSL_zalloc(sizeof(*hands
))) == NULL
)
101 if (!CRYPTO_THREAD_set_local(local
, hands
)) {
107 if (!init_thread_push_handlers(hands
)) {
108 CRYPTO_THREAD_set_local(local
, NULL
);
115 CRYPTO_THREAD_set_local(local
, NULL
);
123 * Since per-thread-specific-data destructors are not universally
124 * available, i.e. not on Windows, only below CRYPTO_THREAD_LOCAL key
125 * is assumed to have destructor associated. And then an effort is made
126 * to call this single destructor on non-pthread platform[s].
128 * Initial value is "impossible". It is used as guard value to shortcut
129 * destructor for threads terminating before libcrypto is initialized or
130 * after it's de-initialized. Access to the key doesn't have to be
131 * serialized for the said threads, because they didn't use libcrypto
132 * and it doesn't matter if they pick "impossible" or dereference real
133 * key value and pull NULL past initialization in the first thread that
134 * intends to use libcrypto.
138 CRYPTO_THREAD_LOCAL value
;
139 } destructor_key
= { -1 };
142 * The thread event handler list is a thread specific linked list
143 * of callback functions which are invoked in list order by the
144 * current thread in case of certain events. (Currently, there is
145 * only one type of event, the 'thread stop' event.)
147 * We also keep a global reference to that linked list, so that we
148 * can deregister handlers if necessary before all the threads are
151 static int init_thread_push_handlers(THREAD_EVENT_HANDLER
**hands
)
154 GLOBAL_TEVENT_REGISTER
*gtr
;
156 gtr
= get_global_tevent_register();
160 if (!CRYPTO_THREAD_write_lock(gtr
->lock
))
162 ret
= (sk_THREAD_EVENT_HANDLER_PTR_push(gtr
->skhands
, hands
) != 0);
163 CRYPTO_THREAD_unlock(gtr
->lock
);
168 static void init_thread_remove_handlers(THREAD_EVENT_HANDLER
**handsin
)
170 GLOBAL_TEVENT_REGISTER
*gtr
;
173 gtr
= get_global_tevent_register();
176 if (!CRYPTO_THREAD_write_lock(gtr
->lock
))
178 for (i
= 0; i
< sk_THREAD_EVENT_HANDLER_PTR_num(gtr
->skhands
); i
++) {
179 THREAD_EVENT_HANDLER
**hands
180 = sk_THREAD_EVENT_HANDLER_PTR_value(gtr
->skhands
, i
);
182 if (hands
== handsin
) {
183 sk_THREAD_EVENT_HANDLER_PTR_delete(gtr
->skhands
, i
);
184 CRYPTO_THREAD_unlock(gtr
->lock
);
188 CRYPTO_THREAD_unlock(gtr
->lock
);
192 static void init_thread_destructor(void *hands
)
194 init_thread_stop(NULL
, (THREAD_EVENT_HANDLER
**)hands
);
195 init_thread_remove_handlers(hands
);
199 int ossl_init_thread(void)
201 if (!CRYPTO_THREAD_init_local(&destructor_key
.value
,
202 init_thread_destructor
))
208 void ossl_cleanup_thread(void)
210 init_thread_deregister(NULL
, 1);
211 CRYPTO_THREAD_cleanup_local(&destructor_key
.value
);
212 destructor_key
.sane
= -1;
215 void OPENSSL_thread_stop_ex(OSSL_LIB_CTX
*ctx
)
217 ctx
= ossl_lib_ctx_get_concrete(ctx
);
219 * It would be nice if we could figure out a way to do this on all threads
220 * that have used the OSSL_LIB_CTX when the context is freed. This is
221 * currently not possible due to the use of thread local variables.
223 ossl_ctx_thread_stop(ctx
);
226 void OPENSSL_thread_stop(void)
228 if (destructor_key
.sane
!= -1) {
229 THREAD_EVENT_HANDLER
**hands
230 = init_get_thread_local(&destructor_key
.value
, 0, 0);
231 init_thread_stop(NULL
, hands
);
233 init_thread_remove_handlers(hands
);
238 void ossl_ctx_thread_stop(void *arg
)
240 if (destructor_key
.sane
!= -1) {
241 THREAD_EVENT_HANDLER
**hands
242 = init_get_thread_local(&destructor_key
.value
, 0, 1);
243 init_thread_stop(arg
, hands
);
249 static void *thread_event_ossl_ctx_new(OSSL_LIB_CTX
*libctx
)
251 THREAD_EVENT_HANDLER
**hands
= NULL
;
252 CRYPTO_THREAD_LOCAL
*tlocal
= OPENSSL_zalloc(sizeof(*tlocal
));
257 if (!CRYPTO_THREAD_init_local(tlocal
, NULL
)) {
261 hands
= OPENSSL_zalloc(sizeof(*hands
));
265 if (!CRYPTO_THREAD_set_local(tlocal
, hands
))
271 OPENSSL_free(tlocal
);
275 static void thread_event_ossl_ctx_free(void *tlocal
)
277 OPENSSL_free(tlocal
);
280 static const OSSL_LIB_CTX_METHOD thread_event_ossl_ctx_method
= {
281 thread_event_ossl_ctx_new
,
282 thread_event_ossl_ctx_free
,
285 void ossl_ctx_thread_stop(void *arg
)
287 THREAD_EVENT_HANDLER
**hands
;
288 OSSL_LIB_CTX
*ctx
= PROV_LIBCTX_OF(arg
);
289 CRYPTO_THREAD_LOCAL
*local
290 = ossl_lib_ctx_get_data(ctx
, OSSL_LIB_CTX_THREAD_EVENT_HANDLER_INDEX
,
291 &thread_event_ossl_ctx_method
);
295 hands
= init_get_thread_local(local
, 0, 0);
296 init_thread_stop(ctx
, hands
);
299 #endif /* FIPS_MODULE */
302 static void init_thread_stop(void *arg
, THREAD_EVENT_HANDLER
**hands
)
304 THREAD_EVENT_HANDLER
*curr
, *prev
= NULL
, *tmp
;
306 /* Can't do much about this */
311 while (curr
!= NULL
) {
312 if (arg
!= NULL
&& curr
->arg
!= arg
) {
317 curr
->handfn(curr
->arg
);
321 prev
->next
= curr
->next
;
330 int ossl_init_thread_start(const void *index
, void *arg
,
331 OSSL_thread_stop_handler_fn handfn
)
333 THREAD_EVENT_HANDLER
**hands
;
334 THREAD_EVENT_HANDLER
*hand
;
336 OSSL_LIB_CTX
*ctx
= arg
;
339 * In FIPS mode the list of THREAD_EVENT_HANDLERs is unique per combination
340 * of OSSL_LIB_CTX and thread. This is because in FIPS mode each
341 * OSSL_LIB_CTX gets informed about thread stop events individually.
343 CRYPTO_THREAD_LOCAL
*local
344 = ossl_lib_ctx_get_data(ctx
, OSSL_LIB_CTX_THREAD_EVENT_HANDLER_INDEX
,
345 &thread_event_ossl_ctx_method
);
348 * Outside of FIPS mode the list of THREAD_EVENT_HANDLERs is unique per
349 * thread, but may hold multiple OSSL_LIB_CTXs. We only get told about
350 * thread stop events globally, so we have to ensure all affected
351 * OSSL_LIB_CTXs are informed.
353 CRYPTO_THREAD_LOCAL
*local
= &destructor_key
.value
;
356 hands
= init_get_thread_local(local
, 1, 0);
361 if (*hands
== NULL
) {
363 * We've not yet registered any handlers for this thread. We need to get
364 * libcrypto to tell us about later thread stop events. c_thread_start
365 * is a callback to libcrypto defined in fipsprov.c
367 if (!c_thread_start(FIPS_get_core_handle(ctx
), ossl_ctx_thread_stop
))
372 hand
= OPENSSL_malloc(sizeof(*hand
));
376 hand
->handfn
= handfn
;
386 static int init_thread_deregister(void *index
, int all
)
388 GLOBAL_TEVENT_REGISTER
*gtr
;
391 gtr
= get_global_tevent_register();
395 if (!CRYPTO_THREAD_write_lock(gtr
->lock
))
398 glob_tevent_reg
= NULL
;
400 for (i
= 0; i
< sk_THREAD_EVENT_HANDLER_PTR_num(gtr
->skhands
); i
++) {
401 THREAD_EVENT_HANDLER
**hands
402 = sk_THREAD_EVENT_HANDLER_PTR_value(gtr
->skhands
, i
);
403 THREAD_EVENT_HANDLER
*curr
= NULL
, *prev
= NULL
, *tmp
;
407 CRYPTO_THREAD_unlock(gtr
->lock
);
411 while (curr
!= NULL
) {
412 if (all
|| curr
->index
== index
) {
414 prev
->next
= curr
->next
;
429 CRYPTO_THREAD_lock_free(gtr
->lock
);
430 sk_THREAD_EVENT_HANDLER_PTR_free(gtr
->skhands
);
433 CRYPTO_THREAD_unlock(gtr
->lock
);
438 int ossl_init_thread_deregister(void *index
)
440 return init_thread_deregister(index
, 0);