Commit 77f492f29f for openssl.org

commit 77f492f29f882e07d57e16920138d991dc9af018
Author: Neil Horman <nhorman@openssl.org>
Date:   Sat Jul 25 22:23:25 2026 -0400

    oqs noted a double free recently:

    https://github.com/open-quantum-safe/oqs-provider/pull/805#issuecomment-5034437500

    Its happening because, recently we made some fixes to account for
    providers that request no-caching fetches, which all seems to work
    reasonably well.

    Except for the corner case oqs recently exposed, in which the provider
    may request the caching of some algorithms, but not others.  When this
    occurs, the core libcrypto creates a temporary store for those
    requested, but stored the cachable ones in the libctx store.

    inner_evp_generic_fetch treats the presence of an allocated tmp store as
    implying that all algorithms fetched are non-cacheable, even if some
    are.  The result is that, for those cacheable items, they also get
    treated as non-cacheable, missing the extra ref count taken when they
    are added to the method store cache.  As a result, they get freed while
    still in use, and you get the issue referenced above.

    Instead of assuming that a tmp_store means all algorithms are
    non-cacheable, instead, try to look the method up in the tmp_store
    cache, and cache it if its not found (i.e. its not in the temp store)

    Fixes https://github.com/open-quantum-safe/oqs-provider/pull/805

    Reviewed-by: Saša NedvÄ›dický <sashan@openssl.org>
    Reviewed-by: Andrew Dinh <andrewd@openssl.org>
    MergeDate: Sat Aug  1 14:30:49 2026
    (Merged from https://github.com/openssl/openssl/pull/32077)

diff --git a/crypto/evp/evp_fetch.c b/crypto/evp/evp_fetch.c
index 15204628db..15478f9151 100644
--- a/crypto/evp/evp_fetch.c
+++ b/crypto/evp/evp_fetch.c
@@ -274,6 +274,9 @@ inner_evp_generic_fetch(struct evp_method_data_st *methdata,
     uint32_t meth_id = 0;
     void *method = NULL;
     int unsupported, name_id;
+    int set_in_cache = 1;
+    void *tmp_method;
+    const OSSL_PROVIDER *tmp_prov = prov;

     if (store == NULL || namemap == NULL) {
         ERR_raise(ERR_LIB_EVP, ERR_R_PASSED_INVALID_ARGUMENT);
@@ -364,7 +367,34 @@ inner_evp_generic_fetch(struct evp_method_data_st *methdata,
                  * cached end up in ->tmp_store when provider asks not
                  * to cache the result (see ossl_method_construct_reserve_store())
                  */
-                if (meth_id != 0 && methdata->tmp_store == NULL) {
+                if (meth_id != 0) {
+                    /*
+                     * If the method doesn't exist in the tmp_store, either the tmp_store doesn't exist
+                     * or the algorithm doesn't exist there, in either case, this is a cacheable entry
+                     */
+                    if (!ossl_method_store_fetch(methdata->tmp_store, meth_id, propq, &tmp_prov, &tmp_method)) {
+                        set_in_cache = 1;
+                    } else {
+                        /*
+                         * We found a matching method in the temp store, don't cache this entry
+                         */
+                        if (tmp_method == method) {
+                            set_in_cache = 0;
+                        } else {
+                            set_in_cache = 1;
+                        }
+
+#ifdef OPENSSL_NO_CACHED_FETCH
+                        /*
+                         * ossl_method_store_fetch takes a reference on the fetched method
+                         * when using NO_CACHED_FETCH, so we need to free it here
+                         */
+                        free_method(tmp_method);
+#endif
+                    }
+                }
+
+                if (set_in_cache == 1) {
                     ossl_method_store_cache_set(store, prov, meth_id, propq,
                         method, up_ref_method, free_method);
                 } else {