From: Neil Horman Date: Fri, 29 May 2026 13:59:21 +0000 (-0400) Subject: convert CRYPTO_THREAD_run_once to use InitOnceExecuteOnce api X-Git-Url: http://git.ipfire.org/gitweb/?a=commitdiff_plain;h=d053fe41a4add4e55dc5633e8aef6602a6a4b90b;p=thirdparty%2Fopenssl.git convert CRYPTO_THREAD_run_once to use InitOnceExecuteOnce api Issue #22059 reported a race condition in CRYPTO_THREAD_run_once on windows platforms. The most correct fix for this is to convert the windows run_once implementation to use the Win32 InitOnceRunOnce interface. Doing so requires at least Windows Vista/Windows Server 2008 to be available, and because WinXP hasn't built since 3.0 released, it seems sensible to bump our minimal NT version to be 0x600 (Vista/2008) Also, while we're at it, this change caught a bad programming practice in the rio_notifier code, which attempts to reset the once variable during shutdown. The windows static initalizer macro for this api is constructed such that attempting to do so causes a build break. Since once variables are not meant to be reset (since they are only triggered once), remove that reset code to avoid the breakage. Note that this problem was independently found and fixed in #30198. We're taking the fix from this pr (as they are effectively identical), and using that PR to add some much needed tests to the rio code. Reviewed-by: Tomas Mraz Reviewed-by: Matt Caswell Reviewed-by: Bob Beck MergeDate: Wed Jun 10 19:44:19 2026 (Merged from https://github.com/openssl/openssl/pull/31339) --- diff --git a/crypto/threads_win.c b/crypto/threads_win.c index 4fedf24e90c..d803b7221d9 100644 --- a/crypto/threads_win.c +++ b/crypto/threads_win.c @@ -512,32 +512,28 @@ void CRYPTO_THREAD_lock_free(CRYPTO_RWLOCK *lock) return; } -#define ONCE_UNINITED 0 -#define ONCE_ININIT 1 -#define ONCE_DONE 2 +struct init_once_cb_info { + void (*init)(void); +}; -/* - * We don't use InitOnceExecuteOnce because that isn't available in WinXP which - * we still have to support. - */ -int CRYPTO_THREAD_run_once(CRYPTO_ONCE *once, void (*init)(void)) +static BOOL CALLBACK init_wrapper(PINIT_ONCE InitOnce, PVOID param, PVOID *context) { - LONG volatile *lock = (LONG *)once; - LONG result; + struct init_once_cb_info *info = (struct init_once_cb_info *)param; - if (*lock == ONCE_DONE) - return 1; + info->init(); + return TRUE; +} - do { - result = InterlockedCompareExchange(lock, ONCE_ININIT, ONCE_UNINITED); - if (result == ONCE_UNINITED) { - init(); - *lock = ONCE_DONE; - return 1; - } - } while (result == ONCE_ININIT); +int CRYPTO_THREAD_run_once(CRYPTO_ONCE *once, void (*init)(void)) +{ + INIT_ONCE *myonce = (INIT_ONCE *)once; + struct init_once_cb_info info = { init }; + BOOL bstatus; - return (*lock == ONCE_DONE); + bstatus = InitOnceExecuteOnce(myonce, init_wrapper, (void *)&info, NULL); + if (bstatus == TRUE) + return 1; + return 0; } int CRYPTO_THREAD_init_local(CRYPTO_THREAD_LOCAL *key, void (*cleanup)(void *)) diff --git a/include/internal/e_os.h b/include/internal/e_os.h index 3ea75209cf5..f0de2f363ca 100644 --- a/include/internal/e_os.h +++ b/include/internal/e_os.h @@ -109,7 +109,7 @@ * 0x0603 // Windows 8.1 * 0x0A00 // Windows 10 */ -#define _WIN32_WINNT 0x0501 +#define _WIN32_WINNT 0x0600 #endif #include #include diff --git a/include/openssl/crypto.h.in b/include/openssl/crypto.h.in index a9ade25b155..071b11b3bc2 100644 --- a/include/openssl/crypto.h.in +++ b/include/openssl/crypto.h.in @@ -543,8 +543,8 @@ void OPENSSL_INIT_free(OPENSSL_INIT_SETTINGS *settings); typedef DWORD CRYPTO_THREAD_LOCAL; typedef DWORD CRYPTO_THREAD_ID; -typedef LONG CRYPTO_ONCE; -#define CRYPTO_ONCE_STATIC_INIT 0 +typedef INIT_ONCE CRYPTO_ONCE; +#define CRYPTO_ONCE_STATIC_INIT INIT_ONCE_STATIC_INIT #endif #else #if defined(__TANDEM) && defined(_SPT_MODEL_) diff --git a/include/openssl/e_os2.h b/include/openssl/e_os2.h index bacc161bd6b..698f2da26bf 100644 --- a/include/openssl/e_os2.h +++ b/include/openssl/e_os2.h @@ -58,6 +58,7 @@ extern "C" { #define OPENSSL_SYS_WIN32_CYGWIN #else #if defined(_WIN32) || defined(OPENSSL_SYS_WIN32) +#include "windows.h" #undef OPENSSL_SYS_UNIX #if !defined(OPENSSL_SYS_WIN32) #define OPENSSL_SYS_WIN32 diff --git a/providers/implementations/rands/seeding/rand_win.c b/providers/implementations/rands/seeding/rand_win.c index 63b523b729f..5a0815042dc 100644 --- a/providers/implementations/rands/seeding/rand_win.c +++ b/providers/implementations/rands/seeding/rand_win.c @@ -70,7 +70,7 @@ size_t ossl_pool_acquire_entropy(RAND_POOL *pool) buffer = ossl_rand_pool_add_begin(pool, bytes_needed); if (buffer != NULL) { size_t bytes = 0; - if (BCryptGenRandom(NULL, buffer, bytes_needed, + if (BCryptGenRandom(NULL, buffer, (ULONG)bytes_needed, BCRYPT_USE_SYSTEM_PREFERRED_RNG) == STATUS_SUCCESS) bytes = bytes_needed; diff --git a/ssl/rio/rio_notifier.c b/ssl/rio/rio_notifier.c index ab635aa7a0b..9d286144068 100644 --- a/ssl/rio/rio_notifier.c +++ b/ssl/rio/rio_notifier.c @@ -31,63 +31,21 @@ static int set_cloexec(int fd) #if defined(OPENSSL_SYS_WINDOWS) static CRYPTO_ONCE ensure_wsa_startup_once = CRYPTO_ONCE_STATIC_INIT; -static CRYPTO_RWLOCK *wsa_lock; -static int wsa_started; -static int wsa_ref; - -static void ossl_wsa_cleanup(void) -{ - if (wsa_started) { - wsa_started = 0; - WSACleanup(); - } - - CRYPTO_THREAD_lock_free(wsa_lock); - wsa_lock = NULL; -} DEFINE_RUN_ONCE_STATIC(do_wsa_startup) { WORD versionreq = 0x0202; /* Version 2.2 */ WSADATA wsadata; - wsa_lock = CRYPTO_THREAD_lock_new(); - if (wsa_lock == NULL) + if (WSAStartup(versionreq, &wsadata) != 0) return 0; - if (WSAStartup(versionreq, &wsadata) != 0) { - CRYPTO_THREAD_lock_free(wsa_lock); - wsa_lock = NULL; - return 0; - } - wsa_started = 1; - return 1; } static ossl_inline int ensure_wsa_startup(void) { - int rv, unused; - - rv = RUN_ONCE(&ensure_wsa_startup_once, do_wsa_startup); - if (rv != 0) - CRYPTO_atomic_add(&wsa_ref, 1, &unused, wsa_lock); - - return rv; -} - -static void wsa_done(void) -{ - int ref; - - if (wsa_lock != NULL) { - CRYPTO_atomic_add(&wsa_ref, -1, &ref, wsa_lock); - if (ref == 0) { - ossl_wsa_cleanup(); - ensure_wsa_startup_once = CRYPTO_ONCE_STATIC_INIT; - wsa_lock = NULL; - } - } + return RUN_ONCE(&ensure_wsa_startup_once, do_wsa_startup); } #endif @@ -188,8 +146,6 @@ int ossl_rio_notifier_init(RIO_NOTIFIER *nfy) if (!ensure_wsa_startup()) { ERR_raise_data(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR, "Cannot start Windows sockets"); - - wsa_done(); return 0; } #endif @@ -376,9 +332,6 @@ void ossl_rio_notifier_cleanup(RIO_NOTIFIER *nfy) BIO_closesocket(nfy->wfd); BIO_closesocket(nfy->rfd); nfy->rfd = nfy->wfd = -1; -#if defined(OPENSSL_SYS_WINDOWS) - wsa_done(); -#endif } int ossl_rio_notifier_signal(RIO_NOTIFIER *nfy) diff --git a/util/platform_symbols/windows-symbols.txt b/util/platform_symbols/windows-symbols.txt index 0f6cc11450c..7ae58a3f182 100644 --- a/util/platform_symbols/windows-symbols.txt +++ b/util/platform_symbols/windows-symbols.txt @@ -1,5 +1,6 @@ AcquireSRWLockExclusive AcquireSRWLockShared +BCryptGenRandom CertCloseStore CertFindCertificateInStore CertFreeCertificateContext