/src/openssl/crypto/threads_common.c
Line  | Count  | Source  | 
1  |  | /*  | 
2  |  |  * Copyright 2025 The OpenSSL Project Authors. All Rights Reserved.  | 
3  |  |  *  | 
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  | 
8  |  |  */  | 
9  |  |  | 
10  |  | /**  | 
11  |  |  * @file  | 
12  |  |  * @brief Thread-local context-specific data management for OpenSSL  | 
13  |  |  *  | 
14  |  |  * This file implements a mechanism to store and retrieve context-specific  | 
15  |  |  * data using OpenSSL's thread-local storage (TLS) system. It provides a way  | 
16  |  |  * to associate and manage data based on a combination of a thread-local key  | 
17  |  |  * and an `OSSL_LIB_CTX *` context.  | 
18  |  |  *  | 
19  |  |  * NOTE: This differs from the CRYPTO_THREAD_[get|set]_local api set in that  | 
20  |  |  * this api stores a single OS level thread-local key per-process, and manages  | 
21  |  |  * subsequent keys using a series of arrays and sparse arrays stored against  | 
22  |  |  * that aforementioned thread local key  | 
23  |  |  *  | 
24  |  |  * Data Design:  | 
25  |  |  *  | 
26  |  |  * per-thread master key data  -> +--------------+-+  | 
27  |  |  *                                |              | |  | 
28  |  |  *                                |              | |  | 
29  |  |  *                                +--------------+ |  | 
30  |  |  *                                +--------------+ |  | 
31  |  |  *                                |              | |  | 
32  |  |  *                                |              | |  | 
33  |  |  *                                +--------------+ |  fixed  array indexed  | 
34  |  |  *                                       .         |      by key id  | 
35  |  |  *                                       .         |  | 
36  |  |  *                                       .         |  | 
37  |  |  *                                +--------------+ |  | 
38  |  |  *                                |              | |  | 
39  |  |  *                                |       |      | |  | 
40  |  |  *                                +-------+------+-+  | 
41  |  |  *                                        |  | 
42  |  |  *           ++---------------+           |  | 
43  |  |  *           ||               |<----------+  | 
44  |  |  *           ||               |  | 
45  |  |  *           |+---------------+  | 
46  |  |  *           |+---------------+  | 
47  |  |  *           ||               |  | 
48  |  |  *           ||               |  sparse array indexed  | 
49  |  |  *           |+---------------+     by libctx pointer  | 
50  |  |  *           |        .              cast to uintptr_t  | 
51  |  |  *           |        .  | 
52  |  |  *           |        .  | 
53  |  |  *           |+---------------+  | 
54  |  |  *           ||        +------+----> +-----------------+  | 
55  |  |  *           ||        |      |      |                 |  | 
56  |  |  *           ++--------+------+      +-----------------+  | 
57  |  |  *                                  per-<thread*ctx> data  | 
58  |  |  *  | 
59  |  |  * It uses the following lookup pattern:  | 
60  |  |  *   1) A global os defined key to a per-thread fixed array  | 
61  |  |  *   2) A libcrypto defined key id as an index to (1) to get a sparse array  | 
62  |  |  *   3) A Library context pointer as an index to (2) to produce a per  | 
63  |  |  *      thread*context data pointer  | 
64  |  |  *  | 
65  |  |  * Two primary functions are provided:  | 
66  |  |  *   - CRYPTO_THREAD_get_local_ex() retrieves data associated with a key and  | 
67  |  |  *     context.  | 
68  |  |  *   - CRYPTO_THREAD_set_local_ex() associates data with a given key and  | 
69  |  |  *     context, allocating tables as needed.  | 
70  |  |  *  | 
71  |  |  * Internal structures:  | 
72  |  |  *   - CTX_TABLE_ENTRY: wraps a context-specific data pointer.  | 
73  |  |  *   - MASTER_KEY_ENTRY: maintains a table of CTX_TABLE_ENTRY and an optional  | 
74  |  |  *     cleanup function.  | 
75  |  |  *  | 
76  |  |  * The implementation ensures:  | 
77  |  |  *   - Lazy initialization of master key data using CRYPTO_ONCE.  | 
78  |  |  *   - Automatic cleanup of all context and key mappings when a thread exits.  | 
79  |  |  *  | 
80  |  |  * Cleanup routines:  | 
81  |  |  *   - clean_ctx_entry: releases context-specific entries.  | 
82  |  |  *   - clean_master_key_id: releases all entries for a specific key ID.  | 
83  |  |  *   - clean_master_key: top-level cleanup for the thread-local master key.  | 
84  |  |  *  | 
85  |  |  */  | 
86  |  |  | 
87  |  | #include <openssl/crypto.h>  | 
88  |  | #include <crypto/cryptlib.h>  | 
89  |  | #include <crypto/sparse_array.h>  | 
90  |  | #include "internal/cryptlib.h"  | 
91  |  | #include "internal/threads_common.h"  | 
92  |  |  | 
93  |  | /**  | 
94  |  |  * @struct CTX_TABLE_ENTRY  | 
95  |  |  * @brief Represents a wrapper for context-specific data.  | 
96  |  |  *  | 
97  |  |  * This structure is used to hold a pointer to data that is associated  | 
98  |  |  * with a particular `OSSL_LIB_CTX` instance in a thread-local context  | 
99  |  |  * mapping. It is stored within a sparse array, allowing efficient  | 
100  |  |  * per-context data lookup keyed by a context identifier.  | 
101  |  |  *  | 
102  |  |  * @var CTX_TABLE_ENTRY::ctx_data  | 
103  |  |  * Pointer to the data associated with a given library context.  | 
104  |  |  */  | 
105  |  | typedef void *CTX_TABLE_ENTRY;  | 
106  |  |  | 
107  |  | /*  | 
108  |  |  * define our sparse array of CTX_TABLE_ENTRY functions  | 
109  |  |  */  | 
110  |  | DEFINE_SPARSE_ARRAY_OF(CTX_TABLE_ENTRY);  | 
111  |  |  | 
112  |  | /**  | 
113  |  |  * @struct MASTER_KEY_ENTRY  | 
114  |  |  * @brief Represents a mapping of context-specific data for a TLS key ID.  | 
115  |  |  *  | 
116  |  |  * This structure manages a collection of `CTX_TABLE_ENTRY` items, each  | 
117  |  |  * associated with a different `OSSL_LIB_CTX` instance. It supports  | 
118  |  |  * cleanup of stored data when the thread or key is being destroyed.  | 
119  |  |  *  | 
120  |  |  * @var MASTER_KEY_ENTRY::ctx_table  | 
121  |  |  * Sparse array mapping `OSSL_LIB_CTX` pointers (cast to uintptr_t) to  | 
122  |  |  * `CTX_TABLE_ENTRY` structures that hold context-specific data.  | 
123  |  |  *  | 
124  |  |  */  | 
125  |  | typedef struct master_key_entry { | 
126  |  |     SPARSE_ARRAY_OF(CTX_TABLE_ENTRY) *ctx_table;  | 
127  |  | } MASTER_KEY_ENTRY;  | 
128  |  |  | 
129  |  | /**  | 
130  |  |  * @brief holds our per thread data with the operating system  | 
131  |  |  *  | 
132  |  |  * Global thread local storage pointer, used to create a platform  | 
133  |  |  * specific thread-local key  | 
134  |  |  */  | 
135  |  | static CRYPTO_THREAD_LOCAL master_key;  | 
136  |  |  | 
137  |  | /**  | 
138  |  |  * @brief Informs the library if the master key has been set up  | 
139  |  |  *  | 
140  |  |  * State variable to track if we have initialized the master_key  | 
141  |  |  * If this isn't set to 1, then we need to skip any cleanup  | 
142  |  |  * in CRYPTO_THREAD_clean_for_fips, as the uninitialized key  | 
143  |  |  * will return garbage data  | 
144  |  |  */  | 
145  |  | static uint8_t master_key_init = 0;  | 
146  |  |  | 
147  |  | /**  | 
148  |  |  * @brief gate variable to do one time init of the master key  | 
149  |  |  *  | 
150  |  |  * Run once gate for doing one-time initialization  | 
151  |  |  */  | 
152  |  | static CRYPTO_ONCE master_once = CRYPTO_ONCE_STATIC_INIT;  | 
153  |  |  | 
154  |  | /**  | 
155  |  |  * @brief Cleans up all context-specific entries for a given key ID.  | 
156  |  |  *  | 
157  |  |  * This function is used to release all context data associated with a  | 
158  |  |  * specific thread-local key (identified by `idx`). It iterates over the  | 
159  |  |  * context table in the given `MASTER_KEY_ENTRY`, invoking cleanup for each  | 
160  |  |  * `CTX_TABLE_ENTRY`, then frees the context table and the entry itself.  | 
161  |  |  *  | 
162  |  |  * @param idx  | 
163  |  |  *        The key ID associated with the `MASTER_KEY_ENTRY`. Unused.  | 
164  |  |  *  | 
165  |  |  * @param entry  | 
166  |  |  *        Pointer to the `MASTER_KEY_ENTRY` containing the context table  | 
167  |  |  *        to be cleaned up.  | 
168  |  |  *  | 
169  |  |  * @param arg  | 
170  |  |  *        Unused parameter.  | 
171  |  |  */  | 
172  |  | static void clean_master_key_id(MASTER_KEY_ENTRY *entry)  | 
173  | 48  | { | 
174  | 48  |     ossl_sa_CTX_TABLE_ENTRY_free(entry->ctx_table);  | 
175  | 48  | }  | 
176  |  |  | 
177  |  | /**  | 
178  |  |  * @brief Cleans up all master key entries for the current thread.  | 
179  |  |  *  | 
180  |  |  * This function is the top-level cleanup routine for the thread-local  | 
181  |  |  * storage associated with OpenSSL master keys. It is typically registered  | 
182  |  |  * as the thread-local storage destructor. It iterates over all  | 
183  |  |  * `MASTER_KEY_ENTRY` items in the sparse array, releasing associated  | 
184  |  |  * context data and structures.  | 
185  |  |  *  | 
186  |  |  * @param data  | 
187  |  |  *        Pointer to the thread-local `SPARSE_ARRAY_OF(MASTER_KEY_ENTRY)`  | 
188  |  |  *        structure to be cleaned up.  | 
189  |  |  */  | 
190  |  | static void clean_master_key(void *data)  | 
191  | 16  | { | 
192  | 16  |     MASTER_KEY_ENTRY *mkey = data;  | 
193  | 16  |     int i;  | 
194  |  |  | 
195  | 16  |     if (data == NULL)  | 
196  | 0  |         return;  | 
197  |  |  | 
198  | 144  |     for (i = 0; i < CRYPTO_THREAD_LOCAL_KEY_MAX; i++) { | 
199  | 128  |         if (mkey[i].ctx_table != NULL)  | 
200  | 48  |             clean_master_key_id(&mkey[i]);  | 
201  | 128  |     }  | 
202  | 16  |     OPENSSL_free(mkey);  | 
203  | 16  | }  | 
204  |  |  | 
205  |  | /**  | 
206  |  |  * @brief Initializes the thread-local storage for master key data.  | 
207  |  |  *  | 
208  |  |  * This function sets up the thread-local key used to store per-thread  | 
209  |  |  * master key tables. It also registers the `clean_master_key` function  | 
210  |  |  * as the destructor to be called when the thread exits.  | 
211  |  |  *  | 
212  |  |  * This function is intended to be called once using `CRYPTO_THREAD_run_once`  | 
213  |  |  * to ensure thread-safe initialization.  | 
214  |  |  */  | 
215  |  | static void init_master_key(void)  | 
216  | 16  | { | 
217  |  |     /*  | 
218  |  |      * Note: We assign a cleanup function here, which is atypical for  | 
219  |  |      * uses of CRYPTO_THREAD_init_local.  This is because, nominally  | 
220  |  |      * we expect that the use of ossl_init_thread_start will be used  | 
221  |  |      * to notify openssl of exiting threads.  However, in this case  | 
222  |  |      * we want the metadata for this interface (the sparse arrays) to  | 
223  |  |      * stay valid until the thread actually exits, which is what the  | 
224  |  |      * clean_master_key function does.  Data held in the sparse arrays  | 
225  |  |      * (that is assigned via CRYPTO_THREAD_set_local_ex), are still expected  | 
226  |  |      * to be cleaned via the ossl_init_thread_start/stop api.  | 
227  |  |      */  | 
228  | 16  |     if (!CRYPTO_THREAD_init_local(&master_key, clean_master_key))  | 
229  | 0  |         return;  | 
230  |  |  | 
231  |  |     /*  | 
232  |  |      * Indicate that the key has been set up.  | 
233  |  |      */  | 
234  | 16  |     master_key_init = 1;  | 
235  | 16  | }  | 
236  |  |  | 
237  |  | /**  | 
238  |  |  * @brief Retrieves context-specific data from thread-local storage.  | 
239  |  |  *  | 
240  |  |  * This function looks up and returns the data associated with a given  | 
241  |  |  * thread-local key ID and `OSSL_LIB_CTX` context. The data must have  | 
242  |  |  * previously been stored using `CRYPTO_THREAD_set_local_ex()`.  | 
243  |  |  *  | 
244  |  |  * If the master key table is not yet initialized, it will be lazily  | 
245  |  |  * initialized via `init_master_key()`. If the requested key or context  | 
246  |  |  * entry does not exist, `NULL` is returned.  | 
247  |  |  *  | 
248  |  |  * @param id  | 
249  |  |  *        The thread-local key ID used to identify the master key entry.  | 
250  |  |  *  | 
251  |  |  * @param ctx  | 
252  |  |  *        Pointer to the `OSSL_LIB_CTX` used to index into the context  | 
253  |  |  *        table for the specified key.  | 
254  |  |  *  | 
255  |  |  * @return A pointer to the stored context-specific data, or NULL if no  | 
256  |  |  *         entry is found or initialization fails.  | 
257  |  |  */  | 
258  |  | void *CRYPTO_THREAD_get_local_ex(CRYPTO_THREAD_LOCAL_KEY_ID id, OSSL_LIB_CTX *ctx)  | 
259  | 236k  | { | 
260  | 236k  |     MASTER_KEY_ENTRY *mkey;  | 
261  | 236k  |     CTX_TABLE_ENTRY ctxd;  | 
262  |  |  | 
263  | 236k  |     ctx = (ctx == CRYPTO_THREAD_NO_CONTEXT) ? NULL : ossl_lib_ctx_get_concrete(ctx);  | 
264  |  |     /*  | 
265  |  |      * Make sure the master key has been initialized  | 
266  |  |      * NOTE: We use CRYPTO_THREAD_run_once here, rather than the  | 
267  |  |      * RUN_ONCE macros.  We do this because this code is included both in  | 
268  |  |      * libcrypto, and in fips.[dll|dylib|so].  FIPS attempts to avoid doing  | 
269  |  |      * one time initialization of global data, and so suppresses the definition  | 
270  |  |      * of RUN_ONCE, etc, meaning the build breaks if we were to use that with  | 
271  |  |      * fips-enabled.  However, this is a special case in which we want/need  | 
272  |  |      * this one bit of global data to be initialized in both the fips provider  | 
273  |  |      * and in libcrypto, so we use CRYPTO_THREAD_run_one directly, which is  | 
274  |  |      * always defined.  | 
275  |  |      */  | 
276  | 236k  |     if (!CRYPTO_THREAD_run_once(&master_once, init_master_key))  | 
277  | 0  |         return NULL;  | 
278  |  |  | 
279  | 236k  |     if (!ossl_assert(id < CRYPTO_THREAD_LOCAL_KEY_MAX))  | 
280  | 0  |         return NULL;  | 
281  |  |  | 
282  |  |     /*  | 
283  |  |      * Get our master table sparse array, indexed by key id  | 
284  |  |      */  | 
285  | 236k  |     mkey = CRYPTO_THREAD_get_local(&master_key);  | 
286  | 236k  |     if (mkey == NULL)  | 
287  | 16  |         return NULL;  | 
288  |  |  | 
289  |  |     /*  | 
290  |  |      * Get the specific data entry in the master key  | 
291  |  |      * table for the key id we are searching for  | 
292  |  |      */  | 
293  | 236k  |     if (mkey[id].ctx_table == NULL)  | 
294  | 48  |         return NULL;  | 
295  |  |  | 
296  |  |     /*  | 
297  |  |      * If we find an entry above, that will be a sparse array,  | 
298  |  |      * indexed by OSSL_LIB_CTX.  | 
299  |  |      * Note: Because we're using sparse arrays here, we can do an easy  | 
300  |  |      * trick, since we know all OSSL_LIB_CTX pointers are unique.  By casting  | 
301  |  |      * the pointer to a unitptr_t, we can use that as an ordinal index into  | 
302  |  |      * the sparse array.  | 
303  |  |      */  | 
304  | 236k  |     ctxd = ossl_sa_CTX_TABLE_ENTRY_get(mkey[id].ctx_table, (uintptr_t)ctx);  | 
305  |  |  | 
306  |  |     /*  | 
307  |  |      * If we find an entry for the passed in context, return its data pointer  | 
308  |  |      */  | 
309  | 236k  |     return ctxd;  | 
310  | 236k  | }  | 
311  |  |  | 
312  |  | /**  | 
313  |  |  * @brief Associates context-specific data with a thread-local key.  | 
314  |  |  *  | 
315  |  |  * This function stores a pointer to data associated with a specific  | 
316  |  |  * thread-local key ID and `OSSL_LIB_CTX` context. It ensures that the  | 
317  |  |  * internal thread-local master key table and all necessary sparse array  | 
318  |  |  * structures are initialized and allocated as needed.  | 
319  |  |  *  | 
320  |  |  * If the key or context-specific entry does not already exist, they will  | 
321  |  |  * be created. This function allows each thread to maintain separate data  | 
322  |  |  * for different library contexts under a shared key identifier.  | 
323  |  |  *  | 
324  |  |  * @param id  | 
325  |  |  *        The thread-local key ID to associate the data with.  | 
326  |  |  *  | 
327  |  |  * @param ctx  | 
328  |  |  *        Pointer to the `OSSL_LIB_CTX` used as a secondary key for storing  | 
329  |  |  *        the data.  | 
330  |  |  *  | 
331  |  |  * @param data  | 
332  |  |  *        Pointer to the user-defined context-specific data to store.  | 
333  |  |  *  | 
334  |  |  * @return 1 on success, or 0 if allocation or initialization fails.  | 
335  |  |  */  | 
336  |  | int CRYPTO_THREAD_set_local_ex(CRYPTO_THREAD_LOCAL_KEY_ID id,  | 
337  |  |                                OSSL_LIB_CTX *ctx, void *data)  | 
338  | 128  | { | 
339  | 128  |     MASTER_KEY_ENTRY *mkey;  | 
340  |  |  | 
341  | 128  |     ctx = (ctx == CRYPTO_THREAD_NO_CONTEXT) ? NULL : ossl_lib_ctx_get_concrete(ctx);  | 
342  |  |     /*  | 
343  |  |      * Make sure our master key is initialized  | 
344  |  |      * See notes above on the use of CRYPTO_THREAD_run_once here  | 
345  |  |      */  | 
346  | 128  |     if (!CRYPTO_THREAD_run_once(&master_once, init_master_key))  | 
347  | 0  |         return 0;  | 
348  |  |  | 
349  | 128  |     if (!ossl_assert(id < CRYPTO_THREAD_LOCAL_KEY_MAX))  | 
350  | 0  |         return 0;  | 
351  |  |  | 
352  |  |     /*  | 
353  |  |      * Get our local master key data, which will be  | 
354  |  |      * a sparse array indexed by the id parameter  | 
355  |  |      */  | 
356  | 128  |     mkey = CRYPTO_THREAD_get_local(&master_key);  | 
357  | 128  |     if (mkey == NULL) { | 
358  |  |         /*  | 
359  |  |          * we didn't find one, but that's ok, just initialize it now  | 
360  |  |          */  | 
361  | 16  |         mkey = OPENSSL_calloc(CRYPTO_THREAD_LOCAL_KEY_MAX,  | 
362  | 16  |                               sizeof(MASTER_KEY_ENTRY));  | 
363  | 16  |         if (mkey == NULL)  | 
364  | 0  |             return 0;  | 
365  |  |         /*  | 
366  |  |          * make sure to assign it to our master key thread-local storage  | 
367  |  |          */  | 
368  | 16  |         if (!CRYPTO_THREAD_set_local(&master_key, mkey)) { | 
369  | 0  |             OPENSSL_free(mkey);  | 
370  | 0  |             return 0;  | 
371  | 0  |         }  | 
372  | 16  |     }  | 
373  |  |  | 
374  |  |     /*  | 
375  |  |      * Find the entry that we are looking for using our id index  | 
376  |  |      */  | 
377  | 128  |     if (mkey[id].ctx_table == NULL) { | 
378  |  |  | 
379  |  |         /*  | 
380  |  |          * Didn't find it, that's ok, just add it now  | 
381  |  |          */  | 
382  | 48  |         mkey[id].ctx_table = ossl_sa_CTX_TABLE_ENTRY_new();  | 
383  | 48  |         if (mkey[id].ctx_table == NULL)  | 
384  | 0  |             return 0;  | 
385  | 48  |     }  | 
386  |  |  | 
387  |  |     /*  | 
388  |  |      * Now go look up our per context entry, using the OSSL_LIB_CTX pointer  | 
389  |  |      * that we've been provided.  Note we cast the pointer to a uintptr_t so  | 
390  |  |      * as to use it as an index in the sparse array  | 
391  |  |      *  | 
392  |  |      * Assign to the entry in the table so that we can find it later  | 
393  |  |      */  | 
394  | 128  |     return ossl_sa_CTX_TABLE_ENTRY_set(mkey[id].ctx_table,  | 
395  | 128  |                                        (uintptr_t)ctx, data);  | 
396  | 128  | }  | 
397  |  |  | 
398  |  | void CRYPTO_THREAD_clean_local(void)  | 
399  | 16  | { | 
400  | 16  |     MASTER_KEY_ENTRY *mkey;  | 
401  |  |  | 
402  |  |     /*  | 
403  |  |      * If we never initialized the master key, there  | 
404  |  |      * is no data to clean, so we are done here  | 
405  |  |      */  | 
406  | 16  |     if (master_key_init == 0)  | 
407  | 0  |         return;  | 
408  |  |  | 
409  | 16  |     mkey = CRYPTO_THREAD_get_local(&master_key);  | 
410  | 16  |     if (mkey != NULL) { | 
411  | 16  |         clean_master_key(mkey);  | 
412  |  |         CRYPTO_THREAD_set_local(&master_key, NULL);  | 
413  | 16  |     }  | 
414  | 16  | }  |