Commit fb5e8bed5d for openssl.org
commit fb5e8bed5df5a73ce7b45acb6b7e4f0f7ba62916
Author: Neil Horman <nhorman@openssl.org>
Date: Thu May 28 04:46:10 2026 -0400
Provide independent lock failure signal on cmp_exch_ptr
Our CRYPTO atomic api has a somewhat consistent problem in that its
possible in the case where locking fails to return an error while the
actual operation (store_int, store_ptr, etc), actually succeded.
cmp_exch is somewhat special here in that we may really need to know
independently if the function failed due to lock failure and if the
exchange occured (so we can know the output value of *expect). Add a
separate parameter to allow callers to be informed of these statuses
independently.
Reviewed-by: Norbert Pocs <norbertp@openssl.org>
Reviewed-by: Nikola Pajkovsky <nikolap@openssl.org>
MergeDate: Tue Jun 9 17:46:07 2026
(Merged from https://github.com/openssl/openssl/pull/31319)
diff --git a/crypto/rand/rand_lib.c b/crypto/rand/rand_lib.c
index a99bc7b6c6..c8d77c68e9 100644
--- a/crypto/rand/rand_lib.c
+++ b/crypto/rand/rand_lib.c
@@ -246,6 +246,7 @@ int RAND_set_rand_method(const RAND_METHOD *meth)
const RAND_METHOD *RAND_get_rand_method(void)
{
const RAND_METHOD *tmp_meth = NULL;
+ int lock_failed;
if (!RUN_ONCE(&rand_init, do_rand_init))
goto end;
@@ -267,8 +268,12 @@ const RAND_METHOD *RAND_get_rand_method(void)
* which we can just return as is
*/
if (CRYPTO_atomic_cmp_exch_ptr((void **)&default_RAND_meth, (void **)&tmp_meth,
- (void *)&ossl_rand_meth, rand_meth_lock))
+ (void *)&ossl_rand_meth, rand_meth_lock, &lock_failed)) {
tmp_meth = &ossl_rand_meth;
+ } else {
+ if (lock_failed == 1)
+ return NULL;
+ }
end:
return tmp_meth;
}
diff --git a/crypto/threads_none.c b/crypto/threads_none.c
index 44c4e227ed..170fa32897 100644
--- a/crypto/threads_none.c
+++ b/crypto/threads_none.c
@@ -299,8 +299,14 @@ int CRYPTO_atomic_store_ptr(void **dst, void **val, CRYPTO_RWLOCK *lock)
return 1;
}
-int CRYPTO_atomic_cmp_exch_ptr(void **ptr, void **expect, void *desire, CRYPTO_RWLOCK *lock)
+int CRYPTO_atomic_cmp_exch_ptr(void **ptr, void **expect, void *desire, CRYPTO_RWLOCK *lock, int *lock_failed)
{
+ int lock_sink;
+
+ if (lock_failed == NULL)
+ lock_failed = &lock_sink;
+
+ *lock_failed = 0;
if (*ptr == *expect) {
*ptr = desire;
return 1;
diff --git a/crypto/threads_pthread.c b/crypto/threads_pthread.c
index 52c98a1c2e..a2adcb9afe 100644
--- a/crypto/threads_pthread.c
+++ b/crypto/threads_pthread.c
@@ -1272,20 +1272,33 @@ int CRYPTO_atomic_store_ptr(void **dst, void **val, CRYPTO_RWLOCK *lock)
#endif
}
-int CRYPTO_atomic_cmp_exch_ptr(void **ptr, void **expect, void *desire, CRYPTO_RWLOCK *lock)
+int CRYPTO_atomic_cmp_exch_ptr(void **ptr, void **expect, void *desire, CRYPTO_RWLOCK *lock, int *lock_failed)
{
#if defined(__GNUC__) && defined(__ATOMIC_RELAXED) && !defined(BROKEN_CLANG_ATOMICS)
+ if (lock_failed != NULL)
+ *lock_failed = 0;
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))
+ int lock_sink;
+ int ret = 0;
+
+ if (lock_failed == NULL)
+ lock_failed = &lock_sink;
+
+ *lock_failed = 0;
+ if (lock == NULL || !CRYPTO_THREAD_write_lock(lock)) {
+ *lock_failed = 1;
return 0;
- if (*ptr == *expect)
+ }
+ if (*ptr == *expect) {
+ ret = 1;
*ptr = desire;
- else
+ } else {
*expect = *ptr;
+ }
if (!CRYPTO_THREAD_unlock(lock))
- return 0;
- return 1;
+ *lock_failed = 1;
+ return ret;
#endif
}
diff --git a/crypto/threads_win.c b/crypto/threads_win.c
index 4fe59b3d08..4fedf24e90 100644
--- a/crypto/threads_win.c
+++ b/crypto/threads_win.c
@@ -767,12 +767,18 @@ int CRYPTO_atomic_store_ptr(void **dst, void **val, CRYPTO_RWLOCK *lock)
return 1;
}
-int CRYPTO_atomic_cmp_exch_ptr(void **ptr, void **expect, void *desire, CRYPTO_RWLOCK *lock)
+int CRYPTO_atomic_cmp_exch_ptr(void **ptr, void **expect, void *desire, CRYPTO_RWLOCK *lock, int *lock_failed)
{
- InterlockedCompareExchangePointer(ptr, desire, *expect);
- if (*ptr == desire)
+ void *initial;
+
+ if (lock_failed != NULL)
+ lock_failed = 0;
+
+ /* Load the current pointer value */
+ initial = InterlockedCompareExchangePointer(ptr, desire, *expect);
+ if (*expect == initial)
return 1;
- *expect = *ptr;
+ *expect = initial;
return 0;
}
diff --git a/doc/man3/CRYPTO_THREAD_run_once.pod b/doc/man3/CRYPTO_THREAD_run_once.pod
index 161105471a..c2ce869af9 100644
--- a/doc/man3/CRYPTO_THREAD_run_once.pod
+++ b/doc/man3/CRYPTO_THREAD_run_once.pod
@@ -39,7 +39,8 @@ OSSL_THREAD_SUPPORT_FLAG_DEFAULT_SPAWN - OpenSSL thread support
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);
+ int CRYPTO_atomic_cmp_exch_ptr(void **ptr, void **expect, void *desire, CRYPTO_RWLOCK *lock,
+ int *lock_failed);
int OSSL_set_max_threads(OSSL_LIB_CTX *ctx, uint64_t max_threads);
uint64_t OSSL_get_max_threads(OSSL_LIB_CTX *ctx);
@@ -176,14 +177,19 @@ atomic operations.
=item *
-int CRYPTO_atomic_cmp_exch_ptr(void **ptr, void **expect, void *desire, CRYPTO_RWLOCK *lock);
+int CRYPTO_atomic_cmp_exch_ptr(void **ptr, void **expect, void *desire, CRYPTO_RWLOCK *lock,
+ int *lock_failed);
CRYPTO_atomic_cmp_exch_ptr() atomically performs a read-modify-write operation. If the contents of
I<*ptr> matches the contents of I<*expect> then the value of I<desire> is stored in I<*ptr> and the
function returns 1. If I<*ptr> does not match the contents of I<*expect>, then the contents of
I<*ptr> are atomically loaded to the contents of I<*expect> and the function returns 0.
I<lock> will be used to emulate atomic operations on platforms that do not support
-atomic operations.
+atomic operations. Note that this function contains an additional parameter I<lock_failed> which
+this function will set to 0 (to indicate the lock functioned properly), or 1 (to indicate the lock
+encountered a failure). This parameter allows callers to determine if a failure was caused by a
+locking error (which is generally a permanent/fatal error), or because the compare and exchange
+operation itself failed (which is a transient error indicating the need to retry the operation.
=item *
diff --git a/include/openssl/crypto.h.in b/include/openssl/crypto.h.in
index e5389a4c22..a9ade25b15 100644
--- a/include/openssl/crypto.h.in
+++ b/include/openssl/crypto.h.in
@@ -100,7 +100,8 @@ 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);
+int CRYPTO_atomic_cmp_exch_ptr(void **ptr, void **expect, void *desire, CRYPTO_RWLOCK *lock,
+ int *lock_failed);
/* No longer needed, so this is a no-op */
#define OPENSSL_malloc_init() \