Commit 2e74a3045b for openssl.org

commit 2e74a3045b47ae9db721614fa1283b3e8de46c51
Author: Neil Horman <nhorman@openssl.org>
Date:   Thu Jun 12 13:18:41 2025 -0400

    Move thread-event handlers to the new thread-local api

    Thread event handlers in the fips provider create a thread-local storage
    key per context, meaning we can exhaust our thread-local space quickly
    by creating lots of contexts.  Avoid that by moving to the new
    thread-local storage api.

    Reviewed-by: Saša NedvÄ›dický <sashan@openssl.org>
    Reviewed-by: Tomas Mraz <tomas@openssl.org>
    Reviewed-by: Matt Caswell <matt@openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/27794)

diff --git a/crypto/context.c b/crypto/context.c
index f15bc3d755..4c54299851 100644
--- a/crypto/context.c
+++ b/crypto/context.c
@@ -16,6 +16,7 @@
 #include "internal/core.h"
 #include "internal/bio.h"
 #include "internal/provider.h"
+#include "internal/threads_common.h"
 #include "crypto/decoder.h"
 #include "crypto/context.h"

@@ -31,7 +32,6 @@ struct ossl_lib_ctx_st {
     void *global_properties;
     void *drbg;
     void *drbg_nonce;
-    CRYPTO_THREAD_LOCAL rcu_local_key;
 #ifndef FIPS_MODULE
     void *provider_conf;
     void *bio_core;
@@ -47,7 +47,6 @@ struct ossl_lib_ctx_st {
     void *threads;
 #endif
 #ifdef FIPS_MODULE
-    void *thread_event_handler;
     void *fips_prov;
 #endif
     STACK_OF(SSL_COMP) *comp_methods;
@@ -92,9 +91,6 @@ static int context_init(OSSL_LIB_CTX *ctx)
 {
     int exdata_done = 0;

-    if (!CRYPTO_THREAD_init_local(&ctx->rcu_local_key, NULL))
-        return 0;
-
     ctx->lock = CRYPTO_THREAD_lock_new();
     if (ctx->lock == NULL)
         goto err;
@@ -187,8 +183,7 @@ static int context_init(OSSL_LIB_CTX *ctx)
 #endif

 #ifdef FIPS_MODULE
-    ctx->thread_event_handler = ossl_thread_event_ctx_new(ctx);
-    if (ctx->thread_event_handler == NULL)
+    if (!ossl_thread_event_ctx_new(ctx))
         goto err;

     ctx->fips_prov = ossl_fips_prov_ossl_ctx_new(ctx);
@@ -226,7 +221,6 @@ static int context_init(OSSL_LIB_CTX *ctx)
         ossl_crypto_cleanup_all_ex_data_int(ctx);

     CRYPTO_THREAD_lock_free(ctx->lock);
-    CRYPTO_THREAD_cleanup_local(&ctx->rcu_local_key);
     memset(ctx, '\0', sizeof(*ctx));
     return 0;
 }
@@ -331,10 +325,7 @@ static void context_deinit_objs(OSSL_LIB_CTX *ctx)
 #endif

 #ifdef FIPS_MODULE
-    if (ctx->thread_event_handler != NULL) {
-        ossl_thread_event_ctx_free(ctx->thread_event_handler);
-        ctx->thread_event_handler = NULL;
-    }
+    ossl_thread_event_ctx_free(ctx);

     if (ctx->fips_prov != NULL) {
         ossl_fips_prov_ossl_ctx_free(ctx->fips_prov);
@@ -379,7 +370,6 @@ static int context_deinit(OSSL_LIB_CTX *ctx)

     CRYPTO_THREAD_lock_free(ctx->lock);
     ctx->lock = NULL;
-    CRYPTO_THREAD_cleanup_local(&ctx->rcu_local_key);
     return 1;
 }

@@ -388,24 +378,15 @@ static int context_deinit(OSSL_LIB_CTX *ctx)
 static OSSL_LIB_CTX default_context_int;

 static CRYPTO_ONCE default_context_init = CRYPTO_ONCE_STATIC_INIT;
-static CRYPTO_THREAD_LOCAL default_context_thread_local;
 static int default_context_inited = 0;

 DEFINE_RUN_ONCE_STATIC(default_context_do_init)
 {
-    if (!CRYPTO_THREAD_init_local(&default_context_thread_local, NULL))
-        goto err;
-
     if (!context_init(&default_context_int))
-        goto deinit_thread;
+        return 0;

     default_context_inited = 1;
     return 1;
-
-deinit_thread:
-    CRYPTO_THREAD_cleanup_local(&default_context_thread_local);
-err:
-    return 0;
 }

 void ossl_lib_ctx_default_deinit(void)
@@ -413,7 +394,6 @@ void ossl_lib_ctx_default_deinit(void)
     if (!default_context_inited)
         return;
     context_deinit(&default_context_int);
-    CRYPTO_THREAD_cleanup_local(&default_context_thread_local);
     default_context_inited = 0;
 }

@@ -422,7 +402,7 @@ static OSSL_LIB_CTX *get_thread_default_context(void)
     if (!RUN_ONCE(&default_context_init, default_context_do_init))
         return NULL;

-    return CRYPTO_THREAD_get_local(&default_context_thread_local);
+    return CRYPTO_THREAD_get_local_ex(CRYPTO_THREAD_LOCAL_DEF_CTX_KEY, NULL);
 }

 static OSSL_LIB_CTX *get_default_context(void)
@@ -439,7 +419,7 @@ static int set_default_context(OSSL_LIB_CTX *defctx)
     if (defctx == &default_context_int)
         defctx = NULL;

-    return CRYPTO_THREAD_set_local(&default_context_thread_local, defctx);
+    return CRYPTO_THREAD_set_local_ex(CRYPTO_THREAD_LOCAL_DEF_CTX_KEY, NULL, defctx);
 }
 #endif

@@ -615,9 +595,6 @@ void *ossl_lib_ctx_get_data(OSSL_LIB_CTX *ctx, int index)
 #endif

 #ifdef FIPS_MODULE
-    case OSSL_LIB_CTX_THREAD_EVENT_HANDLER_INDEX:
-        return ctx->thread_event_handler;
-
     case OSSL_LIB_CTX_FIPS_PROV_INDEX:
         return ctx->fips_prov;
 #endif
@@ -656,14 +633,6 @@ const char *ossl_lib_ctx_get_descriptor(OSSL_LIB_CTX *libctx)
 #endif
 }

-CRYPTO_THREAD_LOCAL *ossl_lib_ctx_get_rcukey(OSSL_LIB_CTX *libctx)
-{
-    libctx = ossl_lib_ctx_get_concrete(libctx);
-    if (libctx == NULL)
-        return NULL;
-    return &libctx->rcu_local_key;
-}
-
 int OSSL_LIB_CTX_get_conf_diagnostics(OSSL_LIB_CTX *libctx)
 {
     libctx = ossl_lib_ctx_get_concrete(libctx);
diff --git a/crypto/initthread.c b/crypto/initthread.c
index 27b460009e..ea591dcb89 100644
--- a/crypto/initthread.c
+++ b/crypto/initthread.c
@@ -12,6 +12,7 @@
 #include "crypto/cryptlib.h"
 #include "prov/providercommon.h"
 #include "internal/thread_once.h"
+#include "internal/threads_common.h"
 #include "crypto/context.h"

 #ifdef FIPS_MODULE
@@ -90,6 +91,7 @@ static int  init_thread_deregister(void *arg, int all);
 #endif
 static void init_thread_stop(void *arg, THREAD_EVENT_HANDLER **hands);

+#ifndef FIPS_MODULE
 static THREAD_EVENT_HANDLER **
 init_get_thread_local(CRYPTO_THREAD_LOCAL *local, int alloc, int keep)
 {
@@ -106,13 +108,11 @@ init_get_thread_local(CRYPTO_THREAD_LOCAL *local, int alloc, int keep)
                 return NULL;
             }

-#ifndef FIPS_MODULE
             if (!init_thread_push_handlers(hands)) {
                 CRYPTO_THREAD_set_local(local, NULL);
                 OPENSSL_free(hands);
                 return NULL;
             }
-#endif
         }
     } else if (!keep) {
         CRYPTO_THREAD_set_local(local, NULL);
@@ -121,6 +121,32 @@ init_get_thread_local(CRYPTO_THREAD_LOCAL *local, int alloc, int keep)
     return hands;
 }

+#else
+static THREAD_EVENT_HANDLER **
+init_get_thread_local_ex(OSSL_LIB_CTX *ctx, int alloc, int keep)
+{
+    THREAD_EVENT_HANDLER **hands = CRYPTO_THREAD_get_local_ex(CRYPTO_THREAD_LOCAL_TEVENT_KEY, ctx);
+
+    if (alloc) {
+        if (hands == NULL) {
+
+            if ((hands = OPENSSL_zalloc(sizeof(*hands))) == NULL)
+                return NULL;
+
+            if (!CRYPTO_THREAD_set_local_ex(CRYPTO_THREAD_LOCAL_TEVENT_KEY, ctx, hands)) {
+                OPENSSL_free(hands);
+                return NULL;
+            }
+        }
+    } else if (!keep) {
+        CRYPTO_THREAD_set_local_ex(CRYPTO_THREAD_LOCAL_TEVENT_KEY, ctx, NULL);
+    }
+
+    return hands;
+}
+
+#endif
+
 #ifndef FIPS_MODULE
 /*
  * Since per-thread-specific-data destructors are not universally
@@ -258,22 +284,15 @@ int ossl_thread_register_fips(OSSL_LIB_CTX *libctx)
                           libctx);
 }

-void *ossl_thread_event_ctx_new(OSSL_LIB_CTX *libctx)
+int ossl_thread_event_ctx_new(OSSL_LIB_CTX *libctx)
 {
     THREAD_EVENT_HANDLER **hands = NULL;
-    CRYPTO_THREAD_LOCAL *tlocal = OPENSSL_zalloc(sizeof(*tlocal));
-
-    if (tlocal == NULL)
-        return NULL;
-
-    if (!CRYPTO_THREAD_init_local(tlocal, NULL))
-        goto deinit;

     hands = OPENSSL_zalloc(sizeof(*hands));
     if (hands == NULL)
         goto err;

-    if (!CRYPTO_THREAD_set_local(tlocal, hands))
+    if (!CRYPTO_THREAD_set_local_ex(CRYPTO_THREAD_LOCAL_TEVENT_KEY, libctx, hands))
         goto err;

     /*
@@ -286,19 +305,15 @@ void *ossl_thread_event_ctx_new(OSSL_LIB_CTX *libctx)
      * function.
      */

-    return tlocal;
+    return 1;
  err:
     OPENSSL_free(hands);
-    CRYPTO_THREAD_cleanup_local(tlocal);
- deinit:
-    OPENSSL_free(tlocal);
-    return NULL;
+    return 0;
 }

-void ossl_thread_event_ctx_free(void *tlocal)
+void ossl_thread_event_ctx_free(OSSL_LIB_CTX *ctx)
 {
-    CRYPTO_THREAD_cleanup_local(tlocal);
-    OPENSSL_free(tlocal);
+    CRYPTO_THREAD_set_local_ex(CRYPTO_THREAD_LOCAL_TEVENT_KEY, ctx, NULL);
 }

 static void ossl_arg_thread_stop(void *arg)
@@ -309,12 +324,8 @@ static void ossl_arg_thread_stop(void *arg)
 void ossl_ctx_thread_stop(OSSL_LIB_CTX *ctx)
 {
     THREAD_EVENT_HANDLER **hands;
-    CRYPTO_THREAD_LOCAL *local
-        = ossl_lib_ctx_get_data(ctx, OSSL_LIB_CTX_THREAD_EVENT_HANDLER_INDEX);

-    if (local == NULL)
-        return;
-    hands = init_get_thread_local(local, 0, 0);
+    hands = init_get_thread_local_ex(ctx, 0, 0);
     init_thread_stop(ctx, hands);
     OPENSSL_free(hands);
 }
@@ -377,8 +388,7 @@ int ossl_init_thread_start(const void *index, void *arg,
      * of OSSL_LIB_CTX and thread. This is because in FIPS mode each
      * OSSL_LIB_CTX gets informed about thread stop events individually.
      */
-    CRYPTO_THREAD_LOCAL *local
-        = ossl_lib_ctx_get_data(ctx, OSSL_LIB_CTX_THREAD_EVENT_HANDLER_INDEX);
+    hands = init_get_thread_local_ex(ctx, 1, 0);
 #else
     /*
      * Outside of FIPS mode the list of THREAD_EVENT_HANDLERs is unique per
@@ -387,9 +397,9 @@ int ossl_init_thread_start(const void *index, void *arg,
      * OSSL_LIB_CTXs are informed.
      */
     CRYPTO_THREAD_LOCAL *local = &destructor_key.value;
+    hands = init_get_thread_local(local, 1, 0);
 #endif

-    hands = init_get_thread_local(local, 1, 0);
     if (hands == NULL)
         return 0;

diff --git a/include/crypto/context.h b/include/crypto/context.h
index 1c181933e0..9e0d13137a 100644
--- a/include/crypto/context.h
+++ b/include/crypto/context.h
@@ -23,7 +23,7 @@ void *ossl_self_test_set_callback_new(OSSL_LIB_CTX *);
 void *ossl_indicator_set_callback_new(OSSL_LIB_CTX *);
 void *ossl_rand_crng_ctx_new(OSSL_LIB_CTX *);
 int ossl_thread_register_fips(OSSL_LIB_CTX *);
-void *ossl_thread_event_ctx_new(OSSL_LIB_CTX *);
+int ossl_thread_event_ctx_new(OSSL_LIB_CTX *);
 void *ossl_fips_prov_ossl_ctx_new(OSSL_LIB_CTX *);
 #if defined(OPENSSL_THREADS)
 void *ossl_threads_ctx_new(OSSL_LIB_CTX *);
@@ -42,7 +42,7 @@ void ossl_prov_drbg_nonce_ctx_free(void *);
 void ossl_indicator_set_callback_free(void *cb);
 void ossl_self_test_set_callback_free(void *);
 void ossl_rand_crng_ctx_free(void *);
-void ossl_thread_event_ctx_free(void *);
+void ossl_thread_event_ctx_free(OSSL_LIB_CTX *);
 void ossl_fips_prov_ossl_ctx_free(void *);
 void ossl_release_default_drbg_ctx(void);
 #if defined(OPENSSL_THREADS)
diff --git a/include/internal/cryptlib.h b/include/internal/cryptlib.h
index da442f8a86..8ad542c4d8 100644
--- a/include/internal/cryptlib.h
+++ b/include/internal/cryptlib.h
@@ -103,9 +103,9 @@ typedef struct ossl_ex_data_global_st {
 # define OSSL_LIB_CTX_DRBG_INDEX                     5
 # define OSSL_LIB_CTX_DRBG_NONCE_INDEX               6
 /* slot 7 unused, was CRNG test data and can be reused */
-# ifdef FIPS_MODULE
-#  define OSSL_LIB_CTX_THREAD_EVENT_HANDLER_INDEX    8
-# endif
+/*
+ * slot 8 unused, was OSSL_LIB_CTX_THREAD_EVENT_HANDLER_INDEX
+ */
 # define OSSL_LIB_CTX_FIPS_PROV_INDEX                9
 # define OSSL_LIB_CTX_ENCODER_STORE_INDEX           10
 # define OSSL_LIB_CTX_DECODER_STORE_INDEX           11
@@ -133,7 +133,6 @@ void ossl_lib_ctx_default_deinit(void);
 OSSL_EX_DATA_GLOBAL *ossl_lib_ctx_get_ex_data_global(OSSL_LIB_CTX *ctx);

 const char *ossl_lib_ctx_get_descriptor(OSSL_LIB_CTX *libctx);
-CRYPTO_THREAD_LOCAL *ossl_lib_ctx_get_rcukey(OSSL_LIB_CTX *libctx);

 OSSL_LIB_CTX *ossl_crypto_ex_data_get_ossl_lib_ctx(const CRYPTO_EX_DATA *ad);
 int ossl_crypto_new_ex_data_ex(OSSL_LIB_CTX *ctx, int class_index, void *obj,