Commit b84faffb27 for openssl.org

commit b84faffb2776c725f0cf40a5ede727af04c7451b
Author: Neil Horman <nhorman@openssl.org>
Date:   Tue Apr 28 14:01:04 2026 -0400

    fully replace hash table with linked list

    Reviewed-by: Saša NedvÄ›dický <sashan@openssl.org>
    Reviewed-by: Bob Beck <beck@openssl.org>
    Reviewed-by: Nikola Pajkovsky <nikolap@openssl.org>
    MergeDate: Tue Jun  9 18:17:10 2026
    (Merged from https://github.com/openssl/openssl/pull/31018)

diff --git a/crypto/property/property.c b/crypto/property/property.c
index b0ed725d8f..dbc27c4f9b 100644
--- a/crypto/property/property.c
+++ b/crypto/property/property.c
@@ -16,9 +16,9 @@
 #include "internal/property.h"
 #include "internal/provider.h"
 #include "internal/hashtable.h"
+#include "internal/hashfunc.h"
 #include "internal/tsan_assist.h"
 #include "internal/list.h"
-#include "internal/hashfunc.h"
 #include "internal/time.h"
 #include <openssl/lhash.h>
 #include <openssl/rand.h>
@@ -78,22 +78,13 @@ typedef struct query_st {
     struct query_st *next;
     void *saptr; /* pointer to our owning STORED_ALGORITHM */
     int nid; /* nid of this query */
+    OSSL_PROVIDER *prov; /*provider this belongs to */
     uint64_t specific_hash; /* hash of [nid,prop_query,prov] tuple */
     uint64_t generic_hash; /* hash of [nid,prop_query] tuple */
+    uint64_t prop_hash; /* hash of the property string */
     METHOD method; /* METHOD for this query */
 } QUERY;

-/*
- * This is our key to lookup queries
- * It has no key data as we allocate and marshall
- * it dynamically in ossl_method_store_cache_[set|get]
- */
-typedef struct query_key {
-    HT_KEY key_header;
-} QUERY_KEY;
-
-IMPLEMENT_HT_VALUE_TYPE_FNS(QUERY, cache, static)
-
 DEFINE_LIST_OF(lru_entry, QUERY);

 typedef OSSL_LIST(lru_entry) QUERY_LRU_LIST;
@@ -106,7 +97,6 @@ typedef struct {
 typedef struct {
     SPARSE_ARRAY_OF(ALGORITHM) * algs;

-    HT *cache;
     QUERY *cache_lists[MAX_CACHE_LINES];

     /*
@@ -131,6 +121,9 @@ typedef struct {

 static int ossl_method_store_atomic_insert_to_list(STORED_ALGORITHMS *sa, QUERY *new);
 static int ossl_method_store_atomic_remove_from_list(STORED_ALGORITHMS *sa, QUERY *old);
+static QUERY *ossl_method_store_atomic_find_in_list(STORED_ALGORITHMS *sa, int nid,
+    OSSL_PROVIDER *prov, uint64_t prop_hash);
+static void ossl_cache_lists_flush(STORED_ALGORITHMS *sa);

 struct ossl_method_store_st {
     OSSL_LIB_CTX *ctx;
@@ -266,21 +259,9 @@ static ossl_inline void impl_cache_free(QUERY *elem)
     }
 }

-/*
- * This is the registered free function for all of our
- * allocated QUERY hashtables
- */
-static void query_free(HT_VALUE *v)
-{
-    QUERY *elem = ossl_ht_cache_QUERY_from_value(v);
-    impl_cache_free(elem);
-}
-
 static void impl_cache_flush_alg(ALGORITHM *alg, STORED_ALGORITHMS *sa)
 {
     QUERY *q, *qn;
-    uint64_t hash;
-    QUERY_KEY key;

     /*
      * Instead of iterating over the hashtable with the
@@ -305,10 +286,7 @@ static void impl_cache_flush_alg(ALGORITHM *alg, STORED_ALGORITHMS *sa)
              * We use the specific hash if its available, otherwise use the
              * generic_hash
              */
-            hash = (q->specific_hash != 0) ? q->specific_hash : q->generic_hash;
-            HT_INIT_KEY_CACHED(&key, hash);
             ossl_method_store_atomic_remove_from_list(sa, q);
-            ossl_ht_delete(sa->cache, TO_HT_KEY(&key));
         }
     }
 }
@@ -333,9 +311,9 @@ static void stored_algs_free(STORED_ALGORITHMS *sa)
     for (int i = 0; i < NUM_SHARDS; ++i) {
         ossl_sa_ALGORITHM_doall_arg(sa[i].algs, &alg_cleanup, &sa[i]);
         ossl_sa_ALGORITHM_free(sa[i].algs);
+        ossl_cache_lists_flush(&sa[i]);
         CRYPTO_THREAD_lock_free(sa[i].lock);
         CRYPTO_THREAD_lock_free(sa[i].alock);
-        ossl_ht_free(sa[i].cache);
     }

     OPENSSL_free(sa);
@@ -344,15 +322,6 @@ static void stored_algs_free(STORED_ALGORITHMS *sa)
 static STORED_ALGORITHMS *stored_algs_new(OSSL_LIB_CTX *ctx)
 {
     STORED_ALGORITHMS *ret;
-    HT_CONFIG ht_conf = {
-        .ctx = ctx,
-        .ht_free_fn = query_free,
-        .ht_hash_fn = NULL,
-        .init_neighborhoods = 1,
-        .collision_check = 1,
-        .lockless_reads = 0,
-        .no_rcu = 1
-    };

     ret = OPENSSL_calloc(NUM_SHARDS, sizeof(STORED_ALGORITHMS));
     if (ret == NULL)
@@ -369,9 +338,6 @@ static STORED_ALGORITHMS *stored_algs_new(OSSL_LIB_CTX *ctx)
         ret[i].alock = CRYPTO_THREAD_lock_new();
         if (ret[i].alock == NULL)
             goto err;
-        ret[i].cache = ossl_ht_new(&ht_conf);
-        if (ret[i].cache == NULL)
-            goto err;
     }

     return ret;
@@ -935,6 +901,22 @@ static void ossl_method_cache_flush(STORED_ALGORITHMS *sa, int nid)
         ossl_method_cache_flush_alg(sa, alg);
 }

+static void ossl_cache_lists_flush(STORED_ALGORITHMS *sa)
+{
+    int i;
+    QUERY *idx;
+
+    for (i = 0; i < MAX_CACHE_LINES; i++) {
+        for (;;) {
+            if (!CRYPTO_atomic_load_ptr((void **)&sa->cache_lists[i], (void **)&idx, sa->alock))
+                break;
+            if (idx == NULL)
+                break;
+            ossl_method_store_atomic_remove_from_list(sa, idx);
+        }
+    }
+}
+
 int ossl_method_store_cache_flush_all(OSSL_METHOD_STORE *store)
 {
     for (int i = 0; i < NUM_SHARDS; ++i) {
@@ -942,7 +924,7 @@ int ossl_method_store_cache_flush_all(OSSL_METHOD_STORE *store)

         if (!ossl_property_write_lock(sa))
             return 0;
-        ossl_ht_flush(sa->cache);
+        ossl_cache_lists_flush(sa);
         ossl_property_unlock(sa);
     }

@@ -954,106 +936,25 @@ static ossl_inline int ossl_method_store_cache_get_locked(OSSL_METHOD_STORE *sto
     void **method)
 {
     ALGORITHM *alg;
+    uint64_t prop_hash;
     QUERY *r = NULL;
     int res = 0;
-    QUERY_KEY key;
-    HT_VALUE *v = NULL;
-    uint64_t generic_hash;
-#ifdef ALLOW_VLA
-    uint8_t keybuf[keylen];
-#else
-    uint8_t *keybuf;
-#endif

     *post_insert = NULL;

-#ifndef ALLOW_VLA
-    keybuf = OPENSSL_malloc(keylen);
-    if (keybuf == NULL)
-        goto err;
-#endif
-
     alg = ossl_method_store_retrieve(sa, nid);
     if (alg == NULL)
         goto err;

-    /*
-     * Marshall our lookup key.
-     * the key is always [nid,prop_query] and may include
-     * the address of the provider on the end if given
-     */
-    keylen = 0;
-    memcpy(&keybuf[keylen], &nid, sizeof(int));
-    keylen += sizeof(int);
-    memcpy(&keybuf[keylen], prop_query, strlen(prop_query));
-    keylen += strlen(prop_query);
-    if (prov != NULL) {
-        memcpy(&keybuf[keylen], &prov, sizeof(OSSL_PROVIDER *));
-        keylen += sizeof(OSSL_PROVIDER *);
-    }
+    prop_hash = ossl_fnv1a_hash((uint8_t *)prop_query, strlen(prop_query));

-    HT_INIT_KEY_EXTERNAL(&key, keybuf, keylen);
+    r = ossl_method_store_atomic_find_in_list(sa, nid, prov, prop_hash);

-    r = ossl_ht_cache_QUERY_get(sa->cache, TO_HT_KEY(&key), &v);
-    if (r == NULL) {
-        if (prov != NULL)
-            goto err;
-        /*
-         * We don't have a providerless entry for this lookup
-         * (it likely got culled), so we need to rebuild one
-         * we can used the cached hash value from the above lookup
-         * to scan the lru list for a good match
-         */
-        generic_hash = HT_KEY_GET_HASH(&key);
-        OSSL_LIST_FOREACH(r, lru_entry, &sa->lru_list)
-        {
-            if (r->generic_hash == generic_hash) {
-                /*
-                 * We found an entry for which the generic_hash
-                 * (that is the hash of the [nid,propquery] tuple
-                 * matches what we tried, and failed to look up
-                 * above, so duplicate this as our new generic lookup
-                 */
-                r = OPENSSL_memdup(r, sizeof(*r));
-                if (r == NULL)
-                    goto err;
-                r->generic_hash = generic_hash;
-                r->specific_hash = 0;
-                ossl_list_lru_entry_init_elem(r);
-                HT_INIT_KEY_CACHED(&key, generic_hash);
-                /*
-                 * We need to take a reference here to represent the hash table
-                 * ownership.  We will take a second reference below as the caller
-                 * owns it as well
-                 */
-                if (!ossl_method_up_ref(&r->method)) {
-                    OPENSSL_free(r);
-                    r = NULL;
-                }
-                /*
-                 * Inform the caller that we need to insert this newly created
-                 * QUERY into the hash table.  We do this because we only
-                 * hold the read lock here, so after the caller drops it, we
-                 * can then take the write lock to do the insert
-                 */
-                *post_insert = r;
-                break;
-            }
-        }
-        if (r == NULL)
-            goto err;
-    }
-    if (ossl_method_up_ref(&r->method)) {
+    if (r != NULL && ossl_method_up_ref(&r->method)) {
         *method = r->method.method;
         res = 1;
-    } else if (*post_insert == r) {
-        impl_cache_free_unlinked(r);
-        *post_insert = NULL;
     }
 err:
-#ifndef ALLOW_VLA
-    OPENSSL_free(keybuf);
-#endif
     return res;
 }

@@ -1065,7 +966,6 @@ int ossl_method_store_cache_get(OSSL_METHOD_STORE *store, OSSL_PROVIDER *prov,
     int ret;
     STORED_ALGORITHMS *sa;
     QUERY *post_insert = NULL;
-    QUERY_KEY key;

     if (nid <= 0 || store == NULL || prop_query == NULL)
         return 0;
@@ -1090,36 +990,6 @@ int ossl_method_store_cache_get(OSSL_METHOD_STORE *store, OSSL_PROVIDER *prov,

     ossl_property_unlock(sa);

-    if (ret == 1 && post_insert != NULL) {
-        if (!ossl_property_write_lock(sa)) {
-            ossl_method_free(&post_insert->method);
-            impl_cache_free_unlinked(post_insert);
-            *method = NULL;
-            ret = 0;
-        } else {
-            int insert_rc;
-
-            HT_INIT_KEY_CACHED(&key, post_insert->generic_hash);
-
-            insert_rc = ossl_ht_cache_QUERY_insert(sa->cache, TO_HT_KEY(&key),
-                post_insert, NULL);
-            if (insert_rc != 1) {
-                /*
-                 * Another thread may have inserted the same QUERY, or the
-                 * hash table insertion itself may have failed. Drop this
-                 * pending entry and the caller-visible method reference.
-                 */
-                ossl_method_free(&post_insert->method);
-                impl_cache_free_unlinked(post_insert);
-                *method = NULL;
-                ret = 0;
-            } else {
-                ossl_list_lru_entry_insert_tail(&sa->lru_list, post_insert);
-                ossl_method_store_atomic_insert_to_list(sa, post_insert);
-            }
-            ossl_property_unlock(sa);
-        }
-    }
     return ret;
 }

@@ -1136,6 +1006,7 @@ static int ossl_method_store_atomic_remove_from_list(STORED_ALGORITHMS *sa, QUER
             goto out;
         if (!CRYPTO_atomic_store_ptr((void **)&sa->cache_lists[nid], (void **)&next, sa->alock))
             goto out;
+        impl_cache_free(old);
         ret = 1;
         goto out;
     }
@@ -1148,6 +1019,7 @@ static int ossl_method_store_atomic_remove_from_list(STORED_ALGORITHMS *sa, QUER
                 goto out;
             if (!CRYPTO_atomic_cmp_exch_ptr((void **)&idx->next, (void **)&next, oldnext, sa->alock))
                 goto try_again;
+            impl_cache_free(old);
             ret = 1;
             goto out;
         }
@@ -1158,6 +1030,28 @@ out:
     return ret;
 }

+static QUERY *ossl_method_store_atomic_find_in_list(STORED_ALGORITHMS *sa, int nid,
+    OSSL_PROVIDER *prov, uint64_t prop_hash)
+{
+    int nididx = nid % MAX_CACHE_LINES;
+    QUERY *idx;
+    QUERY *ret = NULL;
+
+    if (!CRYPTO_atomic_load_ptr((void **)&sa->cache_lists[nididx], (void **)&idx, sa->alock))
+        goto out;
+
+    while (idx != NULL) {
+        if (idx->nid == nid && idx->prop_hash == prop_hash && idx->prov == prov) {
+            ret = idx;
+            break;
+        }
+        if (!CRYPTO_atomic_load_ptr((void **)&idx->next, (void **)&idx, sa->alock))
+            goto out;
+    }
+out:
+    return ret;
+}
+
 static int ossl_method_store_atomic_insert_to_list(STORED_ALGORITHMS *sa, QUERY *new)
 {
     int nid = new->nid % MAX_CACHE_LINES;
@@ -1183,47 +1077,33 @@ static ossl_inline int ossl_method_store_cache_set_locked(OSSL_METHOD_STORE *sto
 {
     QUERY *old = NULL, *p = NULL;
     ALGORITHM *alg;
-    QUERY_KEY key;
+    uint64_t prop_hash = ossl_fnv1a_hash((uint8_t *)prop_query, strlen(prop_query));
     int res = 1;
-    int insert_rc;
-#ifdef ALLOW_VLA
-    uint8_t keybuf[keylen];
-#else
-    uint8_t *keybuf;
-#endif
-
-#ifndef ALLOW_VLA
-    keybuf = OPENSSL_malloc(keylen);
-    if (keybuf == NULL)
-        goto err;
-#endif

     alg = ossl_method_store_retrieve(sa, nid);
     if (alg == NULL)
         goto err;

-    /*
-     * Marshall our lookup key
-     * NOTE: Provider cant be NULL here so we always add it
-     */
-    keylen = 0;
-    memcpy(&keybuf[keylen], &nid, sizeof(int));
-    keylen += sizeof(int);
-    memcpy(&keybuf[keylen], prop_query, strlen(prop_query));
-    keylen += strlen(prop_query);
-    memcpy(&keybuf[keylen], &prov, sizeof(OSSL_PROVIDER *));
-    keylen += sizeof(OSSL_PROVIDER *);
-
-    HT_INIT_KEY_EXTERNAL(&key, keybuf, keylen);
-
     if (method == NULL) {
-        ossl_ht_delete(sa->cache, TO_HT_KEY(&key));
+        p = ossl_method_store_atomic_find_in_list(sa, nid, prov, prop_hash);
+        if (p != NULL)
+            ossl_method_store_atomic_remove_from_list(sa, p);
         goto end;
     }
+
+    p = ossl_method_store_atomic_find_in_list(sa, nid, prov, prop_hash);
+    if (p != NULL) {
+        ossl_method_store_atomic_remove_from_list(sa, p);
+        p = ossl_method_store_atomic_find_in_list(sa, nid, NULL, prop_hash);
+        if (p != NULL)
+            ossl_method_store_atomic_remove_from_list(sa, p);
+    }
     p = OPENSSL_malloc(sizeof(*p));
     if (p != NULL) {
         p->saptr = sa;
         p->nid = nid;
+        p->prov = prov;
+        p->prop_hash = prop_hash;
         ossl_list_lru_entry_init_elem(p);
         p->method.method = method;
         p->method.up_ref = method_up_ref;
@@ -1231,17 +1111,6 @@ static ossl_inline int ossl_method_store_cache_set_locked(OSSL_METHOD_STORE *sto
         if (!ossl_method_up_ref(&p->method))
             goto err;

-        insert_rc = ossl_ht_cache_QUERY_insert(sa->cache, TO_HT_KEY(&key), p,
-            &old);
-        if (insert_rc != 1) {
-            impl_cache_free_unlinked(p);
-            p = NULL;
-            goto err;
-        }
-        p->specific_hash = HT_KEY_GET_HASH(&key);
-        p->generic_hash = 0;
-        if (old != NULL)
-            impl_cache_free(old);
         ossl_list_lru_entry_insert_head(&sa->lru_list, p);
         ossl_method_store_atomic_insert_to_list(sa, p);

@@ -1250,28 +1119,16 @@ static ossl_inline int ossl_method_store_cache_set_locked(OSSL_METHOD_STORE *sto
          * from nid and property query.  This lets us match in the event someone does a lookup
          * against a NULL provider (i.e. the "any provided alg will do" match
          */
-        keylen -= sizeof(OSSL_PROVIDER *);
-        HT_INIT_KEY_EXTERNAL(&key, keybuf, keylen);
         old = p;
         p = OPENSSL_memdup(p, sizeof(*p));
         if (p == NULL)
             goto err;
-
+        p->prov = NULL;
         ossl_list_lru_entry_init_elem(p);
         if (!ossl_method_up_ref(&p->method))
             goto err;
-        insert_rc = ossl_ht_cache_QUERY_insert(sa->cache, TO_HT_KEY(&key), p,
-            NULL);
-        if (insert_rc == 1) {
-            p->specific_hash = 0;
-            p->generic_hash = old->generic_hash = HT_KEY_GET_HASH(&key);
-            ossl_list_lru_entry_insert_tail(&sa->lru_list, p);
-            ossl_method_store_atomic_insert_to_list(sa, p);
-        } else {
-            impl_cache_free_unlinked(p);
-            p = NULL;
-            goto err;
-        }
+        ossl_list_lru_entry_insert_tail(&sa->lru_list, p);
+        ossl_method_store_atomic_insert_to_list(sa, p);

         goto end;
     }
@@ -1279,9 +1136,6 @@ err:
     res = 0;
     OPENSSL_free(p);
 end:
-#ifndef ALLOW_VLA
-    OPENSSL_free(keybuf);
-#endif
     return res;
 }