2 * Copyright 1995-2023 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
11 #include "crypto/cryptlib.h"
12 #include "internal/thread_once.h"
14 int ossl_do_ex_data_init(OSSL_LIB_CTX
*ctx
)
16 OSSL_EX_DATA_GLOBAL
*global
= ossl_lib_ctx_get_ex_data_global(ctx
);
21 global
->ex_data_lock
= CRYPTO_THREAD_lock_new();
22 return global
->ex_data_lock
!= NULL
;
26 * Return the EX_CALLBACKS from the |ex_data| array that corresponds to
27 * a given class. On success, *holds the lock.*
28 * The |global| parameter is assumed to be non null (checked by the caller).
29 * If |read| is 1 then a read lock is obtained. Otherwise it is a write lock.
31 static EX_CALLBACKS
*get_and_lock(OSSL_EX_DATA_GLOBAL
*global
, int class_index
,
36 if (class_index
< 0 || class_index
>= CRYPTO_EX_INDEX__COUNT
) {
37 ERR_raise(ERR_LIB_CRYPTO
, ERR_R_PASSED_INVALID_ARGUMENT
);
41 if (global
->ex_data_lock
== NULL
) {
43 * If we get here, someone (who?) cleaned up the lock, so just
44 * treat it as an error.
50 if (!CRYPTO_THREAD_read_lock(global
->ex_data_lock
))
53 if (!CRYPTO_THREAD_write_lock(global
->ex_data_lock
))
57 ip
= &global
->ex_data
[class_index
];
61 static void cleanup_cb(EX_CALLBACK
*funcs
)
67 * Release all "ex_data" state to prevent memory leaks. This can't be made
68 * thread-safe without overhauling a lot of stuff, and shouldn't really be
69 * called under potential race-conditions anyway (it's for program shutdown
72 void ossl_crypto_cleanup_all_ex_data_int(OSSL_LIB_CTX
*ctx
)
75 OSSL_EX_DATA_GLOBAL
*global
= ossl_lib_ctx_get_ex_data_global(ctx
);
80 for (i
= 0; i
< CRYPTO_EX_INDEX__COUNT
; ++i
) {
81 EX_CALLBACKS
*ip
= &global
->ex_data
[i
];
83 sk_EX_CALLBACK_pop_free(ip
->meth
, cleanup_cb
);
87 CRYPTO_THREAD_lock_free(global
->ex_data_lock
);
88 global
->ex_data_lock
= NULL
;
93 * Unregister a new index by replacing the callbacks with no-ops.
94 * Any in-use instances are leaked.
96 static void dummy_new(void *parent
, void *ptr
, CRYPTO_EX_DATA
*ad
, int idx
,
97 long argl
, void *argp
)
101 static void dummy_free(void *parent
, void *ptr
, CRYPTO_EX_DATA
*ad
, int idx
,
102 long argl
, void *argp
)
106 static int dummy_dup(CRYPTO_EX_DATA
*to
, const CRYPTO_EX_DATA
*from
,
107 void **from_d
, int idx
,
108 long argl
, void *argp
)
113 int ossl_crypto_free_ex_index_ex(OSSL_LIB_CTX
*ctx
, int class_index
, int idx
)
118 OSSL_EX_DATA_GLOBAL
*global
= ossl_lib_ctx_get_ex_data_global(ctx
);
123 ip
= get_and_lock(global
, class_index
, 0);
127 if (idx
< 0 || idx
>= sk_EX_CALLBACK_num(ip
->meth
))
129 a
= sk_EX_CALLBACK_value(ip
->meth
, idx
);
132 a
->new_func
= dummy_new
;
133 a
->dup_func
= dummy_dup
;
134 a
->free_func
= dummy_free
;
137 CRYPTO_THREAD_unlock(global
->ex_data_lock
);
141 int CRYPTO_free_ex_index(int class_index
, int idx
)
143 return ossl_crypto_free_ex_index_ex(NULL
, class_index
, idx
);
147 * Register a new index.
149 int ossl_crypto_get_ex_new_index_ex(OSSL_LIB_CTX
*ctx
, int class_index
,
150 long argl
, void *argp
,
151 CRYPTO_EX_new
*new_func
,
152 CRYPTO_EX_dup
*dup_func
,
153 CRYPTO_EX_free
*free_func
,
159 OSSL_EX_DATA_GLOBAL
*global
= ossl_lib_ctx_get_ex_data_global(ctx
);
164 ip
= get_and_lock(global
, class_index
, 0);
168 if (ip
->meth
== NULL
) {
169 ip
->meth
= sk_EX_CALLBACK_new_null();
170 /* We push an initial value on the stack because the SSL
171 * "app_data" routines use ex_data index zero. See RT 3710. */
173 || !sk_EX_CALLBACK_push(ip
->meth
, NULL
)) {
174 sk_EX_CALLBACK_free(ip
->meth
);
176 ERR_raise(ERR_LIB_CRYPTO
, ERR_R_CRYPTO_LIB
);
181 a
= (EX_CALLBACK
*)OPENSSL_malloc(sizeof(*a
));
186 a
->new_func
= new_func
;
187 a
->dup_func
= dup_func
;
188 a
->free_func
= free_func
;
189 a
->priority
= priority
;
191 if (!sk_EX_CALLBACK_push(ip
->meth
, NULL
)) {
192 ERR_raise(ERR_LIB_CRYPTO
, ERR_R_CRYPTO_LIB
);
196 toret
= sk_EX_CALLBACK_num(ip
->meth
) - 1;
197 (void)sk_EX_CALLBACK_set(ip
->meth
, toret
, a
);
200 CRYPTO_THREAD_unlock(global
->ex_data_lock
);
204 int CRYPTO_get_ex_new_index(int class_index
, long argl
, void *argp
,
205 CRYPTO_EX_new
*new_func
, CRYPTO_EX_dup
*dup_func
,
206 CRYPTO_EX_free
*free_func
)
208 return ossl_crypto_get_ex_new_index_ex(NULL
, class_index
, argl
, argp
,
209 new_func
, dup_func
, free_func
, 0);
213 * Initialise a new CRYPTO_EX_DATA for use in a particular class - including
214 * calling new() callbacks for each index in the class used by this variable
215 * Thread-safe by copying a class's array of "EX_CALLBACK" entries
216 * in the lock, then using them outside the lock. Note this only applies
217 * to the global "ex_data" state (ie. class definitions), not 'ad' itself.
219 int ossl_crypto_new_ex_data_ex(OSSL_LIB_CTX
*ctx
, int class_index
, void *obj
,
224 EX_CALLBACK
**storage
= NULL
;
225 EX_CALLBACK
*stack
[10];
227 OSSL_EX_DATA_GLOBAL
*global
= ossl_lib_ctx_get_ex_data_global(ctx
);
232 ip
= get_and_lock(global
, class_index
, 1);
238 mx
= sk_EX_CALLBACK_num(ip
->meth
);
240 if (mx
< (int)OSSL_NELEM(stack
))
243 storage
= OPENSSL_malloc(sizeof(*storage
) * mx
);
245 for (i
= 0; i
< mx
; i
++)
246 storage
[i
] = sk_EX_CALLBACK_value(ip
->meth
, i
);
248 CRYPTO_THREAD_unlock(global
->ex_data_lock
);
250 if (mx
> 0 && storage
== NULL
)
252 for (i
= 0; i
< mx
; i
++) {
253 if (storage
[i
] != NULL
&& storage
[i
]->new_func
!= NULL
) {
254 ptr
= CRYPTO_get_ex_data(ad
, i
);
255 storage
[i
]->new_func(obj
, ptr
, ad
, i
,
256 storage
[i
]->argl
, storage
[i
]->argp
);
259 if (storage
!= stack
)
260 OPENSSL_free(storage
);
264 int CRYPTO_new_ex_data(int class_index
, void *obj
, CRYPTO_EX_DATA
*ad
)
266 return ossl_crypto_new_ex_data_ex(NULL
, class_index
, obj
, ad
);
270 * Duplicate a CRYPTO_EX_DATA variable - including calling dup() callbacks
271 * for each index in the class used by this variable
273 int CRYPTO_dup_ex_data(int class_index
, CRYPTO_EX_DATA
*to
,
274 const CRYPTO_EX_DATA
*from
)
278 EX_CALLBACK
*stack
[10];
279 EX_CALLBACK
**storage
= NULL
;
282 OSSL_EX_DATA_GLOBAL
*global
;
285 if (from
->sk
== NULL
)
286 /* Nothing to copy over */
289 global
= ossl_lib_ctx_get_ex_data_global(from
->ctx
);
293 ip
= get_and_lock(global
, class_index
, 1);
297 mx
= sk_EX_CALLBACK_num(ip
->meth
);
298 j
= sk_void_num(from
->sk
);
302 if (mx
< (int)OSSL_NELEM(stack
))
305 storage
= OPENSSL_malloc(sizeof(*storage
) * mx
);
307 for (i
= 0; i
< mx
; i
++)
308 storage
[i
] = sk_EX_CALLBACK_value(ip
->meth
, i
);
310 CRYPTO_THREAD_unlock(global
->ex_data_lock
);
317 * Make sure the ex_data stack is at least |mx| elements long to avoid
318 * issues in the for loop that follows; so go get the |mx|'th element
319 * (if it does not exist CRYPTO_get_ex_data() returns NULL), and assign
320 * to itself. This is normally a no-op; but ensures the stack is the
323 if (!CRYPTO_set_ex_data(to
, mx
- 1, CRYPTO_get_ex_data(to
, mx
- 1)))
326 for (i
= 0; i
< mx
; i
++) {
327 ptr
= CRYPTO_get_ex_data(from
, i
);
328 if (storage
[i
] != NULL
&& storage
[i
]->dup_func
!= NULL
)
329 if (!storage
[i
]->dup_func(to
, from
, &ptr
, i
,
330 storage
[i
]->argl
, storage
[i
]->argp
))
332 CRYPTO_set_ex_data(to
, i
, ptr
);
336 if (storage
!= stack
)
337 OPENSSL_free(storage
);
341 struct ex_callback_entry
{
342 const EX_CALLBACK
*excb
;
346 static int ex_callback_compare(const void *a
, const void *b
)
348 const struct ex_callback_entry
*ap
= (const struct ex_callback_entry
*)a
;
349 const struct ex_callback_entry
*bp
= (const struct ex_callback_entry
*)b
;
351 if (ap
->excb
== bp
->excb
)
354 if (ap
->excb
== NULL
)
356 if (bp
->excb
== NULL
)
358 if (ap
->excb
->priority
== bp
->excb
->priority
)
360 return ap
->excb
->priority
> bp
->excb
->priority
? -1 : 1;
364 * Cleanup a CRYPTO_EX_DATA variable - including calling free() callbacks for
365 * each index in the class used by this variable
367 void CRYPTO_free_ex_data(int class_index
, void *obj
, CRYPTO_EX_DATA
*ad
)
372 const EX_CALLBACK
*f
;
373 struct ex_callback_entry stack
[10];
374 struct ex_callback_entry
*storage
= NULL
;
375 OSSL_EX_DATA_GLOBAL
*global
= ossl_lib_ctx_get_ex_data_global(ad
->ctx
);
380 ip
= get_and_lock(global
, class_index
, 1);
384 mx
= sk_EX_CALLBACK_num(ip
->meth
);
386 if (mx
< (int)OSSL_NELEM(stack
))
389 storage
= OPENSSL_malloc(sizeof(*storage
) * mx
);
391 for (i
= 0; i
< mx
; i
++) {
392 storage
[i
].excb
= sk_EX_CALLBACK_value(ip
->meth
, i
);
393 storage
[i
].index
= i
;
396 CRYPTO_THREAD_unlock(global
->ex_data_lock
);
398 if (storage
!= NULL
) {
399 /* Sort according to priority. High priority first */
400 qsort(storage
, mx
, sizeof(*storage
), ex_callback_compare
);
401 for (i
= 0; i
< mx
; i
++) {
404 if (f
!= NULL
&& f
->free_func
!= NULL
) {
405 ptr
= CRYPTO_get_ex_data(ad
, storage
[i
].index
);
406 f
->free_func(obj
, ptr
, ad
, storage
[i
].index
, f
->argl
, f
->argp
);
411 if (storage
!= stack
)
412 OPENSSL_free(storage
);
414 sk_void_free(ad
->sk
);
420 * Allocate a given CRYPTO_EX_DATA item using the class specific allocation
423 int CRYPTO_alloc_ex_data(int class_index
, void *obj
, CRYPTO_EX_DATA
*ad
,
428 curval
= CRYPTO_get_ex_data(ad
, idx
);
429 /* Already there, no need to allocate */
433 return ossl_crypto_alloc_ex_data_intern(class_index
, obj
, ad
, idx
);
436 int ossl_crypto_alloc_ex_data_intern(int class_index
, void *obj
,
437 CRYPTO_EX_DATA
*ad
, int idx
)
441 OSSL_EX_DATA_GLOBAL
*global
;
443 global
= ossl_lib_ctx_get_ex_data_global(ad
->ctx
);
447 ip
= get_and_lock(global
, class_index
, 1);
450 f
= sk_EX_CALLBACK_value(ip
->meth
, idx
);
451 CRYPTO_THREAD_unlock(global
->ex_data_lock
);
454 * This should end up calling CRYPTO_set_ex_data(), which allocates
455 * everything necessary to support placing the new data in the right spot.
457 if (f
->new_func
== NULL
)
460 f
->new_func(obj
, NULL
, ad
, idx
, f
->argl
, f
->argp
);
466 * For a given CRYPTO_EX_DATA variable, set the value corresponding to a
467 * particular index in the class used by this variable
469 int CRYPTO_set_ex_data(CRYPTO_EX_DATA
*ad
, int idx
, void *val
)
473 if (ad
->sk
== NULL
) {
474 if ((ad
->sk
= sk_void_new_null()) == NULL
) {
475 ERR_raise(ERR_LIB_CRYPTO
, ERR_R_CRYPTO_LIB
);
480 for (i
= sk_void_num(ad
->sk
); i
<= idx
; ++i
) {
481 if (!sk_void_push(ad
->sk
, NULL
)) {
482 ERR_raise(ERR_LIB_CRYPTO
, ERR_R_CRYPTO_LIB
);
486 if (sk_void_set(ad
->sk
, idx
, val
) != val
) {
487 /* Probably the index is out of bounds */
488 ERR_raise(ERR_LIB_CRYPTO
, ERR_R_PASSED_INVALID_ARGUMENT
);
495 * For a given CRYPTO_EX_DATA_ variable, get the value corresponding to a
496 * particular index in the class used by this variable
498 void *CRYPTO_get_ex_data(const CRYPTO_EX_DATA
*ad
, int idx
)
500 if (ad
->sk
== NULL
|| idx
>= sk_void_num(ad
->sk
))
502 return sk_void_value(ad
->sk
, idx
);
505 OSSL_LIB_CTX
*ossl_crypto_ex_data_get_ossl_lib_ctx(const CRYPTO_EX_DATA
*ad
)