Commit 7122f81416 for openssl.org

commit 7122f8141674140654a1b8a9cc7cd790b9fbb9dd
Author: Pavol Žáčik <zacik.pa@gmail.com>
Date:   Wed Sep 2 14:54:28 2026 +0200

    Do not advertise OPENSSL_INIT_LOAD_CONFIG in optsdone too soon

    If there was no explicit call to OPENSSL_init_crypto before starting
    threading, it would result in a race between loading the config in
    one thread, and attempting to use features dependent on the config,
    e.g., providers in other threads.

    The first thread that gets into OPENSSL_init_crypto would start loading
    the config and set `loading` to 1 The same thread may re-enter the function
    recursively and should do nothing, but it would actually update `optsdone`
    with OPENSSL_INIT_LOAD_CONFIG, even though the loading has not finished yet.
    This commit fixes the issue by masking the opts bit out if we re-enter
    OPENSSL_init_crypto.

    Assisted-by: Claude:claude-opus-4.8

    Reviewed-by: Neil Horman <nhorman@openssl.org>
    Reviewed-by: Dmitry Belyavskiy <beldmit@gmail.com>
    MergeDate: Fri Sep  4 07:15:24 2026
    (Merged from https://github.com/openssl/openssl/pull/32646)

diff --git a/crypto/init.c b/crypto/init.c
index 302e37f7fa..1863894b39 100644
--- a/crypto/init.c
+++ b/crypto/init.c
@@ -348,6 +348,7 @@ void ossl_cleanup_destructor(void)
 int OPENSSL_init_crypto(uint64_t opts, const OPENSSL_INIT_SETTINGS *settings)
 {
     uint64_t tmp;
+    uint64_t optsdone_bits = opts;
     int aloaddone = 0;

     /* Applications depend on 0 being returned when cleanup was already done */
@@ -453,8 +454,15 @@ int OPENSSL_init_crypto(uint64_t opts, const OPENSSL_INIT_SETTINGS *settings)
     if (opts & OPENSSL_INIT_LOAD_CONFIG) {
         int loading = CRYPTO_THREAD_get_local(&in_init_config_local) != NULL;

-        /* If called recursively from OBJ_ calls, just skip it. */
-        if (!loading) {
+        /* If called recursively from OBJ_ calls during config loading,
+         * we have to mask OPENSSL_INIT_LOAD_CONFIG in optsdone to not
+         * advertise that config loading is complete, otherwise other
+         * threads may proceed, e.g., to fetching from a provider that
+         * is not yet loaded.
+         */
+        if (loading) {
+            optsdone_bits &= ~(uint64_t)OPENSSL_INIT_LOAD_CONFIG;
+        } else {
             int ret;

             if (!CRYPTO_THREAD_set_local(&in_init_config_local, (void *)-1))
@@ -480,7 +488,7 @@ int OPENSSL_init_crypto(uint64_t opts, const OPENSSL_INIT_SETTINGS *settings)
         && !RUN_ONCE(&async, ossl_init_async))
         return 0;

-    if (!CRYPTO_atomic_or(&optsdone, opts, &tmp, optsdone_lock))
+    if (!CRYPTO_atomic_or(&optsdone, optsdone_bits, &tmp, optsdone_lock))
         return 0;

     return 1;