Commit 70cdba10fa for openssl.org

commit 70cdba10faa2055fc5791bddc3e12ddfbe772496
Author: Neil Horman <nhorman@openssl.org>
Date:   Wed Apr 1 12:32:40 2026 -0400

    Add some crypto atomic pointer ops

    CRYPTO_atomic_load_ptr - load a pointer value with relaxed semantics
    CRYPTO_atomic_store_ptr - store a pointer value with relaxed semantics
    CRYPTO_atomic_cmp_exch_ptr - cmp/exch a pointer with relaxed or acq/rel
                                 semantics

    The addition of these functions enables us to better use atomics to
    replace read/write locks where we are almost always doing reads

    Reviewed-by: Paul Dale <paul.dale@oracle.com>
    Reviewed-by: Nikola Pajkovsky <nikolap@openssl.org>
    MergeDate: Tue Apr 14 08:29:30 2026
    (Merged from https://github.com/openssl/openssl/pull/30670)

diff --git a/crypto/threads_none.c b/crypto/threads_none.c
index 1bc0e85c71..2453983488 100644
--- a/crypto/threads_none.c
+++ b/crypto/threads_none.c
@@ -282,6 +282,28 @@ int CRYPTO_atomic_store_int(int *dst, int val, CRYPTO_RWLOCK *lock)
     return 1;
 }

+int CRYPTO_atomic_load_ptr(void **ptr, void **ret, CRYPTO_RWLOCK *lock)
+{
+    *ret = *ptr;
+    return 1;
+}
+
+int CRYPTO_atomic_store_ptr(void **dst, void **val, CRYPTO_RWLOCK *lock)
+{
+    *dst = *val;
+    return 1;
+}
+
+int CRYPTO_atomic_cmp_exch_ptr(void **ptr, void **expect, void *desire, CRYPTO_RWLOCK *lock)
+{
+    if (*ptr == *expect) {
+        *ptr = desire;
+        return 1;
+    }
+    *expect = *ptr;
+    return 0;
+}
+
 int openssl_init_fork_handlers(void)
 {
     return 0;
diff --git a/crypto/threads_pthread.c b/crypto/threads_pthread.c
index 178d0c8f60..9472cb437a 100644
--- a/crypto/threads_pthread.c
+++ b/crypto/threads_pthread.c
@@ -1252,6 +1252,53 @@ int CRYPTO_atomic_store_int(int *dst, int val, CRYPTO_RWLOCK *lock)
     return 1;
 }

+int CRYPTO_atomic_load_ptr(void **ptr, void **ret, CRYPTO_RWLOCK *lock)
+{
+#if defined(__GNUC__) && defined(__ATOMIC_RELAXED) && !defined(BROKEN_CLANG_ATOMICS)
+    *ret = __atomic_load_n(ptr, __ATOMIC_RELAXED);
+    return 1;
+#else
+    if (lock == NULL || !CRYPTO_THREAD_read_lock(lock))
+        return 0;
+    *ret = *ptr;
+    if (!CRYPTO_THREAD_unlock(lock))
+        return 0;
+    return 1;
+#endif
+}
+
+int CRYPTO_atomic_store_ptr(void **dst, void **val, CRYPTO_RWLOCK *lock)
+{
+#if defined(__GNUC__) && defined(__ATOMIC_RELAXED) && !defined(BROKEN_CLANG_ATOMICS)
+    __atomic_store(dst, val, __ATOMIC_RELAXED);
+    return 1;
+#else
+    if (lock == NULL || !CRYPTO_THREAD_write_lock(lock))
+        return 0;
+    *dst = *val;
+    if (!CRYPTO_THREAD_unlock(lock))
+        return 0;
+    return 1;
+#endif
+}
+
+int CRYPTO_atomic_cmp_exch_ptr(void **ptr, void **expect, void *desire, CRYPTO_RWLOCK *lock)
+{
+#if defined(__GNUC__) && defined(__ATOMIC_RELAXED) && !defined(BROKEN_CLANG_ATOMICS)
+    return __atomic_compare_exchange_n(ptr, expect, desire, 0, __ATOMIC_ACQ_REL, __ATOMIC_RELAXED) ? 1 : 0;
+#else
+    if (lock == NULL || !CRYPTO_THREAD_write_lock(lock))
+        return 0;
+    if (*ptr == *expect)
+        *ptr = desire;
+    else
+        *expect = *ptr;
+    if (!CRYPTO_THREAD_unlock(lock))
+        return 0;
+    return 1;
+#endif
+}
+
 #ifndef FIPS_MODULE
 int openssl_init_fork_handlers(void)
 {
diff --git a/crypto/threads_win.c b/crypto/threads_win.c
index 059f5118bd..beaf7f0252 100644
--- a/crypto/threads_win.c
+++ b/crypto/threads_win.c
@@ -756,6 +756,34 @@ int CRYPTO_atomic_store_int(int *dst, int val, CRYPTO_RWLOCK *lock)
 #endif
 }

+int CRYPTO_atomic_load_ptr(void **ptr, void **ret, CRYPTO_RWLOCK *lock)
+{
+    /*
+     * Windows doesn't have an atomic to do this properly, but the ms learn
+     * site here:
+     * https://learn.microsoft.com/en-us/windows/win32/api/winnt/nf-winnt-interlockedcompareexchangepointer
+     * suggests that using InterlockedCompareExchangePointer can be used to
+     * devise a load operation
+     */
+    *ret = InterlockedCompareExchangePointer(ptr, NULL, NULL);
+    return 1;
+}
+
+int CRYPTO_atomic_store_ptr(void **dst, void **val, CRYPTO_RWLOCK *lock)
+{
+    InterlockedExchangePointer(dst, *val);
+    return 1;
+}
+
+int CRYPTO_atomic_cmp_exch_ptr(void **ptr, void **expect, void *desire, CRYPTO_RWLOCK *lock)
+{
+    InterlockedCompareExchangePointer(ptr, desire, *expect);
+    if (*ptr == desire)
+        return 1;
+    *expect = *ptr;
+    return 0;
+}
+
 int openssl_init_fork_handlers(void)
 {
     return 0;
diff --git a/include/openssl/crypto.h.in b/include/openssl/crypto.h.in
index 5e15358bbc..e5389a4c22 100644
--- a/include/openssl/crypto.h.in
+++ b/include/openssl/crypto.h.in
@@ -98,6 +98,9 @@ int CRYPTO_atomic_load(uint64_t *val, uint64_t *ret, CRYPTO_RWLOCK *lock);
 int CRYPTO_atomic_load_int(int *val, int *ret, CRYPTO_RWLOCK *lock);
 int CRYPTO_atomic_store(uint64_t *dst, uint64_t val, CRYPTO_RWLOCK *lock);
 int CRYPTO_atomic_store_int(int *dst, int val, CRYPTO_RWLOCK *lock);
+int CRYPTO_atomic_load_ptr(void **ptr, void **ret, CRYPTO_RWLOCK *lock);
+int CRYPTO_atomic_store_ptr(void **dst, void **val, CRYPTO_RWLOCK *lock);
+int CRYPTO_atomic_cmp_exch_ptr(void **ptr, void **expect, void *desire, CRYPTO_RWLOCK *lock);

 /* No longer needed, so this is a no-op */
 #define OPENSSL_malloc_init() \