Commit 3233fe19a1 for openssl.org

commit 3233fe19a19ac7e93d58ce3207cdecbcd7543d65
Author: Neil Horman <nhorman@openssl.org>
Date:   Thu Oct 1 12:53:24 2026 -0400

    Defer implementation freeing in method store until the libctx is freed

    If we unload a provider, while another thread is iterating over the
    method list, its possible we will use an implementation method that gets
    freed from under us.

    Fix it by deferring the freeing of these implementations until such time
    as the libctx itself is removed.  Mark them as unfindable so subsequent
    calls to ossl_method_store_fetch doesn't return them.

    Reviewed-by: Nikola Pajkovsky <nikolap@openssl.org>
    Reviewed-by: Tomas Mraz <tomas@openssl.foundation>
    Merge-date: Mon Oct  5 09:31:06 2026
    Merged-from: https://github.com/openssl/openssl/pull/33056

diff --git a/crypto/property/property.c b/crypto/property/property.c
index 81cb6a8d6d..66c2023f29 100644
--- a/crypto/property/property.c
+++ b/crypto/property/property.c
@@ -49,6 +49,7 @@ typedef struct {
 } METHOD;

 typedef struct {
+    int archived;
     const OSSL_PROVIDER *provider;
     OSSL_PROPERTY_LIST *properties;
     METHOD method;
@@ -416,6 +417,7 @@ int ossl_method_store_add(OSSL_METHOD_STORE *store, const OSSL_PROVIDER *prov,
     impl = OPENSSL_malloc(sizeof(*impl));
     if (impl == NULL)
         return 0;
+    impl->archived = 0;
     impl->method.method = method;
     impl->method.up_ref = method_up_ref;
     impl->method.free = method_destruct;
@@ -482,6 +484,9 @@ int ossl_method_store_add(OSSL_METHOD_STORE *store, const OSSL_PROVIDER *prov,
     for (i = 0; i < sk_IMPLEMENTATION_num(alg->impls); i++) {
         const IMPLEMENTATION *tmpimpl = sk_IMPLEMENTATION_value(alg->impls, i);

+        if (tmpimpl->archived == 1)
+            continue;
+
         if (tmpimpl->provider == impl->provider
             && tmpimpl->properties == impl->properties)
             break;
@@ -602,10 +607,8 @@ alg_cleanup_by_provider(ossl_uintmax_t idx, ALGORITHM *alg, void *arg)
             }
             OSSL_TRACE_END(QUERY);
 #endif
-
-            (void)sk_IMPLEMENTATION_delete(alg->impls, i);
+            impl->archived = 1;
             count++;
-            impl_free(impl);
         }
     }

@@ -648,6 +651,8 @@ static void alg_do_one(ALGORITHM *alg, IMPLEMENTATION *impl,
 static void alg_copy(ossl_uintmax_t idx, ALGORITHM *alg, void *arg)
 {
     STACK_OF(ALGORITHM) *newalg = arg;
+    int i;
+    IMPLEMENTATION *impl;

     alg = OPENSSL_memdup(alg, sizeof(ALGORITHM));
     if (alg == NULL)
@@ -655,6 +660,17 @@ static void alg_copy(ossl_uintmax_t idx, ALGORITHM *alg, void *arg)

     alg->impls = sk_IMPLEMENTATION_dup(alg->impls);

+    /*
+     * Remove any archived items while we're under lock
+     * note we don't have to free the implementation here
+     * as its still tracked in the alg struct we're cloning from
+     */
+    for (i = sk_IMPLEMENTATION_num(alg->impls); i-- > 0;) {
+        impl = sk_IMPLEMENTATION_value(alg->impls, i);
+        if (impl->archived == 1)
+            (void)sk_IMPLEMENTATION_delete(alg->impls, i);
+    }
+
     (void)sk_ALGORITHM_push(newalg, alg);
 }

@@ -799,6 +815,8 @@ int ossl_method_store_fetch(OSSL_METHOD_STORE *store,
     if (pq == NULL) {
         for (j = 0; j < sk_IMPLEMENTATION_num(alg->impls); j++) {
             impl = sk_IMPLEMENTATION_value(alg->impls, j);
+            if (impl->archived == 1)
+                continue;
             if (impl != NULL
                 && (prov == NULL || impl->provider == prov)) {
                 best_impl = impl;
@@ -818,6 +836,7 @@ int ossl_method_store_fetch(OSSL_METHOD_STORE *store,
     for (j = 0; j < sk_IMPLEMENTATION_num(alg->impls); j++) {
         impl = sk_IMPLEMENTATION_value(alg->impls, j);
         if (impl != NULL
+            && impl->archived == 0
             && (prov == NULL || impl->provider == prov)) {
             score = ossl_property_match_count(pq, impl->properties);
             if (score > best) {