Skip to content

Lock globalRNGMutex in BN_rand, ECDH, EC25519 and AddSession - #11048

Open
yosuke-wolfssl wants to merge 1 commit into
wolfSSL:masterfrom
yosuke-wolfssl:fix/f_7543
Open

Lock globalRNGMutex in BN_rand, ECDH, EC25519 and AddSession#11048
yosuke-wolfssl wants to merge 1 commit into
wolfSSL:masterfrom
yosuke-wolfssl:fix/f_7543

Conversation

@yosuke-wolfssl

@yosuke-wolfssl yosuke-wolfssl commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Problem

globalRNG (src/ssl.c) is a single process-wide WC_RNG whose designated lock is globalRNGMutex. The RAND_* family in src/ssl_crypto.c takes that mutex at all eleven of its call sites. Four OpenSSL-compat entry points that use globalRNG as their primary RNG take no lock at all.

WC_RNG has no internal per-instance locking: the only mutex in wolfcrypt/src/random.c is drbgStateMutex, which guards capability flags, not the DRBG V/C working state. Two threads calling BN_rand() concurrently, or one calling BN_rand() while another calls the correctly locked RAND_bytes(), interleave DRBG generation on the same state and can produce torn or duplicate output used directly as private key material.

Closes F-7543.

Fix (src/ssl_bn.c)

Each site now holds globalRNGMutex across its use of the global RNG, open-coded in the same style as the existing lock sites (wolfSSL_SMIME_write_PKCS7 is the closest template). No new helper is introduced, so there remains one locking convention in the file.

Site Lock scope
wolfSSL_BN_rand (ssl_bn.c) wc_RNG_GenerateBlock
wolfSSL_ECDH_compute_key (pk_ec.c) key->rng set, wc_ecc_shared_secret_ex, key->rng clear
wolfSSL_EC25519_shared_key (pk.c) wc_curve25519_shared_secret_ex, under WOLFSSL_CURVE25519_BLINDING
AddSession (ssl_sess.c) wc_RNG_GenerateBlock

Both ECDH sites also gain the missing NULL check on wolfssl_make_global_rng(), so the mutex is not taken on a path that cannot use the RNG. In wolfSSL_ECDH_compute_key the shared-secret call is now guarded on !err, and the lock spans the key->rng set and clear since those mutate the EC_KEY.

Review follow-ups

TOCTOU read of key->rng in wolfSSL_ECDH_compute_key (f-bot) - acknowledged, not fixed here. The observation is accurate: key->rng == NULL is read outside the lock, so a second thread sharing the same EC_KEY could observe it already set and skip locking. It is not addressed in this PR because:

  • Locking before the check would take globalRNGMutex on every ECDH call, including keys carrying their own RNG that never touch the global, serializing all ECDH process-wide.
  • It would not close the window anyway. The second thread would see key->rng != NULL under the lock and either release it, or hold it for every operation regardless of RNG provenance. Even holding it, the first thread's cleanup sets key->rng = NULL mid-operation, yielding MISSING_RNG_E (ecc.c).
  • A shared EC_KEY is already unsound here independent of the RNG. wc_ecc_shared_secret_ex writes private_key->state three times and dispatches on it with default: err = BAD_STATE_E, so two concurrent callers on one key corrupt that state machine and can spuriously fail. That needs a per-key lock, which neither wolfSSL nor OpenSSL provides for EC_KEY.

The unlocked read is pre-existing and unchanged by this PR, so there is no regression, and the case that matters in practice, each thread holding its own key and falling back to the global RNG, is fully fixed.

Intentionally not in this PR

  • Roughly eighteen further sites reach the global only through wolfssl_make_rng()'s fallback, after wc_InitRng() on a local RNG has already failed. Same defect, far lower reachability, deferred to keep this change reviewable.
  • Making a concurrently shared EC_KEY safe, per the review follow-up above. That is a per-key locking problem, not an RNG one.
  • wolfssl_make_rng() sets *local only on success, and pk.c declares int localRng; uninitialized at one caller, so the fallback path can reach wc_FreeRng() on &globalRNG. Worth its own finding.

Verification

  • Builds clean, no new warnings, across opensslall+keygen, opensslextra+all, dh+ecc (no OPENSSL_EXTRA), singlethreaded, and wpas, plus a -DWOLFSSL_CURVE25519_BLINDING build for the Curve25519 path.
  • make check: 17/17 pass on --enable-all, 6/6 on opensslall, 0 failures.
  • ThreadSanitizer clean: 32 threads x 150 iterations hammering BN_rand, ECDH_compute_key, EC25519_shared_key, and RAND_bytes concurrently, 0 races.
  • Negative controls trip. Reverting the BN_rand lock reports races in Hash512_DRBG_Generate, including a thread holding globalRNGMutex racing an unlocked one. Reverting the Curve25519 lock reports 162 races via curve25519_smul_blind, confirming that lock is on a live path.

No regression test is added: a threaded test is nondeterministic and there is no ThreadSanitizer job in CI, so it would add flake without signal.

@yosuke-wolfssl yosuke-wolfssl self-assigned this Aug 4, 2026
Copilot AI lite review requested due to automatic review settings August 4, 2026 05:49

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR addresses a thread-safety issue where multiple OpenSSL-compat entry points use the process-wide globalRNG without taking its designated lock (globalRNGMutex), risking concurrent DRBG state corruption and unsafe key material generation.

Changes:

  • Add globalRNGMutex locking around wc_RNG_GenerateBlock() in wolfSSL_BN_rand() and AddSession().
  • Hold globalRNGMutex across EC_KEY->rng mutation and wc_ecc_shared_secret_ex() in wolfSSL_ECDH_compute_key(), including a missing NULL check on wolfssl_make_global_rng().
  • Lock globalRNGMutex around Curve25519 blinding usage during wolfSSL_EC25519_shared_key().

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

File Description
src/ssl_sess.c Locks globalRNGMutex while generating altSessionID via the global RNG.
src/ssl_bn.c Locks globalRNGMutex around global RNG byte generation in wolfSSL_BN_rand().
src/pk.c Locks globalRNGMutex during Curve25519 shared-secret generation when blinding is enabled.
src/pk_ec.c Locks globalRNGMutex across key->rng set/clear and ECDH shared-secret computation, adding a NULL check for the global RNG.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/pk.c

@wolfSSL-Fenrir-bot wolfSSL-Fenrir-bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fenrir Automated Review — PR #11048

Scan targets checked: wolfssl-bugs, wolfssl-src

Findings: 1
1 finding(s) posted as inline comments (see file-level comments below)

This review was generated automatically by Fenrir. Findings are non-blocking.

Comment thread src/pk_ec.c
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants