Commit 1c81e499a8 for openssl.org

commit 1c81e499a898e492130835dc326f33939ebfa705
Author: Neil Horman <nhorman@openssl.org>
Date:   Thu Apr 30 16:22:46 2026 -0400

    clean up the code a bit

    Remove some vestigual code from the property cache and name things
    appropriately

    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:28 2026
    (Merged from https://github.com/openssl/openssl/pull/31018)

diff --git a/crypto/property/property.c b/crypto/property/property.c
index 94c77bef91..78686e3fbc 100644
--- a/crypto/property/property.c
+++ b/crypto/property/property.c
@@ -15,7 +15,6 @@
 #include <openssl/provider.h>
 #include "internal/property.h"
 #include "internal/provider.h"
-#include "internal/hashtable.h"
 #include "internal/hashfunc.h"
 #include "internal/tsan_assist.h"
 #include "internal/threads_common.h"
@@ -39,27 +38,10 @@
 #endif
 #define NUM_SHARDS (1 << NUM_SHARDS_BITS)

-#ifndef MAX_CACHE_LINES
-#define MAX_CACHE_LINES 8
+#ifndef MAX_CACHE_LINES_BITS
+#define MAX_CACHE_LINES_BITS 3
 #endif
-
-#if defined(__GNUC__) || defined(__clang__)
-/*
- * ALLOW_VLA enables the use of dynamically sized arrays
- * in ossl_method_store_cache_[get|set].  This is done for
- * performance reasons, as moving the stack pointer is
- * way faster than getting memory from heap.  This introduces
- * the potential for stack overflows, but we check for that
- * by capping the size of the buffer to a large value
- * MAX_PROP_QUERY as there shouldn't be any property queries that long.
- */
-#define ALLOW_VLA
-#endif
-
-/*
- * Max allowed length of our property query
- */
-#define MAX_PROP_QUERY 4096
+#define MAX_CACHE_LINES (1 << MAX_CACHE_LINES_BITS)

 typedef struct {
     void *method;
@@ -81,8 +63,6 @@ typedef struct query_st {
     int nid; /* nid of this query */
     int archived; /* Mark entry as no longer findable */
     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;
@@ -896,10 +876,10 @@ static void ossl_cache_lists_free(STORED_ALGORITHMS *sa)

     for (i = 0; i < MAX_CACHE_LINES; i++) {
         if (!CRYPTO_atomic_load_ptr((void **)&sa->cache_lists[i], (void **)&idx, sa->alock))
-            break;
+            return;
         while (idx != NULL) {
             if (!CRYPTO_atomic_load_ptr((void **)&idx->next, (void **)&idxn, sa->alock))
-                break;
+                return;
             impl_cache_free_unlinked(idx);
             idx = idxn;
         }
@@ -930,16 +910,13 @@ int ossl_method_store_cache_flush_all(OSSL_METHOD_STORE *store)
     return 1;
 }

-static ossl_inline int ossl_method_store_cache_get_locked(OSSL_METHOD_STORE *store, OSSL_PROVIDER *prov,
-    int nid, const char *prop_query, size_t keylen, STORED_ALGORITHMS *sa, QUERY **post_insert,
-    void **method)
+static ossl_inline int ossl_method_store_cache_get_atomic(OSSL_METHOD_STORE *store, OSSL_PROVIDER *prov,
+    int nid, const char *prop_query, STORED_ALGORITHMS *sa, void **method)
 {
     uint64_t prop_hash;
     QUERY *r = NULL;
     int res = 0;

-    *post_insert = NULL;
-
     prop_hash = ossl_fnv1a_hash((uint8_t *)prop_query, strlen(prop_query));

     r = ossl_method_store_atomic_find_in_list(sa, nid, prov, prop_hash);
@@ -955,30 +932,19 @@ static ossl_inline int ossl_method_store_cache_get_locked(OSSL_METHOD_STORE *sto
 int ossl_method_store_cache_get(OSSL_METHOD_STORE *store, OSSL_PROVIDER *prov,
     int nid, const char *prop_query, void **method)
 {
-    size_t keylen = sizeof(int) + ((prop_query == NULL) ? 1 : strlen(prop_query))
-        + sizeof(OSSL_PROVIDER *);
     int ret;
     STORED_ALGORITHMS *sa;
-    QUERY *post_insert = NULL;

     if (nid <= 0 || store == NULL || prop_query == NULL)
         return 0;

-    if (keylen > MAX_PROP_QUERY)
-        return 0;
-
     sa = stored_algs_shard(store, nid);

     /*
-     * Note: We've bifurcated this function into a locked and unlocked variant
-     * Not because of any specific need to do the locked work from some other location,
-     * but rather because in the interests of performance, we allocate a buffer on the
-     * stack which can be an arbitrary size.  In order to allow for clamping of that
-     * value, we check the keylen above for size limit, and then use this call to create
-     * a new stack frame in which we can safely do that stack allocation.
+     * Do an atomic linked list walk to search for our entry
      */
-    ret = ossl_method_store_cache_get_locked(store, prov, nid, prop_query, keylen, sa,
-        &post_insert, method);
+    ret = ossl_method_store_cache_get_atomic(store, prov, nid, prop_query, sa,
+        method);

     return ret;
 }
@@ -990,6 +956,21 @@ static int ossl_method_store_atomic_archive(STORED_ALGORITHMS *sa, QUERY *old)
     return 1;
 }

+static ossl_inline int ossl_method_store_put_in_archive(STORED_ALGORITHMS *sa, QUERY *old)
+{
+    /*
+     * point the item we're remoing's next pointer to the top of the archive list
+     */
+    if (!CRYPTO_atomic_load_ptr((void **)&sa->archive, (void **)&old->next, sa->alock))
+        return 0;
+    /*
+     * And update the head of the archive list to be our new entry
+     */
+    if (!CRYPTO_atomic_store_ptr((void **)&sa->archive, (void **)&old, sa->alock))
+        return 0;
+    return 1;
+}
+
 /*
  * Migrate archived items to the archive list.  Must be done with the property write
  * lock held
@@ -999,45 +980,102 @@ static void ossl_method_store_atomic_clean_archive(STORED_ALGORITHMS *sa)
     QUERY *idx, *idxn;
     int archived;
     int i;
+    int lock_failed;

+    /*
+     * For each of our linked lists
+     */
     for (i = 0; i < MAX_CACHE_LINES; i++) {
     restart_list:
+        /*
+         * Get the head of the list
+         */
         if (!CRYPTO_atomic_load_ptr((void **)&sa->cache_lists[i], (void **)&idx, sa->alock))
             continue;
+        /*
+         * If its NULL, the list is currently empty, move on to the next one
+         */
         if (idx == NULL)
             continue;
+        /*
+         * Get its archived value
+         */
         if (!CRYPTO_atomic_load_int(&idx->archived, &archived, sa->alock))
             continue;
+        /*
+         * Also fetch its next pointer to idxn
+         */
         if (!CRYPTO_atomic_load_ptr((void **)&idx->next, (void **)&idxn, sa->alock))
             continue;
+        /*
+         * If its been archived, we want to move it to the archive list
+         */
         if (archived == 1) {
-            if (!CRYPTO_atomic_store_ptr((void **)&sa->cache_lists[i], (void **)&idxn, sa->alock))
-                continue;
-            if (!CRYPTO_atomic_load_ptr((void **)&sa->archive, (void **)&idx->next, sa->alock))
-                continue;
-            if (!CRYPTO_atomic_store_ptr((void **)&sa->archive, (void **)&idx, sa->alock))
+            /*
+             * We know this is the current list head we're working with
+             * so store the next pointer to be the new list head
+             */
+            if (!CRYPTO_atomic_cmp_exch_ptr((void **)&sa->cache_lists[i], (void **)&idx, idxn, sa->alock,
+                    &lock_failed)) {
+                if (lock_failed)
+                    continue;
+                else
+                    goto restart_list;
+            }
+
+            if (!ossl_method_store_put_in_archive(sa, idx))
                 continue;
             goto restart_list;
         }

+        /*
+         * At this point our state is:
+         * idx - points to an element in cache_lists[i]
+         * idxn points to the next entry (i.e. idx->next)
+         */
         while (idx != NULL) {
+            /*
+             * We know idx isn't archived, so we start looking at idxn
+             */
             if (idxn != NULL) {
-                if (CRYPTO_atomic_load_int(&idxn->archived, &archived, sa->alock))
+                /*
+                 * if its not NULL, see if its archived
+                 */
+                if (!CRYPTO_atomic_load_int(&idxn->archived, &archived, sa->alock))
                     break;
+                /*
+                 * If it is, remove it
+                 */
                 if (archived == 1) {
+                    /*
+                     * Start by making idx skip idxn in the list
+                     */
                     if (!CRYPTO_atomic_load_ptr((void **)&idxn->next, (void **)&idx->next, sa->alock))
                         break;
-                    if (!CRYPTO_atomic_load_ptr((void **)&sa->archive, (void **)&idxn->next, sa->alock))
+
+                    if (!ossl_method_store_put_in_archive(sa, idxn))
                         break;
-                    if (!CRYPTO_atomic_store_ptr((void **)&sa->archive, (void **)&idxn, sa->alock))
+
+                    /*
+                     * Idx just got a new next pointer above, so just update idxn, so we are sure that idx
+                     * still isn't archived
+                     */
+                    if (!CRYPTO_atomic_load_ptr((void **)&idx->next, (void **)&idxn, sa->alock))
                         break;
-                }
-                if (!CRYPTO_atomic_load_ptr((void **)&idx->next, (void **)&idx, sa->alock))
-                    break;
-                if (idx != NULL) {
+                } else {
+                    /*
+                     * idxn wasn't archived, so we need to advance both pointers here
+                     */
+                    idx = idxn;
                     if (!CRYPTO_atomic_load_ptr((void **)&idx->next, (void **)&idxn, sa->alock))
                         break;
                 }
+            } else {
+                /*
+                 * idxn is NULL, that means we're at the end of the list.
+                 * Just advance idx to idxn and the loop will break on the next iteration
+                 */
+                idx = idxn;
             }
         }
     }
@@ -1046,7 +1084,7 @@ static void ossl_method_store_atomic_clean_archive(STORED_ALGORITHMS *sa)
 static QUERY *ossl_method_store_atomic_find_in_list(STORED_ALGORITHMS *sa, int nid,
     OSSL_PROVIDER *prov, uint64_t prop_hash)
 {
-    int nididx = (nid >> NUM_SHARDS_BITS) % MAX_CACHE_LINES;
+    int nididx = (nid >> NUM_SHARDS_BITS) & (MAX_CACHE_LINES - 1);
     int archived;
     QUERY *idx;
     QUERY *ret = NULL;
@@ -1070,24 +1108,29 @@ out:

 static int ossl_method_store_atomic_insert_to_list(STORED_ALGORITHMS *sa, QUERY *new)
 {
-    int nid = (new->nid >> NUM_SHARDS_BITS) % MAX_CACHE_LINES;
+    int nid = (new->nid >> NUM_SHARDS_BITS) & (MAX_CACHE_LINES - 1);
     QUERY *headptr;
     int ret = 0;
+    int lock_failed;

     if (!CRYPTO_atomic_load_ptr((void **)&sa->cache_lists[nid], (void **)&headptr, sa->alock))
         goto out;
 try_again:
     if (!CRYPTO_atomic_store_ptr((void **)&new->next, (void **)&headptr, sa->alock))
         goto out;
-    if (!CRYPTO_atomic_cmp_exch_ptr((void **)&sa->cache_lists[nid], (void **)&headptr, new, sa->alock))
+    if (!CRYPTO_atomic_cmp_exch_ptr((void **)&sa->cache_lists[nid], (void **)&headptr, new, sa->alock,
+            &lock_failed)) {
+        if (lock_failed == 1)
+            goto out;
         goto try_again;
+    }
     ret = 1;
 out:
     return ret;
 }

-static ossl_inline int ossl_method_store_cache_set_locked(OSSL_METHOD_STORE *store, OSSL_PROVIDER *prov,
-    int nid, const char *prop_query, size_t keylen, STORED_ALGORITHMS *sa, void *method,
+static ossl_inline int ossl_method_store_cache_set_atomic(OSSL_METHOD_STORE *store, OSSL_PROVIDER *prov,
+    int nid, const char *prop_query, STORED_ALGORITHMS *sa, void *method,
     int (*method_up_ref)(void *),
     void (*method_destruct)(void *))
 {
@@ -1123,7 +1166,10 @@ static ossl_inline int ossl_method_store_cache_set_locked(OSSL_METHOD_STORE *sto
         if (!ossl_method_up_ref(&p->method))
             goto err;

-        ossl_method_store_atomic_insert_to_list(sa, p);
+        if (!ossl_method_store_atomic_insert_to_list(sa, p)) {
+            ossl_method_free(&p->method);
+            goto err;
+        }

         /*
          * We also want to add this method into the cache against a key computed _only_
@@ -1137,7 +1183,10 @@ static ossl_inline int ossl_method_store_cache_set_locked(OSSL_METHOD_STORE *sto
         p->prov = NULL;
         if (!ossl_method_up_ref(&p->method))
             goto err;
-        ossl_method_store_atomic_insert_to_list(sa, p);
+        if (!ossl_method_store_atomic_insert_to_list(sa, p)) {
+            ossl_method_free(&p->method);
+            goto err;
+        }

         goto end;
     }
@@ -1155,8 +1204,6 @@ int ossl_method_store_cache_set(OSSL_METHOD_STORE *store, OSSL_PROVIDER *prov,
 {
     STORED_ALGORITHMS *sa;
     int res = 1;
-    size_t keylen = sizeof(int) + ((prop_query == NULL) ? 1 : strlen(prop_query))
-        + sizeof(OSSL_PROVIDER *);

     if (nid <= 0 || store == NULL || prop_query == NULL)
         return 0;
@@ -1164,23 +1211,13 @@ int ossl_method_store_cache_set(OSSL_METHOD_STORE *store, OSSL_PROVIDER *prov,
     if (!ossl_assert(prov != NULL))
         return 0;

-    if (keylen > MAX_PROP_QUERY)
-        return 0;
-
     sa = stored_algs_shard(store, nid);
-#if 0
-    if (!ossl_property_write_lock(sa))
-        return 0;
-#endif
+
     /*
-     * As with cache_get_locked, we do this to allow ourselves the opportunity to make sure
-     * keylen isn't so large that the stack allocation of keylen bytes will case a stack
-     * overflow
+     * Do an atomic insert into the appropriate cache linked list
      */
-    res = ossl_method_store_cache_set_locked(store, prov, nid, prop_query, keylen, sa, method,
+    res = ossl_method_store_cache_set_atomic(store, prov, nid, prop_query, sa, method,
         method_up_ref, method_destruct);
-#if 0
-    ossl_property_unlock(sa);
-#endif
+
     return res;
 }