From 3a93be0b4cba0d91bf1acf403d2b41d6e7156ba6 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Mon, 26 Oct 2026 13:58:34 -0500 Subject: [PATCH 01/12] wolfcrypt/src/rng_bank.c and wolfssl/wolfcrypt/rng_bank.h: backward-compat and !WC_RNG_HAVE_NEXT_SEED tweaks. * Add backward-compat implementations of wc_rng_bank_inst_lock_{get,put}_unconditional(); * Fix gating around WC_RNG_INIT_FLAG_UPGRADE_FROM_NEXT_SEED (defined(WC_RNG_INIT_FLAG_UPGRADE_FROM_NEXT_SEED) -> defined(WC_RNG_HAVE_NEXT_SEED)) (_HAVE_NEXT_SEED is the requisite feature-sensing flag; the _INIT_FLAG_ is always defined). --- wolfcrypt/src/rng_bank.c | 33 ++++++++++++++++++++++++++++++--- wolfssl/wolfcrypt/rng_bank.h | 15 +++++++++++++++ 2 files changed, 45 insertions(+), 3 deletions(-) diff --git a/wolfcrypt/src/rng_bank.c b/wolfcrypt/src/rng_bank.c index 5ef2b6867f6..bed6a8325c2 100644 --- a/wolfcrypt/src/rng_bank.c +++ b/wolfcrypt/src/rng_bank.c @@ -156,7 +156,7 @@ WOLFSSL_API int wc_rng_bank_init_nonce( wolfSSL_Atomic_Int_Init(&ctx->inst_op_gate, WC_RNG_BANK_INST_OP_FREE); #endif ctx->flags = flags | WC_RNG_BANK_FLAG_INITED; -#ifdef WC_RNG_INIT_FLAG_UPGRADE_FROM_NEXT_SEED +#ifdef WC_RNG_HAVE_NEXT_SEED if (flags & WC_RNG_BANK_FLAG_AUTO_UPGRADE_FROM_NEXT_SEED) rng_flags |= WC_RNG_INIT_FLAG_UPGRADE_FROM_NEXT_SEED; #endif @@ -1988,7 +1988,7 @@ WOLFSSL_API int wc_rng_bank_inst_reinit( if (ret < 0) return ret; -#ifdef WC_RNG_INIT_FLAG_UPGRADE_FROM_NEXT_SEED +#ifdef WC_RNG_HAVE_NEXT_SEED if (flags & WC_RNG_BANK_FLAG_AUTO_UPGRADE_FROM_NEXT_SEED) rng_flags |= WC_RNG_INIT_FLAG_UPGRADE_FROM_NEXT_SEED; #endif @@ -2347,7 +2347,7 @@ static int rng_bank_spawn( { word32 child_init_flags = WC_RNG_INIT_FLAG_NONE; -#ifdef WC_RNG_INIT_FLAG_UPGRADE_FROM_NEXT_SEED +#ifdef WC_RNG_HAVE_NEXT_SEED if (flags & WC_RNG_BANK_FLAG_AUTO_UPGRADE_FROM_NEXT_SEED) child_init_flags |= WC_RNG_INIT_FLAG_UPGRADE_FROM_NEXT_SEED; @@ -3194,6 +3194,26 @@ WOLFSSL_TEST_VIS int wc_rng_bank_inst_lock_get_conditional( return UNEXPECTED_STATE_E; } +WOLFSSL_LOCAL int wc_rng_bank_inst_lock_get_unconditional(struct wc_rng_bank_inst *inst) +{ + WC_RNG_lock_arg_t cur_lock; + + if (inst == NULL) + return BAD_FUNC_ARG; + + cur_lock = WOLFSSL_ATOMIC_LOAD(inst->lock); + + if ((! (cur_lock & WC_RNG_LOCK_HELD)) && + (wolfSSL_Atomic_Uint_CompareExchange( + &inst->lock, &cur_lock, + cur_lock | WC_RNG_LOCK_HELD))) + { + return 0; + } + + return BUSY_E; +} + WOLFSSL_TEST_VIS int wc_rng_bank_inst_lock_put(struct wc_rng_bank_inst *inst) { WC_RNG_lock_arg_t cur_lock, new_lock; @@ -3275,6 +3295,13 @@ WOLFSSL_TEST_VIS int wc_rng_bank_inst_lock_put_conditional(struct wc_rng_bank_in return UNEXPECTED_STATE_E; } +WOLFSSL_LOCAL void wc_rng_bank_inst_lock_put_unconditional(struct wc_rng_bank_inst *inst) +{ + if (inst == NULL) + return; + (void)wolfSSL_Atomic_Uint_FetchSub(&inst->lock, WC_RNG_LOCK_HELD); +} + WOLFSSL_TEST_VIS int wc_rng_bank_inst_lock_read(struct wc_rng_bank_inst *inst, WC_RNG_lock_arg_t* state) { if ((inst == NULL) || (state == NULL)) diff --git a/wolfssl/wolfcrypt/rng_bank.h b/wolfssl/wolfcrypt/rng_bank.h index d376faf1c5e..3c298b39faa 100644 --- a/wolfssl/wolfcrypt/rng_bank.h +++ b/wolfssl/wolfcrypt/rng_bank.h @@ -443,6 +443,19 @@ WOLFSSL_API int wc_rng_new_bankref(struct wc_rng_bank *bank, WC_RNG **rng); return wc_RNG_lock_put_conditional( WC_RNG_BANK_INST_TO_RNG(inst), expect_extra_bits, 0); } + #ifdef BUILDING_WOLFSSL + static WC_INLINE int wc_rng_bank_inst_lock_get_unconditional( + struct wc_rng_bank_inst *inst) + { + return wc_RNG_lock_get_unconditional( + WC_RNG_BANK_INST_TO_RNG(inst)); + } + static WC_INLINE void wc_rng_bank_inst_lock_put_unconditional( + struct wc_rng_bank_inst *inst) + { + wc_RNG_lock_put_unconditional(WC_RNG_BANK_INST_TO_RNG(inst)); + } + #endif /* BUILDING_WOLFSSL */ static WC_INLINE int wc_rng_bank_inst_lock_read( struct wc_rng_bank_inst *inst, WC_RNG_lock_arg_t *state) { @@ -492,8 +505,10 @@ WOLFSSL_API int wc_rng_new_bankref(struct wc_rng_bank *bank, WC_RNG **rng); WOLFSSL_TEST_VIS int wc_rng_bank_inst_lock_get_conditional( struct wc_rng_bank_inst *inst, WC_RNG_lock_arg_t expected_extra_bits, WC_RNG_lock_arg_t want_extra_bits); + WOLFSSL_LOCAL int wc_rng_bank_inst_lock_get_unconditional(struct wc_rng_bank_inst *inst); WOLFSSL_TEST_VIS int wc_rng_bank_inst_lock_put(struct wc_rng_bank_inst *inst); WOLFSSL_TEST_VIS int wc_rng_bank_inst_lock_put_conditional(struct wc_rng_bank_inst *inst, WC_RNG_lock_arg_t extra_bits); + WOLFSSL_LOCAL void wc_rng_bank_inst_lock_put_unconditional(struct wc_rng_bank_inst *inst); WOLFSSL_TEST_VIS int wc_rng_bank_inst_lock_read(struct wc_rng_bank_inst *inst, WC_RNG_lock_arg_t* state); WOLFSSL_TEST_VIS int wc_rng_bank_inst_lock_set_extra(struct wc_rng_bank_inst *inst, WC_RNG_lock_arg_t extra_bits); WOLFSSL_TEST_VIS int wc_rng_bank_inst_lock_add_extra(struct wc_rng_bank_inst *inst, WC_RNG_lock_arg_t extra_bits); From 68034974f853c841096820909f081bdf51c14af4 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Mon, 26 Oct 2026 13:58:51 -0500 Subject: [PATCH 02/12] linuxkm/lkcapi_sha_glue.c: complete support for !WC_RNG_HAVE_NEXT_SEED in wc_linuxkm_entropy_daemon(). * In wc_linuxkm_entropy_daemon(), add !WC_RNG_HAVE_NEXT_SEED code paths for the bank leaf recovery pass, using wc_RNG_DRBG_Reseed_Now(); * Change the WC_RNG_HAVE_NEXT_SEED path to use wc_RNG_DRBG_NextSeedGenerate() directly to pick up lineage from the RNG-level mechanics; * Similar refactor in the "reseed pass for long-lived leaves", adding !WC_RNG_HAVE_NEXT_SEED implementation, plus use wc_RNG_entropy_needs_recovery() to determine eligibility, and change the section build gate from (WC_LINUXKM_HAVE_RNG_REGISTRY && WC_RNG_HAVE_NEXT_SEED && WC_RNG_HAVE_RBGC) to (WC_LINUXKM_HAVE_RNG_REGISTRY && WC_RNG_HAVE_ENTROPY_EPOCH). * Similar refactor in the "bank leaf bonus reseed pass"; * Add proper gating on WC_RNG_BANK_FLAG_AUTO_UPGRADE_FROM_NEXT_SEED in the wc_rng_bank_recover_inst() call. --- linuxkm/lkcapi_sha_glue.c | 284 +++++++++++++++++++++++++++++--------- 1 file changed, 222 insertions(+), 62 deletions(-) diff --git a/linuxkm/lkcapi_sha_glue.c b/linuxkm/lkcapi_sha_glue.c index 46aad8c45b1..3e310100d36 100644 --- a/linuxkm/lkcapi_sha_glue.c +++ b/linuxkm/lkcapi_sha_glue.c @@ -3102,10 +3102,11 @@ static int wc_linuxkm_entropy_daemon(void *arg) } #endif /* WC_RNG_HAVE_GLOBAL_FALLBACK_RNG */ -#if defined(WC_RNG_HAVE_NEXT_SEED) && defined(WC_RNG_HAVE_RBGC) - /* recovery pass: push RBGC seeds to all WC_RNG_LOCK_ENTROPY_INVALIDATED - * instances that need them. */ - if (root_rng != NULL) { +#ifdef WC_RNG_HAVE_RBGC + /* recovery pass: if WC_RNG_HAVE_NEXT_SEED, push RBGC seeds to all + * WC_RNG_LOCK_ENTROPY_INVALIDATED instances that need them; if not, run + * inline RBGC reseeds of all instances that need them. */ + { #ifdef WC_VERBOSE_RNG int n_rbgc_recovered = 0; int n_self_recovered = 0; @@ -3115,80 +3116,165 @@ static int wc_linuxkm_entropy_daemon(void *arg) for (i = 0; i < bank->n_rngs; i++) { WC_RNG *rng = WC_RNG_BANK_INST_TO_RNG(&bank->rngs[i]); -#ifndef WC_RNG_HAVE_ENTROPY_EPOCH + #ifndef WC_RNG_HAVE_ENTROPY_EPOCH + + /* Backward-compat check for invalidation on old-FIPS builds + * (!WC_RNG_HAVE_ENTROPY_EPOCH) + */ + + #ifdef WC_RNG_HAVE_LOCK WC_RNG_lock_arg_t rng_lock_state; + #endif + #ifdef WC_RNG_HAVE_NEXT_SEED WC_ATOMIC_INT_ARG nextSeedLen; + #endif if (wc_RNG_GetStatus(rng) != WC_DRBG_OK) { -#ifdef WC_VERBOSE_RNG + #ifdef WC_VERBOSE_RNG ++n_unhealthy; -#endif + #endif continue; } + #ifdef WC_RNG_HAVE_LOCK if (wc_RNG_lock_read(rng, &rng_lock_state) != 0) { -#ifdef WC_VERBOSE_RNG + #ifdef WC_VERBOSE_RNG ++n_unhealthy; -#endif + #endif continue; } if (! (rng_lock_state & WC_RNG_LOCK_ENTROPY_INVALIDATED)) { -#ifdef WC_VERBOSE_RNG + #ifdef WC_VERBOSE_RNG ++n_self_recovered; -#endif + #endif continue; } + #endif /* WC_RNG_HAVE_LOCK */ + #ifdef WC_RNG_HAVE_NEXT_SEED if (wc_RNG_DRBG_NextSeedCurrent(rng, &nextSeedLen) != 0) { -#ifdef WC_VERBOSE_RNG + #ifdef WC_VERBOSE_RNG ++n_unhealthy; -#endif + #endif continue; } if ((nextSeedLen == WC_DRBG_NEXT_SEED_READY) || (nextSeedLen == WC_DRBG_NEXT_SEED_CONSUMING)) { -#ifdef WC_VERBOSE_RNG + #ifdef WC_VERBOSE_RNG ++n_self_recovered; -#endif + #endif continue; } -#else + #endif /* WC_RNG_HAVE_NEXT_SEED */ + + #ifndef WC_RNG_HAVE_LOCK + /* backward-compat invalidation tracked by reseedCtr + * overloading. */ + { + wc_drbg_reseed_ctr_t this_reseedCtr; + ret = wc_RNG_DRBG_GetReseedCtr(rng, &this_reseedCtr); + if (ret != 0) { + #ifdef WC_VERBOSE_RNG + ++n_unhealthy; + #endif + continue; + } + if (this_reseedCtr < WC_RESEED_INTERVAL) { + #ifdef WC_VERBOSE_RNG + ++n_self_recovered; + #endif + continue; + } + } + #endif /* WC_RNG_HAVE_LOCK */ + + #else /* WC_RNG_HAVE_ENTROPY_EPOCH */ + ret = wc_RNG_entropy_needs_recovery(rng); if (ret != WC_NO_ERR_TRACE(NEEDS_RECOVERY_E)) { -#ifdef WC_VERBOSE_RNG + #ifdef WC_VERBOSE_RNG if (ret == 0) ++n_self_recovered; else ++n_unhealthy; -#endif + #endif continue; } -#endif + #endif /* WC_RNG_HAVE_ENTROPY_EPOCH */ + + #ifdef WC_RNG_HAVE_NEXT_SEED + /* wc_RNG_DRBG_NextSeedGenerate() uses RBGC reseed implicitly + * for reseeds of RBGC RNGs. */ + ret = wc_RNG_DRBG_NextSeedGenerate(rng, WC_DRBG_NEXT_SEED_LEN); + #else /* !WC_RNG_HAVE_NEXT_SEED */ + /* Without _NEXT_SEED support, just perform an inline reseed + * (again, implicitly RBGC). */ + #ifdef WC_RNG_HAVE_LOCK + { + unsigned long uncredited_nonce = random_get_entropy(); + ret = wc_RNG_lock_get_unconditional(rng); + if (ret == 0) { + ret = wc_RNG_DRBG_Reseed_Now( + rng, + (byte *)&uncredited_nonce, + (word32)sizeof uncredited_nonce); + wc_RNG_lock_put_unconditional(rng); + } + #ifdef WC_VERBOSE_RNG + else if (ret == WC_NO_ERR_TRACE(BUSY_E)) + ++n_busy; /* gate held by a reinit, or a claim in flight */ + else + ++n_unhealthy; + #endif + ForceZero(&uncredited_nonce, (word32)sizeof uncredited_nonce); + } + #else /* ! WC_RNG_HAVE_LOCK */ + { + unsigned long uncredited_nonce = random_get_entropy(); + ret = wc_rng_bank_inst_lock_get_unconditional(&bank->rngs[i]); + if (ret == 0) { + ret = wc_RNG_DRBG_Reseed_Now( + rng, + (byte *)&uncredited_nonce, + (word32)sizeof uncredited_nonce); + wc_rng_bank_inst_lock_put_unconditional(&bank->rngs[i]); + } + #ifdef WC_VERBOSE_RNG + else if (ret == WC_NO_ERR_TRACE(BUSY_E)) + ++n_busy; /* gate held by a reinit, or a claim in flight */ + else + ++n_unhealthy; + #endif + ForceZero(&uncredited_nonce, (word32)sizeof uncredited_nonce); + } + + #endif /* ! WC_RNG_HAVE_LOCK */ + + #endif /* !WC_RNG_HAVE_NEXT_SEED */ - ret = wc_rng_bank_next_seed_generate_rbgc(bank, i, WC_DRBG_NEXT_SEED_LEN); if (ret == 0) { -#ifdef WC_VERBOSE_RNG + #ifdef WC_VERBOSE_RNG ++n_rbgc_recovered; -#endif + #endif congested_progress = 1; progress = 1; } -#ifdef WC_VERBOSE_RNG + #ifdef WC_VERBOSE_RNG else if (ret == WC_NO_ERR_TRACE(ALREADY_E)) ++n_self_recovered; else if (ret == WC_NO_ERR_TRACE(BUSY_E)) ++n_busy; /* gate held by a reinit, or a claim in flight */ else ++n_unhealthy; -#endif + #endif } -#ifdef WC_VERBOSE_RNG + #ifdef WC_VERBOSE_RNG if (n_rbgc_recovered > 0) { pr_info("wc_linuxkm_entropy_daemon: RBGC recovery of %d/%d insts, %d self-recovered, %d skipped (busy), %d failed.\n", n_rbgc_recovered, bank->n_rngs, n_self_recovered, n_busy, n_unhealthy); } -#endif + #endif } -#endif /* WC_RNG_HAVE_NEXT_SEED && WC_RNG_HAVE_RBGC */ +#endif /* WC_RNG_HAVE_RBGC */ /* recovery pass: fix out-of-service instances. The status * peek is lockless and possibly stale -- worst case it sends a @@ -3203,8 +3289,11 @@ static int wc_linuxkm_entropy_daemon(void *arg) } ret = wc_rng_bank_recover_inst( bank, i, 0 /* timeout_secs */, - WC_LKM_BANK_RBGC_FLAG | - WC_RNG_BANK_FLAG_AUTO_UPGRADE_FROM_NEXT_SEED); + WC_LKM_BANK_RBGC_FLAG + #ifdef WC_RNG_HAVE_NEXT_SEED + | WC_RNG_BANK_FLAG_AUTO_UPGRADE_FROM_NEXT_SEED + #endif + ); if (ret == 0) { (void)wc_rng_bank_inst_flags_down( &bank->rngs[i], WC_RNG_BANK_INST_FLAG_ALREADY_WARNED); @@ -3442,15 +3531,13 @@ static int wc_linuxkm_entropy_daemon(void *arg) } #endif /* WC_RNG_HAVE_POOL */ -#if defined(WC_LINUXKM_HAVE_RNG_REGISTRY) && \ - defined(WC_RNG_HAVE_NEXT_SEED) && defined(WC_RNG_HAVE_RBGC) - /* registered-leaf pass: bank RBGC seeds from root_rng into - * long-lived leaves that are invalidated or chain-backed, so their - * next generate recovers/promotes in place - * (WC_RNG_INIT_FLAG_UPGRADE_FROM_NEXT_SEED). - * Sleepable-mutex context; entropy gathers are legal under it by - * the atomic-born exclusion rule. */ - if (root_rng != NULL) { +#if defined(WC_LINUXKM_HAVE_RNG_REGISTRY) && defined(WC_RNG_HAVE_ENTROPY_EPOCH) + /* reseed pass for long-lived leaves that need it: if _HAVE_NEXT_SEED, + * then reseed is by RBGC from parent, after which their next generate + * recovers/promotes in place (WC_RNG_INIT_FLAG_UPGRADE_FROM_NEXT_SEED); + * if !_HAVE_NEXT_SEED, reseed inline. Sleepable-mutex context; entropy + * gathers are legal under it by the atomic-born exclusion rule. */ + { WC_ATOMIC_INT_ARG needs_recovery_snapshot = WOLFSSL_ATOMIC_LOAD(wc_linuxkm_rng_registry_needs_recovery); if (needs_recovery_snapshot != 0) { @@ -3459,32 +3546,61 @@ static int wc_linuxkm_entropy_daemon(void *arg) for (obj = wc_linuxkm_rng_registry_head; obj != NULL; obj = obj->next) { - WC_RNG_lock_arg_t leaf_lock_state; if (obj->is_bank) continue; - if (wc_RNG_lock_read(obj->rng, &leaf_lock_state) != 0) - continue; - if (((leaf_lock_state & WC_RNG_LOCK_ENTROPY_INVALIDATED) || - (wc_RNG_DRBG_GetRBGCStratum(obj->rng) > 0)) -#ifdef WC_RNG_HAVE_LOCK - && (wc_RNG_lock_get(root_rng, 0) == 0) -#endif - ) + + #ifdef WC_RNG_HAVE_NEXT_SEED + /* Note, _BONUS_RESEED_INTERVAL reseeds are only possible in + * _HAVE_NEXT_SEED configurations, otherwise the entropy + * daemon's wc_RNG_lock_get_unconditional(obj->rng) will + * contend with application callers, glitching the + * application. + */ + { - if (wc_RNG_DRBG_NextSeedGenerate_RBGC(obj->rng, - root_rng, WC_DRBG_NEXT_SEED_LEN) == 0) - progress = 1; -#ifdef WC_RNG_HAVE_LOCK - ret = wc_RNG_lock_put(root_rng, 0); - if (ret == WC_NO_ERR_TRACE(NEEDS_RECOVERY_E)) { - /* the invalidation walk landed under our hold: not a - * failure, the recovery pass above owns it next turn. */ + word32 this_flags; + if (wc_RNG_GetFlags(obj->rng, &this_flags) != 0) + continue; + if (this_flags & WC_RNG_FLAG_UPGRADE_FROM_NEXT_SEED) { + wc_drbg_reseed_ctr_t this_reseedCtr; + ret = wc_RNG_DRBG_GetReseedCtr(obj->rng, &this_reseedCtr); + if ((ret != 0) || + ((this_reseedCtr < WC_LINUXKM_BONUS_RESEED_INTERVAL) && + (wc_RNG_entropy_needs_recovery(obj->rng) != WC_NO_ERR_TRACE(NEEDS_RECOVERY_E)))) + { + continue; + } } - else if (ret != 0) { - pr_crit_ratelimited( - "wc_entropyd: root_rng unlock failed: %d\n", ret); + else { + if (wc_RNG_entropy_needs_recovery(obj->rng) != WC_NO_ERR_TRACE(NEEDS_RECOVERY_E)) { + continue; + } } -#endif + } + /* If obj->rng has a parent, wc_RNG_DRBG_NextSeedGenerate() + * is implicitly RBGC. */ + ret = wc_RNG_DRBG_NextSeedGenerate(obj->rng, WC_DRBG_NEXT_SEED_LEN); + #else /* !WC_RNG_HAVE_NEXT_SEED */ + if (wc_RNG_entropy_needs_recovery(obj->rng) != WC_NO_ERR_TRACE(NEEDS_RECOVERY_E)) + continue; + /* Without _NEXT_SEED support, just perform an inline reseed + * (again, implicitly RBGC). */ + { + unsigned long uncredited_nonce = random_get_entropy(); + ret = wc_RNG_lock_get_unconditional(obj->rng); + if (ret == 0) { + ret = wc_RNG_DRBG_Reseed_Now( + obj->rng, + (byte *)&uncredited_nonce, + (word32)sizeof uncredited_nonce); + wc_RNG_lock_put_unconditional(obj->rng); + } + ForceZero(&uncredited_nonce, (word32)sizeof uncredited_nonce); + } + #endif /* !WC_RNG_HAVE_NEXT_SEED */ + if (ret == 0) { + progress = 1; + congested_progress = 1; } } mutex_unlock(&wc_linuxkm_rng_registry_mutex); @@ -3495,7 +3611,7 @@ static int wc_linuxkm_entropy_daemon(void *arg) &needs_recovery_snapshot, 0); } } -#endif /* WC_LINUXKM_HAVE_RNG_REGISTRY && WC_RNG_HAVE_NEXT_SEED && WC_RNG_HAVE_RBGC */ +#endif /* WC_LINUXKM_HAVE_RNG_REGISTRY && WC_RNG_HAVE_ENTROPY_EPOCH */ /* if we're coping with congestion hits, continue here, don't bog down * in primary seed ops. */ @@ -3503,7 +3619,9 @@ static int wc_linuxkm_entropy_daemon(void *arg) goto next_pass; #ifdef WC_RNG_HAVE_NEXT_SEED - /* seed banking pass: one gather granule per instance per turn. */ + /* seed banking pass: one gather granule per instance per turn. + * Generators will reseed opportunistically, using the banked seed, at + * the WC_LINUXKM_BONUS_RESEED_INTERVAL. */ for (i = 0; i < bank->n_rngs; i++) { ret = wc_rng_bank_next_seed_generate( @@ -3533,7 +3651,49 @@ static int wc_linuxkm_entropy_daemon(void *arg) "returned %d\n", i, ret); } } -#endif /* WC_RNG_HAVE_NEXT_SEED */ +#elif defined(WC_RNG_HAVE_LOCK) + /* !_HAVE_NEXT_SEED: bank leaf bonus reseed pass. */ + + for (i = 0; i < bank->n_rngs; i++) { + WC_RNG *leaf = WC_RNG_BANK_INST_TO_RNG(&bank->rngs[i]); + wc_drbg_reseed_ctr_t cur_counter = 0; + ret = wc_RNG_DRBG_GetReseedCtr(leaf, &cur_counter); + if ((ret == 0) && + (cur_counter >= WC_LINUXKM_BONUS_RESEED_INTERVAL) && + (wc_RNG_lock_get_unconditional(leaf) == 0)) + { + unsigned long uncredited_nonce = random_get_entropy(); + ret = wc_RNG_DRBG_Reseed_Now(leaf, + (byte *)&uncredited_nonce, + (word32)sizeof uncredited_nonce); + ForceZero(&uncredited_nonce, (word32)sizeof uncredited_nonce); + if (ret == 0) + progress = 1; + wc_RNG_lock_put_unconditional(leaf); + } + } +#else /* !WC_RNG_HAVE_NEXT_SEED && !WC_RNG_HAVE_LOCK */ + /* !_HAVE_NEXT_SEED && !_HAVE_LOCK: bank leaf bonus reseed pass. */ + + for (i = 0; i < bank->n_rngs; i++) { + WC_RNG *leaf = WC_RNG_BANK_INST_TO_RNG(&bank->rngs[i]); + wc_drbg_reseed_ctr_t cur_counter = 0; + ret = wc_RNG_DRBG_GetReseedCtr(leaf, &cur_counter); + if ((ret == 0) && + (cur_counter >= WC_LINUXKM_BONUS_RESEED_INTERVAL) && + (wc_rng_bank_inst_lock_get_unconditional(&bank->rngs[i]) == 0)) + { + unsigned long uncredited_nonce = random_get_entropy(); + ret = wc_RNG_DRBG_Reseed_Now(leaf, + (byte *)&uncredited_nonce, + (word32)sizeof uncredited_nonce); + ForceZero(&uncredited_nonce, (word32)sizeof uncredited_nonce); + if (ret == 0) + progress = 1; + wc_rng_bank_inst_lock_put_unconditional(&bank->rngs[i]); + } + } +#endif /* !WC_RNG_HAVE_NEXT_SEED && !WC_RNG_HAVE_LOCK */ next_pass: From 92749b305cf5eef38cde0380cb68a6702a99e903 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Mon, 26 Oct 2026 23:50:48 -0500 Subject: [PATCH 03/12] wolfcrypt/src/rng_bank.c, wolfssl/wolfcrypt/rng_bank.h, doc/dox_comments/header_files/rng_bank.h: add wc_rng_bank_root_rng_reseed_now() and wc_rng_bank_root_rng_reseed_if_due(). Serialized forced reseed of the bank root, by whatever method the configuration uses to serialize root access: with WC_RNG_HAVE_LOCK, the root's lease is taken by unconditional try-lock and held across the reseed; without it, the inst-op gate is claimed as WC_RNG_BANK_INST_OP_DAEMON per the holder contract (try-once, fail-fast, atomic-legal), excluding member reinstantiations chaining from the root, entropy-invalidation walks, and root reinstantiation. Either refusal returns BUSY_E with nothing touched. The threshold form reads the reseed counter under the same hold as the reseed -- no TOCTOU, and on legacy FIPS boundaries no unexcluded dereference of internal DRBG state -- and returns ALREADY_E (steady state, not an error) below threshold; threshold 0 reseeds unconditionally. Without the lock facility the saturated counter is the invalidation walk's only root-side record, so a threshold of WC_RESEED_INTERVAL implements deterministic post-invalidation root recovery. --- doc/dox_comments/header_files/rng_bank.h | 60 ++++++++++++++++++ wolfcrypt/src/rng_bank.c | 77 ++++++++++++++++++++++++ wolfssl/wolfcrypt/rng_bank.h | 16 +++++ 3 files changed, 153 insertions(+) diff --git a/doc/dox_comments/header_files/rng_bank.h b/doc/dox_comments/header_files/rng_bank.h index 4fa4fb0e980..40f8da1b927 100644 --- a/doc/dox_comments/header_files/rng_bank.h +++ b/doc/dox_comments/header_files/rng_bank.h @@ -944,6 +944,66 @@ int wc_rng_bank_root_rng_reinit(struct wc_rng_bank *bank, const byte *perso, word32 persoSz, word32 flags); +/*! + \ingroup RNGBank + + \brief Force an immediate reseed of the bank's root RNG, serialized + against other root touchers. With WC_RNG_HAVE_LOCK, the root's lease + is taken by unconditional try-lock and held across the reseed. + Without it, the serializing inst-op gate is claimed by try-once + (fail-fast, atomic-legal), excluding member reinstantiations chaining + from the root, entropy-invalidation walks, and root reinstantiation + for the reseed's span. + + \return 0 Success + \return BAD_FUNC_ARG bank is null. + \return BUSY_E The root's lease or the inst-op gate is held; nothing + was touched, retry later. + \return negative wc_RNG_DRBG_Reseed_Now() failure. + + \param bank The bank whose root to reseed. + \param nonce Optional reseed nonce (may be null). + \param nonceSz Length of nonce in bytes. + + \sa wc_rng_bank_root_rng_reinit + \sa wc_RNG_DRBG_Reseed_Now +*/ +int wc_rng_bank_root_rng_reseed_now(struct wc_rng_bank *bank, + const byte *nonce, word32 nonceSz); + +/*! + \ingroup RNGBank + + \brief Threshold form of wc_rng_bank_root_rng_reseed_now(): reseed the + bank's root RNG only when its reseed counter has reached threshold, + with the counter read and the reseed performed under a single hold of + the configuration's root serialization (the root's lease with + WC_RNG_HAVE_LOCK, else the inst-op gate). Without the lock facility + the saturated counter is the invalidation walk's only root-side + record, so a threshold of WC_RESEED_INTERVAL makes this the root's + deterministic post-invalidation recovery. + + \return 0 Success + \return BAD_FUNC_ARG bank is null. + \return ALREADY_E The counter is below threshold: nothing due. + \return BUSY_E The root's lease or the inst-op gate is held; nothing + was touched, retry later. + \return negative wc_RNG_DRBG_GetReseedCtr() or + wc_RNG_DRBG_Reseed_Now() failure. + + \param bank The bank whose root to reseed. + \param nonce Optional reseed nonce (may be null). + \param nonceSz Length of nonce in bytes. + \param threshold Reseed-counter value at which a reseed is due; 0 + reseeds unconditionally. + + \sa wc_rng_bank_root_rng_reseed_now + \sa wc_RNG_DRBG_GetReseedCtr +*/ +int wc_rng_bank_root_rng_reseed_if_due(struct wc_rng_bank *bank, + const byte *nonce, word32 nonceSz, + wc_drbg_reseed_ctr_t threshold); + /*! \ingroup Random diff --git a/wolfcrypt/src/rng_bank.c b/wolfcrypt/src/rng_bank.c index bed6a8325c2..c5e9a5ce1c7 100644 --- a/wolfcrypt/src/rng_bank.c +++ b/wolfcrypt/src/rng_bank.c @@ -1632,6 +1632,83 @@ WOLFSSL_API int wc_rng_bank_root_rng_reinit(struct wc_rng_bank *bank, ); } +#ifdef HAVE_HASHDRBG +/* Forced reseed of the bank root, serialized however the configuration + * serializes root access. With WC_RNG_HAVE_LOCK, the root's lease is + * taken by unconditional try-lock and held across the reseed. Without + * it, every other root toucher serializes on the inst-op gate -- member + * reinstantiation chaining from the root (wc_rng_bank_inst_reinit()), + * the invalidation walk's counter saturation + * (wc_rng_bank_invalidate_entropy()), and root reinstantiation + * (wc_rng_bank_root_rng_reinit()) -- so the reseed holds the gate, + * claimed per the holder contract try-once, fail-fast (atomic-legal). + * Either way a refusal (BUSY_E) means nothing was touched, and the + * caller's next pass retries. + * + * The threshold form reseeds only when the root's reseed counter has + * reached threshold, with the counter read under the same hold as the + * reseed -- no TOCTOU, and on legacy boundaries (where the read + * dereferences internal DRBG state) no unexcluded deref. A below- + * threshold counter returns ALREADY_E: steady state, not an error. + * threshold == 0 reseeds unconditionally. Without the lock facility + * the saturated counter is the invalidation walk's only root-side + * record, so threshold == WC_RESEED_INTERVAL makes this the root's + * deterministic post-invalidation recovery. */ +WOLFSSL_API int wc_rng_bank_root_rng_reseed_if_due( + struct wc_rng_bank *bank, const byte *nonce, word32 nonceSz, + wc_drbg_reseed_ctr_t threshold) +{ + int ret; + + if (bank == NULL) + return BAD_FUNC_ARG; + +#ifdef WC_RNG_HAVE_LOCK + ret = wc_RNG_lock_get_unconditional(&bank->root_rng); + if (ret != 0) + return ret; +#else /* WC_RNG_BANK_HAVE_INST_OP_GATE */ + { + WC_ATOMIC_INT_ARG expected = WC_RNG_BANK_INST_OP_FREE; + if (! wolfSSL_Atomic_Int_CompareExchange(&bank->inst_op_gate, + &expected, + WC_RNG_BANK_INST_OP_DAEMON)) + { + return BUSY_E; + } + } +#endif + + if (threshold > 0) { + wc_drbg_reseed_ctr_t cur_ctr = 0; + ret = wc_RNG_DRBG_GetReseedCtr(&bank->root_rng, &cur_ctr); + if ((ret == 0) && (cur_ctr < threshold)) + ret = ALREADY_E; + } + else { + ret = 0; + } + + if (ret == 0) + ret = wc_RNG_DRBG_Reseed_Now(&bank->root_rng, nonce, nonceSz); + +#ifdef WC_RNG_HAVE_LOCK + wc_RNG_lock_put_unconditional(&bank->root_rng); +#else /* WC_RNG_BANK_HAVE_INST_OP_GATE */ + WOLFSSL_ATOMIC_STORE(bank->inst_op_gate, WC_RNG_BANK_INST_OP_FREE); +#endif + + return ret; +} + +WOLFSSL_API int wc_rng_bank_root_rng_reseed_now(struct wc_rng_bank *bank, + const byte *nonce, + word32 nonceSz) +{ + return wc_rng_bank_root_rng_reseed_if_due(bank, nonce, nonceSz, 0); +} +#endif /* HAVE_HASHDRBG */ + #endif /* WC_RNG_BANK_HAVE_ROOT_RNG */ #ifdef WC_HAVE_RNG_BANKREF diff --git a/wolfssl/wolfcrypt/rng_bank.h b/wolfssl/wolfcrypt/rng_bank.h index 3c298b39faa..1d28a2009df 100644 --- a/wolfssl/wolfcrypt/rng_bank.h +++ b/wolfssl/wolfcrypt/rng_bank.h @@ -402,6 +402,11 @@ WOLFSSL_API int wc_rng_bank_root_rng_reinit(struct wc_rng_bank *bank, const byte *nonce, word32 nonceSz, const byte *perso, word32 persoSz, word32 flags); +#ifdef HAVE_HASHDRBG +WOLFSSL_API int wc_rng_bank_root_rng_reseed_now(struct wc_rng_bank *bank, + const byte *nonce, + word32 nonceSz); +#endif #endif WOLFSSL_API int wc_rng_bank_register_free_hook(struct wc_rng_bank *bank, @@ -576,6 +581,17 @@ WOLFSSL_API int wc_rng_new_bankref(struct wc_rng_bank *bank, WC_RNG **rng); #endif #endif /* HAVE_FIPS && FIPS_VERSION3_LT(7,0,0) */ +#if defined(WC_RNG_BANK_HAVE_ROOT_RNG) && defined(HAVE_HASHDRBG) +/* Threshold form of wc_rng_bank_root_rng_reseed_now(): under the same + * serialization, reseed only when the root's reseed counter has reached + * threshold (0 means unconditional; a below-threshold counter returns + * ALREADY_E). Declared here, after wc_drbg_reseed_ctr_t is defined on + * all boundaries. */ +WOLFSSL_API int wc_rng_bank_root_rng_reseed_if_due( + struct wc_rng_bank *bank, const byte *nonce, word32 nonceSz, + wc_drbg_reseed_ctr_t threshold); +#endif + #ifdef WC_RNG_DEBUG_STATS WOLFSSL_API int wc_rng_bank_debug_stats_snap(struct wc_rng_debug_stats_snapshot *s, struct wc_rng_bank *bank); From d39321d63448d439b9df46cf0cd66705b8bd26b6 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Mon, 26 Oct 2026 23:56:42 -0500 Subject: [PATCH 04/12] linuxkm/lkcapi_sha_glue.c: fixes for wc_linuxkm_entropy_daemon() and wc_linuxkm_vmgenid_poll() in !WC_RNG_HAVE_NEXT_SEED and legacy-FIPS configurations. legacy-FIPS (!WC_RNG_HAVE_ENTROPY_EPOCH) bank-leaf sensing and recovery: * recovery pass: sense invalidation directly on the wc_rng_bank_inst.lock stand-in latch via wc_rng_bank_inst_lock_read(), replacing the reseedCtr-overload check. The latch is the authoritative record -- invalidation during a consumer's hold leaves the counter unsaturated -- and the one-word read eliminates the unexcluded dereference of internal DRBG state in wc_RNG_DRBG_GetReseedCtr() on legacy boundaries. * recovery and bonus passes: perform the reseed with wc_rng_bank_inst_reseed_now() under wc_rng_bank_inst_lock_get_unconditional(), so a successful reseed clears the inst latch (recovery_exit) rather than stranding an instance that ordinary checkouts refuse, and the try-lock excludes in-flight holders and reinits. In the bonus pass, take the inst lock before wc_RNG_DRBG_GetReseedCtr(), for the same legacy-dereference reason, and release it on every path. * add #error tripwires for WC_RNG_HAVE_LOCK or WC_RNG_HAVE_NEXT_SEED without WC_RNG_HAVE_ENTROPY_EPOCH (unreachable kernel module configurations) and drop the resulting dead arms, unifying the recovery action across epoch and legacy configurations. Drop double-counted verbose accounting in the recovery action (the shared chain below covers all return codes). registry-leaf pass (!WC_RNG_HAVE_NEXT_SEED): * inhibit inline reseeds of leaves that aren't lock-enforcing: a leaf created without WC_RNG_INIT_FLAG_LOCK_REQUIRED has owners that never consult WC_RNG.lock, so wc_RNG_lock_get_unconditional() excludes nobody and the daemon's inline reseed would update DRBG state concurrently with the owner's generates. Sense WC_RNG_LOCK_REQUIRED via wc_RNG_lock_read() and skip the leaf otherwise. wc_RNG_entropy_needs_recovery() has already propagated the invalidation event by the time the gate is evaluated, so a skipped leaf recovers in its owner's next generate; only the daemon-side inline reseed is inhibited. Bank leaves are unaffected (always _LOCK_REQUIRED or the wc_rng_bank_inst.lock stand-in equivalent, and accessed only through the bank checkout API), and WC_RNG_HAVE_NEXT_SEED configurations synchronize through the WC_RNG.nextSeedLen aperture instead. root_rng recovery and serialization in !WC_RNG_HAVE_LOCK configurations: * the loop-head root recovery block gains an #else arm: a bank root recovers via wc_rng_bank_root_rng_reseed_if_due() at threshold WC_RESEED_INTERVAL -- the invalidation walk's saturation is the only root-side record in this configuration, and the threshold form reads and acts under a single hold of the inst-op gate -- bounding recovery latency at one daemon nap, as in the lock-facility arm. A memberless root checks wc_RNG_entropy_needs_recovery() when WC_RNG_HAVE_ENTROPY_EPOCH (the global epoch detects without notification); without the epoch facility nothing can reach a daemon-private root, and the vmgenid fold plus the periodic countdown reseed are its complete freshness regime (now documented). * the periodic root reseed routes through wc_rng_bank_root_rng_reseed_now() on lock-less bank roots, excluding member reinstantiations that chain from the root, and now passes the cycle-counter nonce on all boundaries, replacing the pre-v7 NULL arm: the nonce lands natively in the credited reseed on current code and the 5.2.4 backport, and as a trailing uncredited stir in the legacy-boundary fallback. * wc_linuxkm_vmgenid_poll(): the generation-id fold routes through wc_rng_bank_root_rng_reseed_now() whenever the bank has a root, retiring the hand-rolled lease bracket; the signature takes the bank when WC_RNG_BANK_HAVE_ROOT_RNG. The memberless arm is documented as daemon-private with no reachable invalidation events. * vmgenid poll teardown is no longer gated on root_rng != NULL: the poll runs (and maps the ACPI generation-id page) regardless of the root's fate, so the previous gating leaked the mapping on the continuing-without-root path. * note added at the loop-head needs_recovery() check: any nonzero verdict deliberately routes to recovery -- a dire code is exactly when the reinit arm is wanted. pass accounting and tidying: * the congestion-triggered pre-bank pass sets congested_progress only on success: a persistently failing wc_rng_bank_next_seed_generate_rbgc() no longer suppresses the banking, bonus, and periodic reseed passes indefinitely. * harmonize the global-fallback bonus threshold to >= WC_LINUXKM_BONUS_RESEED_INTERVAL, matching the leaf passes. * note that wc_RNG_global_fallback_get() returns the pointer to the static WC_RNG, not a hold; fix a doubled space in its error message. * normalize nested preprocessor-directive indentation in the root recovery block; label the bonus passes by boundary; misc comment fixes. --- linuxkm/lkcapi_sha_glue.c | 327 +++++++++++++++++++++++--------------- 1 file changed, 201 insertions(+), 126 deletions(-) diff --git a/linuxkm/lkcapi_sha_glue.c b/linuxkm/lkcapi_sha_glue.c index 3e310100d36..00b512ff905 100644 --- a/linuxkm/lkcapi_sha_glue.c +++ b/linuxkm/lkcapi_sha_glue.c @@ -2739,8 +2739,13 @@ static acpi_status wc_linuxkm_vmgenid_acpi_cb(acpi_handle handle, u32 depth, return AE_OK; } -static void wc_linuxkm_vmgenid_poll(struct wc_linuxkm_vmgenid_poll_state *st, - WC_RNG *local_root) +static void wc_linuxkm_vmgenid_poll(struct wc_linuxkm_vmgenid_poll_state *st + #ifdef WC_RNG_BANK_HAVE_ROOT_RNG + , struct wc_rng_bank *bank + #else + , WC_RNG *local_root + #endif + ) { if (st->state == 0) { /* one-time discovery, in daemon task context. ACPICA uppercases @@ -2782,18 +2787,23 @@ static void wc_linuxkm_vmgenid_poll(struct wc_linuxkm_vmgenid_poll_state *st, * unconditionally as in the loop-head arm (the standing latch is * the point); if the lease can't be had this instant, the loop * head recovers the root on this same pass, without the fold. */ + + #ifdef WC_RNG_BANK_HAVE_ROOT_RNG + (void)wc_rng_bank_root_rng_reseed_now(bank, + (const byte *)st->last, 16); + #else if (local_root != NULL) { -#ifdef WC_RNG_HAVE_LOCK - if (wc_RNG_lock_get_unconditional(local_root) == 0) { - (void)wc_RNG_DRBG_Reseed_Now(local_root, - (const byte *)st->last, 16); - wc_RNG_lock_put_unconditional(local_root); - } -#else + /* The memberless root is daemon-private -- its only reference + * is on this thread's stack, and the invalidation walk's root + * arm doesn't exist in this configuration -- so nothing + * concurrent can touch it, and unlocked access is trivially + * safe. The flip side: no invalidation event can ever reach + * it, so this fold and the periodic countdown reseed are its + * entire freshness regime. */ (void)wc_RNG_DRBG_Reseed_Now(local_root, (const byte *)st->last, 16); -#endif } + #endif } } @@ -2905,9 +2915,11 @@ static int wc_linuxkm_entropy_daemon(void *arg) #ifdef WC_RNG_HAVE_GLOBAL_FALLBACK_RNG if (bank->flags & WC_RNG_BANK_FLAG_DEFAULT_BANK) { + /* note wc_RNG_global_fallback_get() just gets the pointer to the static + * WC_RNG, not a hold. */ ret = wc_RNG_global_fallback_get(&global_fallback_rng); if (ret != 0) { - pr_err("wc_linuxkm_entropy_daemon: wc_RNG_global_fallback_get() failed with " + pr_err("wc_linuxkm_entropy_daemon: wc_RNG_global_fallback_get() failed with " "code %d -- continuing without.\n", ret); } } @@ -2943,7 +2955,13 @@ static int wc_linuxkm_entropy_daemon(void *arg) int congested_progress = 0; #ifdef WC_LINUXKM_VMGENID_POLL - wc_linuxkm_vmgenid_poll(&vmgenid_poll_state, root_rng); + wc_linuxkm_vmgenid_poll(&vmgenid_poll_state + #ifdef WC_RNG_BANK_HAVE_ROOT_RNG + , bank + #else + , root_rng + #endif + ); #endif #ifdef WC_RNG_HAVE_LOCK @@ -2953,7 +2971,12 @@ static int wc_linuxkm_entropy_daemon(void *arg) * epoch stamp (the reseed counter is the SP 800-90A budget only, * and is never saturated here) -- and * wc_RNG_entropy_needs_recovery() reads both; its verdict is the - * authoritative signal and this is the authoritative response. */ + * authoritative signal and this is the authoritative response. + * + * Note, any nonzero retval from wc_RNG_entropy_needs_recovery() + * triggers recovery -- a dire code here means the root needs the + * reinit-based recovery below. + */ if ((root_rng != NULL) && ((ret = wc_RNG_entropy_needs_recovery(root_rng)) != 0)) { unsigned long uncredited_nonce = random_get_entropy(); int inv_ret = wc_RNG_lock_get_unconditional(root_rng); @@ -2969,11 +2992,11 @@ static int wc_linuxkm_entropy_daemon(void *arg) goto next_pass; } -#ifdef WC_VERBOSE_RNG + #ifdef WC_VERBOSE_RNG pr_info("wc_linuxkm_entropy_daemon: starting root recovery reseed (%d).\n", ret); -#endif + #endif -#if !defined(WOLFSSL_USE_SAVE_VECTOR_REGISTERS) || defined(WC_SVR_USE_NATIVE_REG_BUFS) + #if !defined(WOLFSSL_USE_SAVE_VECTOR_REGISTERS) || defined(WC_SVR_USE_NATIVE_REG_BUFS) /* all RNGs depend on the default root reseed -- try a pass with * preemption inhibited to maximize our chances. */ if (bank->flags & WC_RNG_BANK_FLAG_DEFAULT_BANK) { @@ -2984,7 +3007,7 @@ static int wc_linuxkm_entropy_daemon(void *arg) preempt_enable(); } if ((! (bank->flags & WC_RNG_BANK_FLAG_DEFAULT_BANK)) || (inv_ret != 0)) -#endif + #endif { inv_ret = wc_RNG_DRBG_Reseed_Now( root_rng, @@ -2994,9 +3017,9 @@ static int wc_linuxkm_entropy_daemon(void *arg) wc_RNG_lock_put_unconditional(root_rng); if (inv_ret == 0) { -#ifdef WC_VERBOSE_RNG + #ifdef WC_VERBOSE_RNG pr_info("wc_linuxkm_entropy_daemon: finished root recovery reseed.\n"); -#endif + #endif progress = 1; } else { @@ -3028,7 +3051,7 @@ static int wc_linuxkm_entropy_daemon(void *arg) "failed (%d) -- attempting reinit\n", inv_ret); } -#ifdef WC_RNG_BANK_HAVE_ROOT_RNG + #ifdef WC_RNG_BANK_HAVE_ROOT_RNG /* Gate-bracketed free+reinstantiate: the inst-op gate * excludes concurrent invalidation walkers, which * dereference the root's DRBG state, for the span of @@ -3037,14 +3060,14 @@ static int wc_linuxkm_entropy_daemon(void *arg) bank, (byte *)&uncredited_nonce, (word32)sizeof uncredited_nonce, NULL, 0, 0); -#else + #else /* Standalone (memberless) root: daemon-private, never * reachable by the invalidation walk -- no gating * needed or possible. */ inv_ret = wc_FreeRng(root_rng); if (inv_ret == 0) inv_ret = wc_InitRng(root_rng); -#endif + #endif if (inv_ret == 0) { progress = 1; if (bank->flags & WC_RNG_BANK_FLAG_DEFAULT_BANK) { @@ -3082,7 +3105,78 @@ static int wc_linuxkm_entropy_daemon(void *arg) ForceZero(&uncredited_nonce, (word32)sizeof uncredited_nonce); } -#endif +#else /* !WC_RNG_HAVE_LOCK */ + /* Deterministic root recovery without the lock facility. + * + * Bank root: the invalidation walk's only root-side record in + * this configuration is the saturated reseed counter + * (wc_rng_bank_invalidate_entropy() -> + * wc_RNG_DRBG_ScheduleReseed()), so recovery is + * threshold-driven: the threshold form of the root reseed + * service reads the counter and reseeds under a single hold of + * the inst-op gate. Ordinary wear-out (a counter at + * WC_RESEED_INTERVAL with no event) is absorbed by the same + * remedy. + * + * Memberless root: no event can reach it (daemon-private; see + * wc_linuxkm_vmgenid_poll()), but when WC_RNG_HAVE_ENTROPY_EPOCH + * is available the global epoch is detection without + * notification, so check it and reseed on staleness. Without + * the epoch facility, the vmgenid fold and the periodic + * countdown reseed are the complete freshness regime, by + * design. */ + #ifdef WC_RNG_BANK_HAVE_ROOT_RNG + if (root_rng != NULL) { + unsigned long uncredited_nonce = random_get_entropy(); + ret = wc_rng_bank_root_rng_reseed_if_due( + bank, + (byte *)&uncredited_nonce, (word32)sizeof uncredited_nonce, + (wc_drbg_reseed_ctr_t)WC_RESEED_INTERVAL); + ForceZero(&uncredited_nonce, (word32)sizeof uncredited_nonce); + if (ret == 0) { + #ifdef WC_VERBOSE_RNG + pr_info("wc_linuxkm_entropy_daemon: root recovery reseed " + "(saturated counter).\n"); + #endif + progress = 1; + } + else if (WC_LKM_RNG_ERR_IS_HEALTH(ret)) { + pr_warn_ratelimited( + "wc_entropyd: root_rng recovery reseed failed the seed " + "health test: %d -- entropy source suspect\n", ret); + } + else if ((ret != WC_NO_ERR_TRACE(ALREADY_E)) && + (ret != WC_NO_ERR_TRACE(WRONG_TYPE_OBJECT_E)) && + (! WC_RNG_ERR_IS_TRANSIENT(ret))) + { + /* ALREADY_E: not due -- the steady state. + * WRONG_TYPE_OBJECT_E: no DRBG behind the root (RDRAND et + * al.) -- permanently nothing to do. Transient codes (the + * gate held) are one pass away. */ + pr_err_ratelimited( + "wc_entropyd: root_rng recovery reseed failed: %d\n", + ret); + } + } + #elif defined(WC_RNG_HAVE_ENTROPY_EPOCH) + if ((root_rng != NULL) && + (wc_RNG_entropy_needs_recovery(root_rng) != 0)) + { + unsigned long uncredited_nonce = random_get_entropy(); + ret = wc_RNG_DRBG_Reseed_Now( + root_rng, + (byte *)&uncredited_nonce, (word32)sizeof uncredited_nonce); + ForceZero(&uncredited_nonce, (word32)sizeof uncredited_nonce); + if (ret == 0) + progress = 1; + else if (! WC_RNG_ERR_IS_TRANSIENT(ret)) { + pr_err_ratelimited( + "wc_entropyd: root_rng recovery reseed failed: %d\n", + ret); + } + } + #endif /* WC_RNG_BANK_HAVE_ROOT_RNG / WC_RNG_HAVE_ENTROPY_EPOCH */ +#endif /* WC_RNG_HAVE_LOCK */ #ifdef WC_RNG_HAVE_GLOBAL_FALLBACK_RNG if ((global_fallback_rng != NULL) && @@ -3117,75 +3211,40 @@ static int wc_linuxkm_entropy_daemon(void *arg) WC_RNG *rng = WC_RNG_BANK_INST_TO_RNG(&bank->rngs[i]); #ifndef WC_RNG_HAVE_ENTROPY_EPOCH - /* Backward-compat check for invalidation on old-FIPS builds * (!WC_RNG_HAVE_ENTROPY_EPOCH) */ + /* These configuration combinations aren't reachable, but make sure. */ #ifdef WC_RNG_HAVE_LOCK - WC_RNG_lock_arg_t rng_lock_state; + #error WC_RNG_HAVE_LOCK without WC_RNG_HAVE_ENTROPY_EPOCH is not a supported kernel module configuration. #endif #ifdef WC_RNG_HAVE_NEXT_SEED - WC_ATOMIC_INT_ARG nextSeedLen; + #error WC_RNG_HAVE_NEXT_SEED without WC_RNG_HAVE_ENTROPY_EPOCH is not a supported kernel module configuration. #endif + WC_RNG_lock_arg_t rng_inst_lock_state; + if (wc_RNG_GetStatus(rng) != WC_DRBG_OK) { #ifdef WC_VERBOSE_RNG ++n_unhealthy; #endif continue; } - #ifdef WC_RNG_HAVE_LOCK - if (wc_RNG_lock_read(rng, &rng_lock_state) != 0) { - #ifdef WC_VERBOSE_RNG - ++n_unhealthy; - #endif - continue; - } - if (! (rng_lock_state & WC_RNG_LOCK_ENTROPY_INVALIDATED)) { - #ifdef WC_VERBOSE_RNG - ++n_self_recovered; - #endif - continue; - } - #endif /* WC_RNG_HAVE_LOCK */ - #ifdef WC_RNG_HAVE_NEXT_SEED - if (wc_RNG_DRBG_NextSeedCurrent(rng, &nextSeedLen) != 0) { - #ifdef WC_VERBOSE_RNG + + if (wc_rng_bank_inst_lock_read(&bank->rngs[i], &rng_inst_lock_state) != 0) { + #ifdef WC_VERBOSE_RNG ++n_unhealthy; - #endif + #endif continue; } - if ((nextSeedLen == WC_DRBG_NEXT_SEED_READY) || - (nextSeedLen == WC_DRBG_NEXT_SEED_CONSUMING)) - { - #ifdef WC_VERBOSE_RNG + + if (! (rng_inst_lock_state & WC_RNG_LOCK_ENTROPY_INVALIDATED)) { + #ifdef WC_VERBOSE_RNG ++n_self_recovered; - #endif + #endif continue; } - #endif /* WC_RNG_HAVE_NEXT_SEED */ - - #ifndef WC_RNG_HAVE_LOCK - /* backward-compat invalidation tracked by reseedCtr - * overloading. */ - { - wc_drbg_reseed_ctr_t this_reseedCtr; - ret = wc_RNG_DRBG_GetReseedCtr(rng, &this_reseedCtr); - if (ret != 0) { - #ifdef WC_VERBOSE_RNG - ++n_unhealthy; - #endif - continue; - } - if (this_reseedCtr < WC_RESEED_INTERVAL) { - #ifdef WC_VERBOSE_RNG - ++n_self_recovered; - #endif - continue; - } - } - #endif /* WC_RNG_HAVE_LOCK */ #else /* WC_RNG_HAVE_ENTROPY_EPOCH */ @@ -3208,47 +3267,18 @@ static int wc_linuxkm_entropy_daemon(void *arg) #else /* !WC_RNG_HAVE_NEXT_SEED */ /* Without _NEXT_SEED support, just perform an inline reseed * (again, implicitly RBGC). */ - #ifdef WC_RNG_HAVE_LOCK - { - unsigned long uncredited_nonce = random_get_entropy(); - ret = wc_RNG_lock_get_unconditional(rng); - if (ret == 0) { - ret = wc_RNG_DRBG_Reseed_Now( - rng, - (byte *)&uncredited_nonce, - (word32)sizeof uncredited_nonce); - wc_RNG_lock_put_unconditional(rng); - } - #ifdef WC_VERBOSE_RNG - else if (ret == WC_NO_ERR_TRACE(BUSY_E)) - ++n_busy; /* gate held by a reinit, or a claim in flight */ - else - ++n_unhealthy; - #endif - ForceZero(&uncredited_nonce, (word32)sizeof uncredited_nonce); - } - #else /* ! WC_RNG_HAVE_LOCK */ { unsigned long uncredited_nonce = random_get_entropy(); ret = wc_rng_bank_inst_lock_get_unconditional(&bank->rngs[i]); if (ret == 0) { - ret = wc_RNG_DRBG_Reseed_Now( - rng, + ret = wc_rng_bank_inst_reseed_now( + &bank->rngs[i], (byte *)&uncredited_nonce, (word32)sizeof uncredited_nonce); wc_rng_bank_inst_lock_put_unconditional(&bank->rngs[i]); } - #ifdef WC_VERBOSE_RNG - else if (ret == WC_NO_ERR_TRACE(BUSY_E)) - ++n_busy; /* gate held by a reinit, or a claim in flight */ - else - ++n_unhealthy; - #endif ForceZero(&uncredited_nonce, (word32)sizeof uncredited_nonce); } - - #endif /* ! WC_RNG_HAVE_LOCK */ - #endif /* !WC_RNG_HAVE_NEXT_SEED */ if (ret == 0) { @@ -3347,9 +3377,10 @@ static int wc_linuxkm_entropy_daemon(void *arg) { ret = wc_rng_bank_next_seed_generate_rbgc( bank, i, WC_DRBG_NEXT_SEED_LEN); - congested_progress = 1; - if (ret == 0) + if (ret == 0) { + congested_progress = 1; progress = 1; + } } } } @@ -3365,15 +3396,25 @@ static int wc_linuxkm_entropy_daemon(void *arg) #endif ) { -#if defined(HAVE_FIPS) && FIPS_VERSION3_LT(7,0,0) - ret = wc_RNG_DRBG_Reseed_Now(root_rng, NULL, 0); -#else + /* The nonce lands on every boundary: natively in the credited + * reseed on current code and the 5.2.4 backport, and as an + * uncredited stir transition following the reseed in the + * legacy-boundary fallback. */ unsigned long uncredited_nonce = random_get_entropy(); - ret = wc_RNG_DRBG_Reseed_Now(root_rng, - (byte *)&uncredited_nonce, - sizeof uncredited_nonce); - ForceZero(&uncredited_nonce, sizeof uncredited_nonce); +#if !defined(WC_RNG_HAVE_LOCK) && defined(WC_RNG_BANK_HAVE_ROOT_RNG) + /* bank-root DRBG access serializes on the inst-op gate -- see + * wc_rng_bank_root_rng_reseed_now(). A BUSY_E refusal is + * transient: the countdown stays expired and the next pass + * retries. */ + ret = wc_rng_bank_root_rng_reseed_now( + bank, + (byte *)&uncredited_nonce, (word32)sizeof uncredited_nonce); +#else + ret = wc_RNG_DRBG_Reseed_Now( + root_rng, + (byte *)&uncredited_nonce, (word32)sizeof uncredited_nonce); #endif + ForceZero(&uncredited_nonce, sizeof uncredited_nonce); if (ret == 0) { root_rng_reseed_countdown = WC_LINUXKM_BONUS_RESEED_INTERVAL; @@ -3407,7 +3448,7 @@ static int wc_linuxkm_entropy_daemon(void *arg) if (global_fallback_rng != NULL) { wc_drbg_reseed_ctr_t this_reseedCtr; ret = wc_RNG_DRBG_GetReseedCtr(global_fallback_rng, &this_reseedCtr); - if ((ret == 0) && (this_reseedCtr > WC_LINUXKM_BONUS_RESEED_INTERVAL)) { + if ((ret == 0) && (this_reseedCtr >= WC_LINUXKM_BONUS_RESEED_INTERVAL)) { unsigned long uncredited_nonce = random_get_entropy(); #ifdef WC_RNG_HAVE_NEXT_SEED /* use the NextSeed aperture to minimize held time. */ @@ -3583,6 +3624,34 @@ static int wc_linuxkm_entropy_daemon(void *arg) #else /* !WC_RNG_HAVE_NEXT_SEED */ if (wc_RNG_entropy_needs_recovery(obj->rng) != WC_NO_ERR_TRACE(NEEDS_RECOVERY_E)) continue; + /* If the leaf isn't enforcing locks, then we have no thread + * synchronization mechanism to lean on and we can't reseed + * from here. + * + * This limitation doesn't exist for bank leaves, because + * they are always _LOCK_REQUIRED or the equivalent thereof, + * either directly via WC_RNG.lock or via the + * wc_rng_bank_inst.lock stand-in, and are accessed only + * through the bank checkout API. + * + * Moreover, this limitation doesn't exist in the + * WC_RNG_HAVE_NEXT_SEED configuration because + * wc_RNG_DRBG_NextSeedGenerate() has its own atomics-based + * thread synchronization mechanism pivoting on + * WC_RNG.nextSeedLen. + * + * Notice that _needs_recovery() above has already + * propagated an invalidation event if any; only the inline + * reseed operation is inhibited. + */ + { + WC_RNG_lock_arg_t this_lock; + if (wc_RNG_lock_read(obj->rng, &this_lock) != 0) + continue; + if (! (this_lock & WC_RNG_LOCK_REQUIRED)) + continue; + } + /* Without _NEXT_SEED support, just perform an inline reseed * (again, implicitly RBGC). */ { @@ -3622,8 +3691,8 @@ static int wc_linuxkm_entropy_daemon(void *arg) /* seed banking pass: one gather granule per instance per turn. * Generators will reseed opportunistically, using the banked seed, at * the WC_LINUXKM_BONUS_RESEED_INTERVAL. */ - for (i = 0; i < bank->n_rngs; i++) { + for (i = 0; i < bank->n_rngs; i++) { ret = wc_rng_bank_next_seed_generate( bank, i, WC_LINUXKM_ENTROPY_DAEMON_GRANULE); if (ret == 0) { @@ -3652,7 +3721,8 @@ static int wc_linuxkm_entropy_daemon(void *arg) } } #elif defined(WC_RNG_HAVE_LOCK) - /* !_HAVE_NEXT_SEED: bank leaf bonus reseed pass. */ + /* !_HAVE_NEXT_SEED: bank leaf bonus reseed pass on current + (WC_RNG.lock) code. */ for (i = 0; i < bank->n_rngs; i++) { WC_RNG *leaf = WC_RNG_BANK_INST_TO_RNG(&bank->rngs[i]); @@ -3673,25 +3743,32 @@ static int wc_linuxkm_entropy_daemon(void *arg) } } #else /* !WC_RNG_HAVE_NEXT_SEED && !WC_RNG_HAVE_LOCK */ - /* !_HAVE_NEXT_SEED && !_HAVE_LOCK: bank leaf bonus reseed pass. */ + /* !_HAVE_NEXT_SEED && !_HAVE_LOCK: bank leaf bonus reseed pass on + legacy FIPS. */ for (i = 0; i < bank->n_rngs; i++) { - WC_RNG *leaf = WC_RNG_BANK_INST_TO_RNG(&bank->rngs[i]); wc_drbg_reseed_ctr_t cur_counter = 0; - ret = wc_RNG_DRBG_GetReseedCtr(leaf, &cur_counter); + + /* Note, we have to get the lock first to exclude competing reinits, + * because on legacy code, wc_RNG_DRBG_GetReseedCtr() requires + * dereferencing the internal DRBG state. */ + + if (wc_rng_bank_inst_lock_get_unconditional(&bank->rngs[i]) != 0) + continue; + ret = wc_RNG_DRBG_GetReseedCtr(WC_RNG_BANK_INST_TO_RNG(&bank->rngs[i]), + &cur_counter); if ((ret == 0) && - (cur_counter >= WC_LINUXKM_BONUS_RESEED_INTERVAL) && - (wc_rng_bank_inst_lock_get_unconditional(&bank->rngs[i]) == 0)) + (cur_counter >= WC_LINUXKM_BONUS_RESEED_INTERVAL)) { unsigned long uncredited_nonce = random_get_entropy(); - ret = wc_RNG_DRBG_Reseed_Now(leaf, - (byte *)&uncredited_nonce, - (word32)sizeof uncredited_nonce); + ret = wc_rng_bank_inst_reseed_now(&bank->rngs[i], + (byte *)&uncredited_nonce, + (word32)sizeof uncredited_nonce); ForceZero(&uncredited_nonce, (word32)sizeof uncredited_nonce); if (ret == 0) progress = 1; - wc_rng_bank_inst_lock_put_unconditional(&bank->rngs[i]); } + wc_rng_bank_inst_lock_put_unconditional(&bank->rngs[i]); } #endif /* !WC_RNG_HAVE_NEXT_SEED && !WC_RNG_HAVE_LOCK */ @@ -3706,11 +3783,9 @@ static int wc_linuxkm_entropy_daemon(void *arg) } } - if (root_rng != NULL) { #ifdef WC_LINUXKM_VMGENID_POLL - wc_linuxkm_vmgenid_poll_teardown(&vmgenid_poll_state); + wc_linuxkm_vmgenid_poll_teardown(&vmgenid_poll_state); #endif - } #ifndef WC_RNG_BANK_HAVE_ROOT_RNG if (root_rng != NULL) { From 0a46c1b7ae5251627f2358be78d0894e90b1c243 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Tue, 27 Oct 2026 00:18:28 -0500 Subject: [PATCH 05/12] doc/dox_comments/header_files/random.h: add clarifying details to dox for wc_RNG_DRBG_NextSeedGenerate(), wc_RNG_Pool_Alloc(), and wc_RNG_Pool_Collect2(). --- doc/dox_comments/header_files/random.h | 30 ++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/doc/dox_comments/header_files/random.h b/doc/dox_comments/header_files/random.h index 33f0d3cc493..2bcd2a64219 100644 --- a/doc/dox_comments/header_files/random.h +++ b/doc/dox_comments/header_files/random.h @@ -1898,7 +1898,18 @@ int wc_RNG_DRBG_GetNextSeedRBGCStratum(const WC_RNG* rng); instance -- the single-writer fill and the atomic aperture hand-off make it safe alongside a concurrent consumer. - \details For an SP 800-90C chain member (RBGC stratum > 0, in builds + \details The next-seed bank is an intermediate staging buffer in the + seed pipeline -- the ordinary decoupling of entropy gather from seed + consumption, as in any entropy source with an internal FIFO or + collector thread. Banked material is not DRBG state: it becomes + state only in the consuming thread's reseed transition + (wc_RNG_DRBG_NextSeedNow(), or an opportunistic generate-path + consumption), is zeroized on consumption, and is purged unpublished + if its source crossed an invalidation event mid-fill. The aperture + protocol (WC_RNG.nextSeedLen) guarantees single production and + single consumption. + + For an SP 800-90C chain member (RBGC stratum > 0, in builds with instance locking), the material is drawn by the same fallback ladder as wc_RNG_DRBG_Reseed_Now(): the retained parent first (nonblocking acquisition), then the global fallback instance (under @@ -2534,6 +2545,18 @@ int wc_RNG_GetFlags(const WC_RNG* rng, word32 *flags); atomic aperture word, so it is safe against a concurrent wc_FreeRng() or wc_RNG_Pool_Collect2() on the same instance. + \details The pool facility involves exactly one generator: the source + that fills it (the instance itself via wc_RNG_Pool_Collect(), or, via + wc_RNG_Pool_Collect2(), the instance or its direct RBGC parent). The + pool is buffered output of that generator, held inside the module + boundary, and the host WC_RNG performs no cryptographic operation on + pool data: it contributes buffer storage, lock protection, and + scheduling/affinity convenience, and the bytes travel through their + own retrieval path (wc_RNG_Pool_Extract()), which never touches the + host's DRBG state. In-boundary placement is a conservatism: pooled + output inherits the entropy-invalidation machinery (purged on events, + wiped on failure) that an out-of-boundary buffer would forgo. + \return 0 Success \return BAD_FUNC_ARG rng is null, or size is less than 2 or greater than 32767. @@ -2613,7 +2636,10 @@ int wc_RNG_Pool_Collect(WC_RNG* rng, word32 n); eligibility rule is narrower still: rng_src must be rng_dest itself, or (under WC_RNG_HAVE_RBGC) rng_dest's direct RBGC parent -- the two relationships under which rng_src's output is already sanctioned to - reach rng_dest. Any other source is refused with + reach rng_dest. The rule is an attribution guarantee, not a seeding + criterion -- no reseed occurs here: extracted pool bytes are served + under rng_dest's identity and stratum, and eligibility ensures that + label is never misleading. Any other source is refused with WRONG_TYPE_OBJECT_E before any other processing, the n = 0 no-op included. From 502459c620b2e946244961b4da99a73d64ea8dbd Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Tue, 27 Oct 2026 00:54:59 -0500 Subject: [PATCH 06/12] linuxkm/lkcapi_sha_glue.c: in wc_linuxkm_entropy_daemon(), fix root reinstantiation, recovery-pass gating, and epoch-less configuration support. * !WC_RNG_HAVE_LOCK root recovery: An out-of-service root cannot recover by reseed (the reseed paths refuse a failed instance), so the recovery arm now leads with a lockless status peek and reinstantiates via wc_rng_bank_root_rng_reinit() (gate-serialized internally; spin-entered, task context). Likewise for a memberless root, by bare free+reinstantiate (daemon-private, no serialization needed or possible), covering the epoch-less memberless case nothing else could notice. Unlike the lock-facility arm, a hard reinit failure does not exit the daemon: without the lock facility the members cannot self-reseed inline, so the daemon stays and retries. * Gate the bank-leaf invalidation recovery pass on HAVE_HASHDRBG rather than WC_RNG_HAVE_RBGC: invalidation never saturates the reseed counter on lock-facility builds, so without this pass a healthy-but-latched leaf in a !WC_RNG_HAVE_NEXT_SEED && !WC_RNG_HAVE_RBGC build is invisible to the bonus pass and the status patrol alike, and ordinary checkouts starve. The reseed is implicitly RBGC when the instance has a chain parent, else primary-sourced; RBGC was never a precondition. * Remove the just-added #error tripwires for WC_RNG_HAVE_LOCK/WC_RNG_HAVE_NEXT_SEED without WC_RNG_HAVE_ENTROPY_EPOCH: WC_RNG_NO_ENTROPY_EPOCH (random.h) disables the epoch independently of the lock and next-seed facilities, so those configurations are reachable and buildable. No fallback arms need restoring: the wc_rng_bank_inst lock accessors map to WC_RNG.lock when the lock facility is present and to the wc_rng_bank_inst.lock stand-in otherwise, and the reseed epilogue's latch clear is epoch-independent, so the unified epoch-less arm already serves all of them. --- linuxkm/lkcapi_sha_glue.c | 192 ++++++++++++++++++++++++++------------ 1 file changed, 132 insertions(+), 60 deletions(-) diff --git a/linuxkm/lkcapi_sha_glue.c b/linuxkm/lkcapi_sha_glue.c index 00b512ff905..d2b41b55faf 100644 --- a/linuxkm/lkcapi_sha_glue.c +++ b/linuxkm/lkcapi_sha_glue.c @@ -3127,55 +3127,120 @@ static int wc_linuxkm_entropy_daemon(void *arg) * design. */ #ifdef WC_RNG_BANK_HAVE_ROOT_RNG if (root_rng != NULL) { - unsigned long uncredited_nonce = random_get_entropy(); - ret = wc_rng_bank_root_rng_reseed_if_due( - bank, - (byte *)&uncredited_nonce, (word32)sizeof uncredited_nonce, - (wc_drbg_reseed_ctr_t)WC_RESEED_INTERVAL); - ForceZero(&uncredited_nonce, (word32)sizeof uncredited_nonce); - if (ret == 0) { + if (wc_RNG_GetStatus(root_rng) != WC_DRBG_OK) { + /* Out-of-service root: the reseed paths refuse a failed + * instance, so recovery is by reinstantiation -- + * gate-serialized inside wc_rng_bank_root_rng_reinit() + * (spin-entered; task context). The status peek is + * lockless and possibly stale: worst case it reinits a + * root another party just fixed (a fresh seed, no harm) + * or misses one cycle. Unlike the lock-facility arm, + * a hard failure does not exit the daemon: without the + * lock facility the members cannot self-reseed inline, + * so the daemon stays and retries. */ + unsigned long uncredited_nonce = random_get_entropy(); + ret = wc_rng_bank_root_rng_reinit( + bank, + (byte *)&uncredited_nonce, + (word32)sizeof uncredited_nonce, + NULL, 0, 0); + ForceZero(&uncredited_nonce, + (word32)sizeof uncredited_nonce); + if (ret == 0) { + pr_notice("wc_entropyd: root_rng reinitialization " + "succeeded.\n"); + progress = 1; + } + else if (WC_LKM_RNG_ERR_IS_HEALTH(ret)) { + pr_warn_ratelimited( + "wc_entropyd: root_rng reinitialization failed " + "the seed health test: %d -- entropy source " + "suspect\n", ret); + } + else if (! WC_RNG_ERR_IS_TRANSIENT(ret)) { + pr_err_ratelimited( + "wc_entropyd: root_rng reinitialization failed: " + "%d\n", ret); + } + } + else { + unsigned long uncredited_nonce = random_get_entropy(); + ret = wc_rng_bank_root_rng_reseed_if_due( + bank, + (byte *)&uncredited_nonce, + (word32)sizeof uncredited_nonce, + (wc_drbg_reseed_ctr_t)WC_RESEED_INTERVAL); + ForceZero(&uncredited_nonce, + (word32)sizeof uncredited_nonce); + if (ret == 0) { #ifdef WC_VERBOSE_RNG - pr_info("wc_linuxkm_entropy_daemon: root recovery reseed " - "(saturated counter).\n"); + pr_info("wc_linuxkm_entropy_daemon: root recovery " + "reseed (saturated counter).\n"); #endif - progress = 1; - } - else if (WC_LKM_RNG_ERR_IS_HEALTH(ret)) { - pr_warn_ratelimited( - "wc_entropyd: root_rng recovery reseed failed the seed " - "health test: %d -- entropy source suspect\n", ret); - } - else if ((ret != WC_NO_ERR_TRACE(ALREADY_E)) && - (ret != WC_NO_ERR_TRACE(WRONG_TYPE_OBJECT_E)) && - (! WC_RNG_ERR_IS_TRANSIENT(ret))) - { - /* ALREADY_E: not due -- the steady state. - * WRONG_TYPE_OBJECT_E: no DRBG behind the root (RDRAND et - * al.) -- permanently nothing to do. Transient codes (the - * gate held) are one pass away. */ - pr_err_ratelimited( - "wc_entropyd: root_rng recovery reseed failed: %d\n", - ret); + progress = 1; + } + else if (WC_LKM_RNG_ERR_IS_HEALTH(ret)) { + pr_warn_ratelimited( + "wc_entropyd: root_rng recovery reseed failed the " + "seed health test: %d -- entropy source suspect\n", + ret); + } + else if ((ret != WC_NO_ERR_TRACE(ALREADY_E)) && + (ret != WC_NO_ERR_TRACE(WRONG_TYPE_OBJECT_E)) && + (! WC_RNG_ERR_IS_TRANSIENT(ret))) + { + /* ALREADY_E: not due -- the steady state. + * WRONG_TYPE_OBJECT_E: no DRBG behind the root + * (RDRAND et al.) -- permanently nothing to do. + * Transient codes (the gate held) are one pass away. + * A root that failed between the status peek and the + * reseed logs once here; the status branch above + * reinstantiates it next pass. */ + pr_err_ratelimited( + "wc_entropyd: root_rng recovery reseed failed: " + "%d\n", ret); + } } } - #elif defined(WC_RNG_HAVE_ENTROPY_EPOCH) - if ((root_rng != NULL) && - (wc_RNG_entropy_needs_recovery(root_rng) != 0)) - { - unsigned long uncredited_nonce = random_get_entropy(); - ret = wc_RNG_DRBG_Reseed_Now( - root_rng, - (byte *)&uncredited_nonce, (word32)sizeof uncredited_nonce); - ForceZero(&uncredited_nonce, (word32)sizeof uncredited_nonce); - if (ret == 0) - progress = 1; - else if (! WC_RNG_ERR_IS_TRANSIENT(ret)) { - pr_err_ratelimited( - "wc_entropyd: root_rng recovery reseed failed: %d\n", - ret); + #else /* !WC_RNG_BANK_HAVE_ROOT_RNG */ + if (root_rng != NULL) { + if (wc_RNG_GetStatus(root_rng) != WC_DRBG_OK) { + /* Daemon-private root: free+reinstantiate, no + * serialization needed or possible. */ + ret = wc_FreeRng(root_rng); + if (ret == 0) + ret = wc_InitRng(root_rng); + if (ret == 0) { + pr_info("wc_entropyd: root_rng reinitialization " + "succeeded.\n"); + progress = 1; + } + else if (! WC_RNG_ERR_IS_TRANSIENT(ret)) { + pr_err_ratelimited( + "wc_entropyd: root_rng reinitialization failed: " + "%d\n", ret); + } + } + #ifdef WC_RNG_HAVE_ENTROPY_EPOCH + else if (wc_RNG_entropy_needs_recovery(root_rng) != 0) { + unsigned long uncredited_nonce = random_get_entropy(); + ret = wc_RNG_DRBG_Reseed_Now( + root_rng, + (byte *)&uncredited_nonce, + (word32)sizeof uncredited_nonce); + ForceZero(&uncredited_nonce, + (word32)sizeof uncredited_nonce); + if (ret == 0) + progress = 1; + else if (! WC_RNG_ERR_IS_TRANSIENT(ret)) { + pr_err_ratelimited( + "wc_entropyd: root_rng recovery reseed failed: " + "%d\n", ret); + } } + #endif /* WC_RNG_HAVE_ENTROPY_EPOCH */ } - #endif /* WC_RNG_BANK_HAVE_ROOT_RNG / WC_RNG_HAVE_ENTROPY_EPOCH */ + #endif /* !WC_RNG_BANK_HAVE_ROOT_RNG */ #endif /* WC_RNG_HAVE_LOCK */ #ifdef WC_RNG_HAVE_GLOBAL_FALLBACK_RNG @@ -3196,10 +3261,14 @@ static int wc_linuxkm_entropy_daemon(void *arg) } #endif /* WC_RNG_HAVE_GLOBAL_FALLBACK_RNG */ -#ifdef WC_RNG_HAVE_RBGC - /* recovery pass: if WC_RNG_HAVE_NEXT_SEED, push RBGC seeds to all +#ifdef HAVE_HASHDRBG + /* recovery pass: if WC_RNG_HAVE_NEXT_SEED, push seeds to all * WC_RNG_LOCK_ENTROPY_INVALIDATED instances that need them; if not, run - * inline RBGC reseeds of all instances that need them. */ + * inline reseeds of all instances that need them. Both are implicitly + * RBGC when the instance has a chain parent, else primary-sourced: + * invalidated instances need recovery whether or not the build has + * RBGC, since invalidation never saturates the reseed counter on + * lock-facility builds, so no other pass would collect them. */ { #ifdef WC_VERBOSE_RNG int n_rbgc_recovered = 0; @@ -3211,17 +3280,17 @@ static int wc_linuxkm_entropy_daemon(void *arg) WC_RNG *rng = WC_RNG_BANK_INST_TO_RNG(&bank->rngs[i]); #ifndef WC_RNG_HAVE_ENTROPY_EPOCH - /* Backward-compat check for invalidation on old-FIPS builds - * (!WC_RNG_HAVE_ENTROPY_EPOCH) - */ - - /* These configuration combinations aren't reachable, but make sure. */ - #ifdef WC_RNG_HAVE_LOCK - #error WC_RNG_HAVE_LOCK without WC_RNG_HAVE_ENTROPY_EPOCH is not a supported kernel module configuration. - #endif - #ifdef WC_RNG_HAVE_NEXT_SEED - #error WC_RNG_HAVE_NEXT_SEED without WC_RNG_HAVE_ENTROPY_EPOCH is not a supported kernel module configuration. - #endif + /* Backward-compat invalidation check for + * !WC_RNG_HAVE_ENTROPY_EPOCH builds -- old FIPS, or any + * build with WC_RNG_NO_ENTROPY_EPOCH, which random.h + * allows independently of the lock and next-seed + * facilities. This one arm serves them all: the + * wc_rng_bank_inst lock accessors map to WC_RNG.lock when + * the lock facility is present and to the + * wc_rng_bank_inst.lock stand-in otherwise, and the + * reseed epilogue's latch clear is epoch-independent + * (wolfcrypt/src/random.c), so the latch sensing and the + * reseed protocol below are configuration-agnostic. */ WC_RNG_lock_arg_t rng_inst_lock_state; @@ -3299,12 +3368,15 @@ static int wc_linuxkm_entropy_daemon(void *arg) } #ifdef WC_VERBOSE_RNG if (n_rbgc_recovered > 0) { - pr_info("wc_linuxkm_entropy_daemon: RBGC recovery of %d/%d insts, %d self-recovered, %d skipped (busy), %d failed.\n", - n_rbgc_recovered, bank->n_rngs, n_self_recovered, n_busy, n_unhealthy); + pr_info("wc_linuxkm_entropy_daemon: recovery of %d/%d " + "insts, %d self-recovered, %d skipped (busy), " + "%d failed.\n", + n_rbgc_recovered, bank->n_rngs, n_self_recovered, + n_busy, n_unhealthy); } #endif } -#endif /* WC_RNG_HAVE_RBGC */ +#endif /* HAVE_HASHDRBG */ /* recovery pass: fix out-of-service instances. The status * peek is lockless and possibly stale -- worst case it sends a From 59bdee981173bec62cb2ab1b25f520543470a4f1 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Tue, 27 Oct 2026 01:23:45 -0500 Subject: [PATCH 07/12] linuxkm/lkcapi_sha_glue.c: in wc_linuxkm_entropy_daemon(), fix two recovery-coverage gaps in epoch-less and lock-less configurations. * bank recovery pass, epoch arm: on !WC_RNG_HAVE_LOCK, also read the wc_rng_bank_inst.lock stand-in latch, which wc_RNG_entropy_needs_recovery() cannot see -- a member invalidated while checked out is latched without counter saturation, and a bank-scoped invalidation moves no global epoch, so the member read as healthy while checkouts refused it. RNG-level verdicts are preserved. * registry-leaf sweep: gate on WC_LINUXKM_HAVE_RNG_REGISTRY && (WC_RNG_HAVE_LOCK || WC_RNG_HAVE_NEXT_SEED) rather than the entropy epoch, which is neither necessary (needs_recovery reads the latch epoch-independently) nor sufficient (the !_NEXT_SEED arm is lock-facility API). The gate admits exactly the configurations where the sweep has a safe action, and makes the lock_read arm compile-safe by construction. --- linuxkm/lkcapi_sha_glue.c | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/linuxkm/lkcapi_sha_glue.c b/linuxkm/lkcapi_sha_glue.c index d2b41b55faf..b469021629d 100644 --- a/linuxkm/lkcapi_sha_glue.c +++ b/linuxkm/lkcapi_sha_glue.c @@ -3318,6 +3318,25 @@ static int wc_linuxkm_entropy_daemon(void *arg) #else /* WC_RNG_HAVE_ENTROPY_EPOCH */ ret = wc_RNG_entropy_needs_recovery(rng); + #ifndef WC_RNG_HAVE_LOCK + if (ret == 0) { + /* The bank-side stand-in latch is invisible to + * wc_RNG_entropy_needs_recovery(): a member + * invalidated while checked out is latched without + * counter saturation, and a bank-scoped invalidation + * (wc_rng_bank_invalidate_entropy()) moves no global + * epoch, so the member can read as healthy here while + * ordinary checkouts refuse it. Read the latch + * directly; RNG-level verdicts above are preserved. */ + WC_RNG_lock_arg_t inst_lock_state; + if ((wc_rng_bank_inst_lock_read( + &bank->rngs[i], &inst_lock_state) == 0) && + (inst_lock_state & WC_RNG_LOCK_ENTROPY_INVALIDATED)) + { + ret = NEEDS_RECOVERY_E; + } + } + #endif /* !WC_RNG_HAVE_LOCK */ if (ret != WC_NO_ERR_TRACE(NEEDS_RECOVERY_E)) { #ifdef WC_VERBOSE_RNG if (ret == 0) @@ -3644,7 +3663,16 @@ static int wc_linuxkm_entropy_daemon(void *arg) } #endif /* WC_RNG_HAVE_POOL */ -#if defined(WC_LINUXKM_HAVE_RNG_REGISTRY) && defined(WC_RNG_HAVE_ENTROPY_EPOCH) +#if defined(WC_LINUXKM_HAVE_RNG_REGISTRY) && \ + (defined(WC_RNG_HAVE_LOCK) || defined(WC_RNG_HAVE_NEXT_SEED)) + /* The sweep compiles wherever it has a safe action: the next-seed + * aperture, or a lock-bracketed inline reseed (the !_NEXT_SEED + * arm below is lock-facility API, so the gate guarantees + * WC_RNG_HAVE_LOCK whenever that arm compiles). The entropy + * epoch is not a precondition: wc_RNG_entropy_needs_recovery() + * reads the invalidation latch independently of epochs. Without + * either facility, standalone leaves have no lock word to sense + * or exclude by, and recover owner-side instead. */ /* reseed pass for long-lived leaves that need it: if _HAVE_NEXT_SEED, * then reseed is by RBGC from parent, after which their next generate * recovers/promotes in place (WC_RNG_INIT_FLAG_UPGRADE_FROM_NEXT_SEED); @@ -3752,7 +3780,8 @@ static int wc_linuxkm_entropy_daemon(void *arg) &needs_recovery_snapshot, 0); } } -#endif /* WC_LINUXKM_HAVE_RNG_REGISTRY && WC_RNG_HAVE_ENTROPY_EPOCH */ +#endif /* WC_LINUXKM_HAVE_RNG_REGISTRY && + * (WC_RNG_HAVE_LOCK || WC_RNG_HAVE_NEXT_SEED) */ /* if we're coping with congestion hits, continue here, don't bog down * in primary seed ops. */ From e5882c6bd49541b55919a6a9c995ead73b42bf36 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Tue, 27 Oct 2026 02:24:50 -0500 Subject: [PATCH 08/12] wolfcrypt/src/random.c, linuxkm/lkcapi_sha_glue.c: fix RBGC_parent reread races on the unowned-caller path, and a lost registry recovery signal. * random.c: wc_RNG_DRBG_NextSeedGenerate() admits unowned callers by design (a scheduling daemon synchronizes through the next-seed aperture, not the instance lease), so a concurrent teardown of rng can CAS-null rng->RBGC_parent mid-call: a reread between wc_RNG_lock_get() and wc_RNG_lock_put() could release the wrong object (or none), leaving the parent permanently locked, and the rereads in the FIPS eligibility criteria (the fallback arm, and the source check in _NextSeedGenerate_local(), reachable from the same entry) could dereference null. Capture the pointer exactly once per scope; the held lease pins the parent shell against its owner's wc_FreeRng(). The owned-rng reseed paths (PollAndReSeed(), wc_RNG_DRBG_ReseedRBGC_local()) are unchanged: ownership of rng is required there by mechanism or contract, which makes RBGC_parent stable, and the comments now record that distinction. * lkcapi_sha_glue.c: the registry-leaf sweep no longer clears wc_linuxkm_rng_registry_needs_recovery after a pass in which a leaf was refused on a transient (lock contention, source not ready, or a standing latch): a lock-required leaf's own consumers are refused by the same latch, so the sweep is its only recovery agent, and clearing the counter consumed the only signal that brings the sweep back. Leaves the sweep can never serve (lock-naive) do not hold the counter hot. --- linuxkm/lkcapi_sha_glue.c | 26 ++++++++++++++++++----- wolfcrypt/src/random.c | 44 +++++++++++++++++++++++++++++---------- 2 files changed, 54 insertions(+), 16 deletions(-) diff --git a/linuxkm/lkcapi_sha_glue.c b/linuxkm/lkcapi_sha_glue.c index b469021629d..675712cadff 100644 --- a/linuxkm/lkcapi_sha_glue.c +++ b/linuxkm/lkcapi_sha_glue.c @@ -3683,6 +3683,7 @@ static int wc_linuxkm_entropy_daemon(void *arg) WOLFSSL_ATOMIC_LOAD(wc_linuxkm_rng_registry_needs_recovery); if (needs_recovery_snapshot != 0) { struct linuxkm_rng_object *obj; + int sweep_incomplete = 0; mutex_lock(&wc_linuxkm_rng_registry_mutex); for (obj = wc_linuxkm_rng_registry_head; obj != NULL; obj = obj->next) @@ -3771,13 +3772,28 @@ static int wc_linuxkm_entropy_daemon(void *arg) progress = 1; congested_progress = 1; } + else if (WC_RNG_ERR_IS_TRANSIENT(ret)) { + /* Refused this pass -- contention, source not + * ready, or a standing latch. A lock-required + * leaf's own consumers are refused by the same + * latch, so this sweep is its only recovery + * agent: keep the needs-recovery counter hot + * below rather than consuming the only signal + * that brings the sweep back. */ + sweep_incomplete = 1; + } } mutex_unlock(&wc_linuxkm_rng_registry_mutex); - /* on failure, an invalidation landed since the snapshot: - * leave the counter hot and re-sweep next pass. */ - (void)wolfSSL_Atomic_Int_CompareExchange( - &wc_linuxkm_rng_registry_needs_recovery, - &needs_recovery_snapshot, 0); + /* Clear the counter only after a complete sweep. A CAS + * failure means an invalidation landed since the + * snapshot; sweep_incomplete means a leaf was refused on + * a transient. Either way the counter stays hot and the + * next pass re-sweeps. */ + if (! sweep_incomplete) { + (void)wolfSSL_Atomic_Int_CompareExchange( + &wc_linuxkm_rng_registry_needs_recovery, + &needs_recovery_snapshot, 0); + } } } #endif /* WC_LINUXKM_HAVE_RNG_REGISTRY && diff --git a/wolfcrypt/src/random.c b/wolfcrypt/src/random.c index 80ea87ec8b7..cb57a44eb94 100644 --- a/wolfcrypt/src/random.c +++ b/wolfcrypt/src/random.c @@ -6602,12 +6602,19 @@ static WARN_UNUSED_RESULT int wc_RNG_DRBG_NextSeedGenerate_local( * because the tree may have been broken by a wc_FreeRng(), so we use the * same criterion as wc_RNG_DRBG_ReseedRBGC_local() to qualify the supplied * root. */ - if ((rng->RBGC_parent != NULL) && - (rng->RBGC_parent != root) && - ((rng->RBGC_parent->RBGCStratum > 0) || - (root->RBGCStratum > 0))) { - return WRONG_TYPE_OBJECT_E; + /* One load: reachable from the unowned-caller entry + * (wc_RNG_DRBG_NextSeedGenerate()), where a concurrent + * teardown can CAS-null rng->RBGC_parent between the null + * check and the dereference. */ + WC_RNG * const parent = rng->RBGC_parent; + if ((parent != NULL) && + (parent != root) && + ((parent->RBGCStratum > 0) || + (root->RBGCStratum > 0))) + { + return WRONG_TYPE_OBJECT_E; + } } #endif /* HAVE_FIPS */ @@ -6897,11 +6904,26 @@ int wc_RNG_DRBG_NextSeedGenerate(WC_RNG* rng, word32 n) { if (rng->RBGCStratum > 0) { int ret = WC_NO_ERR_TRACE(NEEDS_RECOVERY_E); int put_ret; - if ((wc_RNG_Status(rng->RBGC_parent) == 0) && - (wc_RNG_lock_get(rng->RBGC_parent, 0) == 0)) + /* Captured once: this entry admits unowned callers by design (a + * scheduling daemon synchronizes through the next-seed aperture, + * not the instance lease), so a concurrent teardown of rng can + * CAS-null rng->RBGC_parent mid-call, and the put below must + * release exactly what the get acquired. Contrast the owned-rng + * reseed paths (PollAndReSeed() et al.): ownership of rng makes + * RBGC_parent stable there, and no capture is used. + * + * Storage lifetime is the usual shell contract: wc_FreeRng() may + * retire parent or child at any time (the shells' words stay live + * and refuse coherently), but free() of either shell requires + * quiescing unowned callers first. Every in-tree parent is a + * bank root or the static global fallback, satisfying this by + * construction. */ + WC_RNG * const parent = rng->RBGC_parent; + if ((wc_RNG_Status(parent) == 0) && + (wc_RNG_lock_get(parent, 0) == 0)) { - ret = wc_RNG_DRBG_NextSeedGenerate_local(rng, rng->RBGC_parent, n); - put_ret = wc_RNG_lock_put(rng->RBGC_parent, 0); + ret = wc_RNG_DRBG_NextSeedGenerate_local(rng, parent, n); + put_ret = wc_RNG_lock_put(parent, 0); if (put_ret != 0) { if (put_ret == WC_NO_ERR_TRACE(NEEDS_RECOVERY_E)) { int purge_ret = NextSeedPurge(rng); @@ -6923,10 +6945,10 @@ int wc_RNG_DRBG_NextSeedGenerate(WC_RNG* rng, word32 n) { /* SP-800-90C requires that an alternative randomness source be "a * sibling of the parent, an ancestor, or the initial randomness * source". */ - (rng->RBGC_parent != NULL) && + (parent != NULL) && ((global_fallback_rng.RBGCStratum == 0) && (global_fallback_rng.RBGCStratum == - rng->RBGC_parent->RBGCStratum)) && + parent->RBGCStratum)) && #endif (wc_RNG_lock_get(&global_fallback_rng, 0) == 0)) { From a15e1f43cd48f327cc6157c547883b2404462c04 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Wed, 28 Oct 2026 23:50:39 -0500 Subject: [PATCH 09/12] wolfcrypt/test/test.c: fix retval capture/encoding in rng_epoch_test() and rng_lifecycle_test(). --- wolfcrypt/test/test.c | 448 +++++++++++++++++++++++++++++++----------- 1 file changed, 337 insertions(+), 111 deletions(-) diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 270f8b4e0b4..c8ece868624 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -36590,54 +36590,72 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_epoch_test(void) * parent's own recovery: a lease taken before the event is not * revoked by it, only reported at release. */ api_ret = wc_RNG_lock_get(&parent, 0); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); if (api_ret == 0) { api_ret = wc_InitRngNonceRBGC(&child, &parent, NULL, 0, NULL, 0, 0); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); if (api_ret == 0) child_inited = 1; - if (api_ret == 0) + if (api_ret == 0) { api_ret = wc_RNG_entropy_needs_recovery(&child); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); + } if (api_ret == 0) { (void)wc_RNG_global_invalidate_entropy(); api_ret = wc_RNG_entropy_needs_recovery(&child); if (api_ret == WC_NO_ERR_TRACE(NEEDS_RECOVERY_E)) api_ret = 0; else if (api_ret == 0) - api_ret = WC_NO_ERR_TRACE(UNEXPECTED_STATE_E); + api_ret = WC_TEST_RET_ENC_EC(UNEXPECTED_STATE_E); + else + api_ret = WC_TEST_RET_ENC_EC(api_ret); } if (api_ret == 0) { /* the parent is in the same boat, and recovers under the * lease; the child then recovers from the recovered parent. */ api_ret = wc_RNG_GenerateBlock(&parent, block, (word32)sizeof(block)); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); } { + /* recovered: no latch to report */ int put_ret = wc_RNG_lock_put(&parent, 0); if ((api_ret == 0) && (put_ret != 0)) - api_ret = put_ret; /* recovered: no latch to report */ + api_ret = WC_TEST_RET_ENC_EC(put_ret); } } - if (api_ret == 0) + if (api_ret == 0) { api_ret = wc_RNG_GenerateBlock(&child, block, (word32)sizeof(block)); - if (api_ret == 0) + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); + } + if (api_ret == 0) { api_ret = wc_RNG_entropy_needs_recovery(&child); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); + } if (api_ret == 0) { /* still a chain member: recovery did not relabel it */ if (wc_RNG_DRBG_GetRBGCStratum(&child) != 1) - api_ret = WC_NO_ERR_TRACE(UNEXPECTED_STATE_E); + api_ret = WC_TEST_RET_ENC_EC(UNEXPECTED_STATE_E); } if (child_inited) { int free_ret = wc_FreeRng(&child); if ((api_ret == 0) && (free_ret != 0)) - api_ret = free_ret; + api_ret = WC_TEST_RET_ENC_EC(free_ret); } if (parent_inited) { int free_ret = wc_FreeRng(&parent); if ((api_ret == 0) && (free_ret != 0)) - api_ret = free_ret; + api_ret = WC_TEST_RET_ENC_EC(free_ret); } if (api_ret != 0) - ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out_l); + ERROR_OUT(api_ret, out_l); /* encoded at the failure site */ } #endif /* WC_RNG_HAVE_RBGC && WC_RNG_HAVE_LOCK */ @@ -36653,6 +36671,8 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_epoch_test(void) if (api_ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out_l); api_ret = wc_RNG_DRBG_ScheduleReseed(rng); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); if (api_ret != 0) { (void)wc_SetSeed_Cb(WC_GENERATE_SEED_DEFAULT); ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out_l); @@ -36692,20 +36712,27 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_epoch_test(void) if (api_ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out_l); api_ret = wc_RNG_DRBG_ScheduleReseed(rng); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); if (api_ret == 0) { api_ret = wc_RNG_DRBG_Reseed_Now(rng, NULL, 0); - if (api_ret == WC_NO_ERR_TRACE(BUSY_E)) + if (api_ret == WC_NO_ERR_TRACE(BUSY_E)) { api_ret = wc_RNG_Status(rng); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); + } else if (api_ret == 0) - api_ret = WC_NO_ERR_TRACE(UNEXPECTED_STATE_E); + api_ret = WC_TEST_RET_ENC_EC(UNEXPECTED_STATE_E); + else + api_ret = WC_TEST_RET_ENC_EC(api_ret); } { int cb_ret = wc_SetSeed_Cb(WC_GENERATE_SEED_DEFAULT); if ((api_ret == 0) && (cb_ret != 0)) - api_ret = cb_ret; + api_ret = WC_TEST_RET_ENC_EC(cb_ret); } if (api_ret != 0) - ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out_l); + ERROR_OUT(api_ret, out_l); /* encoded at the failure site */ api_ret = wc_RNG_GenerateBlock(rng, block, (word32)sizeof(block)); if (api_ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out_l); @@ -36717,20 +36744,27 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_epoch_test(void) if (api_ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out_l); api_ret = wc_RNG_DRBG_ScheduleReseed(rng); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); if (api_ret == 0) { api_ret = wc_RNG_DRBG_Reseed_Now(rng, NULL, 0); - if (api_ret == WC_NO_ERR_TRACE(MEMORY_E)) + if (api_ret == WC_NO_ERR_TRACE(MEMORY_E)) { api_ret = wc_RNG_Status(rng); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); + } else if (api_ret == 0) - api_ret = WC_NO_ERR_TRACE(UNEXPECTED_STATE_E); + api_ret = WC_TEST_RET_ENC_EC(UNEXPECTED_STATE_E); + else + api_ret = WC_TEST_RET_ENC_EC(api_ret); } { int cb_ret = wc_SetSeed_Cb(WC_GENERATE_SEED_DEFAULT); if ((api_ret == 0) && (cb_ret != 0)) - api_ret = cb_ret; + api_ret = WC_TEST_RET_ENC_EC(cb_ret); } if (api_ret != 0) - ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out_l); + ERROR_OUT(api_ret, out_l); /* encoded at the failure site */ api_ret = wc_RNG_GenerateBlock(rng, block, (word32)sizeof(block)); if (api_ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out_l); @@ -36795,19 +36829,27 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_lifecycle_test(void) WC_DRBG_SEED_SZ, NULL, 0, NULL, 0, HEAP_HINT, devId, WC_RNG_INIT_FLAG_NO_PRIMARY_SEED); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); if (api_ret == 0) { u_inited = 1; if (wc_RNG_DRBG_GetRBGCStratum(&u) != WC_RNG_RBGC_USER_SEED_STRATUM) { - api_ret = WC_NO_ERR_TRACE(UNEXPECTED_STATE_E); + api_ret = WC_TEST_RET_ENC_EC(UNEXPECTED_STATE_E); } } - if (api_ret == 0) + if (api_ret == 0) { api_ret = wc_RNG_GenerateBlock(&u, block, (word32)sizeof(block)); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); + } #ifdef WC_RNG_HAVE_LOCK - if (api_ret == 0) + if (api_ret == 0) { api_ret = wc_RNG_invalidate_entropy(&u); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); + } if (api_ret == 0) { WC_RNG *fb = NULL; #ifndef HAVE_FIPS @@ -36821,12 +36863,14 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_lifecycle_test(void) if (fb != NULL) { api_ret = wc_RNG_GenerateBlock(&u, block, (word32)sizeof(block)); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); if ((api_ret == 0) && (wc_RNG_DRBG_GetRBGCStratum(&u) != RBGC_RESEED_STRATUM(wc_RNG_DRBG_GetRBGCStratum(fb) + 1, WC_RNG_RBGC_USER_SEED_STRATUM))) { - api_ret = WC_NO_ERR_TRACE(UNEXPECTED_STATE_E); + api_ret = WC_TEST_RET_ENC_EC(UNEXPECTED_STATE_E); } } else { @@ -36835,29 +36879,43 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_lifecycle_test(void) api_ret = wc_RNG_GenerateBlock(&u, block, (word32)sizeof(block)); if (api_ret == 0) - api_ret = WC_NO_ERR_TRACE(UNEXPECTED_STATE_E); + api_ret = WC_TEST_RET_ENC_EC(UNEXPECTED_STATE_E); else if ((api_ret == WC_NO_ERR_TRACE(NEEDS_RECOVERY_E)) || (api_ret == WC_NO_ERR_TRACE(NOT_READY_E))) + { api_ret = wc_RNG_Status(&u); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); + } + else + api_ret = WC_TEST_RET_ENC_EC(api_ret); } } #endif if (api_ret == 0) { XMEMSET(user_seed, 0xa5, WC_DRBG_SEED_SZ); api_ret = wc_RNG_DRBG_Reseed(&u, user_seed, WC_DRBG_SEED_SZ); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); } WC_FREE_VAR(user_seed, HEAP_HINT); - if (api_ret == 0) + if (api_ret == 0) { api_ret = wc_RNG_GenerateBlock(&u, block, (word32)sizeof(block)); - if (api_ret == 0) + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); + } + if (api_ret == 0) { api_ret = wc_RNG_entropy_needs_recovery(&u); /* discharged */ + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); + } if (u_inited) { int free_ret = wc_FreeRng(&u); if ((api_ret == 0) && (free_ret != 0)) - api_ret = free_ret; + api_ret = WC_TEST_RET_ENC_EC(free_ret); } if (api_ret != 0) - ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out_l); + ERROR_OUT(api_ret, out_l); /* encoded at the failure site */ } #endif /* WC_RNG_HAVE_RBGC */ @@ -36872,20 +36930,35 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_lifecycle_test(void) api_ret = wc_InitRngNonce_ex2(&m, NULL, 0, NULL, 0, HEAP_HINT, devId, WC_RNG_INIT_FLAG_LOCK_REQUIRED | WC_RNG_INIT_FLAG_USE_FULL_MUTEX); - if (api_ret == 0) + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); + if (api_ret == 0) { api_ret = wc_FreeRng(&m); /* never held: free takes and releases */ - if (api_ret == 0) + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); + } + if (api_ret == 0) { api_ret = wc_InitRngNonce_ex2(&m, NULL, 0, NULL, 0, HEAP_HINT, devId, WC_RNG_INIT_FLAG_LOCK_REQUIRED | WC_RNG_INIT_FLAG_USE_FULL_MUTEX); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); + } if (api_ret == 0) { api_ret = wc_RNG_lock_get(&m, 0); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); if (api_ret == 0) { api_ret = wc_RNG_GenerateBlock(&m, block, (word32)sizeof(block)); - if (api_ret == 0) + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); + if (api_ret == 0) { api_ret = wc_FreeRng_PreLocked(&m); /* holder's teardown */ + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); + } else (void)wc_RNG_lock_put(&m, 0); } @@ -36899,11 +36972,16 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_lifecycle_test(void) devId, WC_RNG_INIT_FLAG_LOCK_REQUIRED | WC_RNG_INIT_FLAG_USE_FULL_MUTEX); - if (api_ret == 0) + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); + if (api_ret == 0) { api_ret = wc_FreeRng(&m); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); + } } if (api_ret != 0) - ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out_l); + ERROR_OUT(api_ret, out_l); /* encoded at the failure site */ } #if defined(HAVE_WC_RNG_BANK) && defined(WC_RNG_BANK_HAVE_ROOT_RNG) @@ -36922,38 +37000,51 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_lifecycle_test(void) WC_RNG_BANK_FLAG_CAN_WAIT | WC_RNG_BANK_FLAG_QUIET, 10, HEAP_HINT, devId); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); if (api_ret == 0) bank_inited = 1; root = (api_ret == 0) ? wc_rng_bank_root_rng_get(bank) : NULL; if ((api_ret == 0) && (root == NULL)) - api_ret = WC_NO_ERR_TRACE(MISSING_RNG_E); + api_ret = WC_TEST_RET_ENC_EC(MISSING_RNG_E); /* vacate the root so the constructor can be tried on it */ - if (api_ret == 0) + if (api_ret == 0) { api_ret = wc_FreeRng(root); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); + } if (api_ret == 0) { api_ret = wc_rng_bank_root_rng_init( bank, NULL, 0, NULL, 0, WC_RNG_INIT_FLAG_USE_FULL_MUTEX); if (api_ret == WC_NO_ERR_TRACE(BAD_FUNC_ARG)) api_ret = 0; else if (api_ret == 0) - api_ret = WC_NO_ERR_TRACE(UNEXPECTED_STATE_E); + api_ret = WC_TEST_RET_ENC_EC(UNEXPECTED_STATE_E); + else + api_ret = WC_TEST_RET_ENC_EC(api_ret); } if ((api_ret == 0) && (wc_rng_bank_root_rng_get(bank) != NULL)) - api_ret = WC_NO_ERR_TRACE(UNEXPECTED_STATE_E); - if (api_ret == 0) + api_ret = WC_TEST_RET_ENC_EC(UNEXPECTED_STATE_E); + if (api_ret == 0) { api_ret = wc_rng_bank_root_rng_init(bank, NULL, 0, NULL, 0, 0); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); + } if ((api_ret == 0) && (wc_rng_bank_root_rng_get(bank) != root)) - api_ret = WC_NO_ERR_TRACE(UNEXPECTED_STATE_E); - if (api_ret == 0) + api_ret = WC_TEST_RET_ENC_EC(UNEXPECTED_STATE_E); + if (api_ret == 0) { api_ret = wc_rng_bank_root_rng_reinit(bank, NULL, 0, NULL, 0, 0); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); + } if (bank_inited) { int fini_ret = wc_rng_bank_fini(bank); if ((api_ret == 0) && (fini_ret != 0)) - api_ret = fini_ret; + api_ret = WC_TEST_RET_ENC_EC(fini_ret); } WC_FREE_VAR(bank, HEAP_HINT); if (api_ret != 0) - ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out_l); + ERROR_OUT(api_ret, out_l); /* encoded at the failure site */ } #endif /* HAVE_WC_RNG_BANK && WC_RNG_BANK_HAVE_ROOT_RNG */ #endif /* WC_RNG_HAVE_LOCK_FULL_MUTEX */ @@ -36981,32 +37072,42 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_lifecycle_test(void) WC_RNG_BANK_FLAG_CAN_WAIT | WC_RNG_BANK_FLAG_QUIET, 10, HEAP_HINT, devId); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); if (api_ret == 0) bank_inited = 1; root = (api_ret == 0) ? wc_rng_bank_root_rng_get(bank) : NULL; if ((api_ret == 0) && (root == NULL)) - api_ret = WC_NO_ERR_TRACE(MISSING_RNG_E); + api_ret = WC_TEST_RET_ENC_EC(MISSING_RNG_E); /* take member 0 out of service, as a failed reinit would leave it */ if (api_ret == 0) { api_ret = wc_rng_bank_checkout(bank, &inst, 0, 10, WC_RNG_BANK_FLAG_NONE); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); if (api_ret == 0) { member = WC_RNG_BANK_INST_TO_RNG(inst); if (wc_RNG_DRBG_GetRBGCStratum(member) != 1) - api_ret = WC_NO_ERR_TRACE(UNEXPECTED_STATE_E); - else + api_ret = WC_TEST_RET_ENC_EC(UNEXPECTED_STATE_E); + else { api_ret = wc_FreeRng_PreLocked(member); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); + } { int ci_ret = wc_rng_bank_inst_checkin(&inst); if ((api_ret == 0) && (ci_ret != 0)) - api_ret = ci_ret; + api_ret = WC_TEST_RET_ENC_EC(ci_ret); } } } /* latch the root, as the invalidation walk would */ - if (api_ret == 0) + if (api_ret == 0) { api_ret = wc_RNG_invalidate_entropy(root); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); + } if (api_ret == 0) { api_ret = wc_rng_bank_recover_inst(bank, 0, 0, WC_RNG_BANK_FLAG_NONE); @@ -37014,50 +37115,63 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_lifecycle_test(void) /* refused, not substituted: still out of service */ member = WC_RNG_BANK_OFFSET_TO_RNG(bank, 0); if (wc_RNG_GetStatus(member) != WC_DRBG_NOT_INIT) - api_ret = WC_NO_ERR_TRACE(UNEXPECTED_STATE_E); + api_ret = WC_TEST_RET_ENC_EC(UNEXPECTED_STATE_E); else api_ret = 0; } else if (api_ret == 0) { - api_ret = WC_NO_ERR_TRACE(UNEXPECTED_STATE_E); + api_ret = WC_TEST_RET_ENC_EC(UNEXPECTED_STATE_E); + } + else { + api_ret = WC_TEST_RET_ENC_EC(api_ret); } } /* recover the root the bank's way (the daemon's root recovery: * reinstantiate in place, retaining its children), then the member */ - if (api_ret == 0) + if (api_ret == 0) { api_ret = wc_rng_bank_root_rng_reinit(bank, NULL, 0, NULL, 0, 0); - if (api_ret == 0) + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); + } + if (api_ret == 0) { api_ret = wc_rng_bank_recover_inst(bank, 0, 0, WC_RNG_BANK_FLAG_NONE); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); + } if (api_ret == 0) { member = WC_RNG_BANK_OFFSET_TO_RNG(bank, 0); if ((wc_RNG_GetStatus(member) != WC_DRBG_OK) || (wc_RNG_DRBG_GetRBGCStratum(member) != 1)) { - api_ret = WC_NO_ERR_TRACE(UNEXPECTED_STATE_E); + api_ret = WC_TEST_RET_ENC_EC(UNEXPECTED_STATE_E); } } if (api_ret == 0) { api_ret = wc_rng_bank_checkout(bank, &inst, 0, 10, WC_RNG_BANK_FLAG_NONE); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); if (api_ret == 0) { api_ret = wc_RNG_GenerateBlock(WC_RNG_BANK_INST_TO_RNG(inst), block, (word32)sizeof(block)); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); { int ci_ret = wc_rng_bank_inst_checkin(&inst); if ((api_ret == 0) && (ci_ret != 0)) - api_ret = ci_ret; + api_ret = WC_TEST_RET_ENC_EC(ci_ret); } } } if (bank_inited) { int fini_ret = wc_rng_bank_fini(bank); if ((api_ret == 0) && (fini_ret != 0)) - api_ret = fini_ret; + api_ret = WC_TEST_RET_ENC_EC(fini_ret); } WC_FREE_VAR(bank, HEAP_HINT); if (api_ret != 0) - ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out_l); + ERROR_OUT(api_ret, out_l); /* encoded at the failure site */ } #endif /* HAVE_WC_RNG_BANK && ROOT_RNG && RBGC && LOCK */ @@ -37087,58 +37201,81 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_lifecycle_test(void) WC_RNG_BANK_FLAG_CAN_WAIT | WC_RNG_BANK_FLAG_QUIET, 10, HEAP_HINT, devId); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); if (api_ret == 0) bank_inited = 1; root = (api_ret == 0) ? wc_rng_bank_root_rng_get(bank) : NULL; if ((api_ret == 0) && (root == NULL)) - api_ret = WC_NO_ERR_TRACE(MISSING_RNG_E); + api_ret = WC_TEST_RET_ENC_EC(MISSING_RNG_E); /* the walk: root and both members latched */ - if (api_ret == 0) + if (api_ret == 0) { api_ret = wc_rng_bank_invalidate_entropy(bank, 0); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); + } if (api_ret == 0) { if (! (WOLFSSL_ATOMIC_LOAD(root->lock) & WC_RNG_LOCK_ENTROPY_INVALIDATED)) - api_ret = WC_NO_ERR_TRACE(UNEXPECTED_STATE_E); + api_ret = WC_TEST_RET_ENC_EC(UNEXPECTED_STATE_E); } if (api_ret == 0) { api_ret = wc_rng_bank_inst_lock_read(&bank->rngs[1], &word); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); if ((api_ret == 0) && (! (word & WC_RNG_LOCK_ENTROPY_INVALIDATED))) { - api_ret = WC_NO_ERR_TRACE(UNEXPECTED_STATE_E); + api_ret = WC_TEST_RET_ENC_EC(UNEXPECTED_STATE_E); } } /* root recovered the bank's way: reinstantiated in place, children * retained, latch discharged */ - if (api_ret == 0) + if (api_ret == 0) { api_ret = wc_rng_bank_root_rng_reinit(bank, NULL, 0, NULL, 0, 0); - if (api_ret == 0) + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); + } + if (api_ret == 0) { api_ret = wc_RNG_entropy_needs_recovery(root); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); + } /* a member recovers under its holder's first generate */ if (api_ret == 0) { api_ret = wc_rng_bank_checkout(bank, &inst, 1, 10, WC_RNG_BANK_FLAG_MAYBE_FOR_RECOVERY); if (api_ret == WC_NO_ERR_TRACE(NEEDS_RECOVERY_E)) api_ret = 0; + else if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); if ((api_ret == 0) && (inst == NULL)) - api_ret = WC_NO_ERR_TRACE(UNEXPECTED_STATE_E); - if (api_ret == 0) + api_ret = WC_TEST_RET_ENC_EC(UNEXPECTED_STATE_E); + if (api_ret == 0) { api_ret = wc_RNG_GenerateBlock(WC_RNG_BANK_INST_TO_RNG(inst), block, (word32)sizeof(block)); - if (api_ret == 0) + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); + } + if (api_ret == 0) { api_ret = wc_RNG_entropy_needs_recovery( WC_RNG_BANK_INST_TO_RNG(inst)); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); + } if (inst != NULL) { int ci_ret = wc_rng_bank_inst_checkin(&inst); if ((api_ret == 0) && (ci_ret != 0)) - api_ret = ci_ret; + api_ret = WC_TEST_RET_ENC_EC(ci_ret); } } /* the walk against a root mid-reinit */ if (api_ret == 0) { api_ret = wc_RNG_lock_get(root, 0); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); if (api_ret == 0) root_held = 1; } @@ -37148,15 +37285,20 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_lifecycle_test(void) api_ret = wc_FreeRng_PreLocked(root); if (api_ret == WC_NO_ERR_TRACE(STILL_REFERENCED_E)) api_ret = 0; + else if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); } if ((api_ret == 0) && (wc_RNG_GetStatus(root) != WC_DRBG_NOT_INIT)) - api_ret = WC_NO_ERR_TRACE(UNEXPECTED_STATE_E); - if (api_ret == 0) + api_ret = WC_TEST_RET_ENC_EC(UNEXPECTED_STATE_E); + if (api_ret == 0) { api_ret = wc_rng_bank_invalidate_entropy(bank, 0); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); + } if (api_ret == 0) { if (! (WOLFSSL_ATOMIC_LOAD(root->lock) & WC_RNG_LOCK_ENTROPY_INVALIDATED)) - api_ret = WC_NO_ERR_TRACE(UNEXPECTED_STATE_E); + api_ret = WC_TEST_RET_ENC_EC(UNEXPECTED_STATE_E); } /* the reinit's entry discharge retires the latch: its material is * acquired after the event */ @@ -37165,31 +37307,42 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_lifecycle_test(void) devId, WC_RNG_INIT_FLAG_LOCK_REQUIRED | WC_RNG_INIT_FLAG_PRESERVE_LOCK); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); } if (root_held) { int put_ret = wc_RNG_lock_put(root, 0); root_held = 0; if ((api_ret == 0) && (put_ret != 0)) - api_ret = put_ret; + api_ret = WC_TEST_RET_ENC_EC(put_ret); } - if (api_ret == 0) + if (api_ret == 0) { api_ret = wc_RNG_entropy_needs_recovery(root); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); + } if (api_ret == 0) { /* and the members, latched by that walk, recover from it */ api_ret = wc_rng_bank_reseed_range(bank, 0, -1, NULL, 0, 10, WC_RNG_BANK_FLAG_CAN_WAIT | WC_RNG_BANK_FLAG_RBGC); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); } if (api_ret == 0) { api_ret = wc_rng_bank_checkout(bank, &inst, 0, 10, WC_RNG_BANK_FLAG_NONE); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); if (api_ret == 0) { api_ret = wc_RNG_GenerateBlock(WC_RNG_BANK_INST_TO_RNG(inst), block, (word32)sizeof(block)); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); { int ci_ret = wc_rng_bank_inst_checkin(&inst); if ((api_ret == 0) && (ci_ret != 0)) - api_ret = ci_ret; + api_ret = WC_TEST_RET_ENC_EC(ci_ret); } } } @@ -37200,11 +37353,11 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_lifecycle_test(void) if (bank_inited) { int fini_ret = wc_rng_bank_fini(bank); if ((api_ret == 0) && (fini_ret != 0)) - api_ret = fini_ret; + api_ret = WC_TEST_RET_ENC_EC(fini_ret); } WC_FREE_VAR(bank, HEAP_HINT); if (api_ret != 0) - ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out_l); + ERROR_OUT(api_ret, out_l); /* encoded at the failure site */ } #endif /* HAVE_WC_RNG_BANK && ROOT_RNG && RBGC && LOCK && PRESERVE_LOCK */ @@ -37235,30 +37388,36 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_lifecycle_test(void) WC_RNG_BANK_FLAG_CAN_WAIT | WC_RNG_BANK_FLAG_QUIET, 10, HEAP_HINT, devId); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); if (api_ret == 0) bank_inited = 1; root = (api_ret == 0) ? wc_rng_bank_root_rng_get(bank) : NULL; if ((api_ret == 0) && (root == NULL)) - api_ret = WC_NO_ERR_TRACE(MISSING_RNG_E); + api_ret = WC_TEST_RET_ENC_EC(MISSING_RNG_E); /* spawn under the caller-held root lease: the child pins the root * alongside the two members' pins. */ if (api_ret == 0) { api_ret = wc_RNG_lock_get(root, 0); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); if (api_ret == 0) { api_ret = wc_InitRngNonceRBGC(&child, root, NULL, 0, NULL, 0, 0); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); if (api_ret == 0) child_inited = 1; { int put_ret = wc_RNG_lock_put(root, 0); if ((api_ret == 0) && (put_ret != 0)) - api_ret = put_ret; + api_ret = WC_TEST_RET_ENC_EC(put_ret); } } } if ((api_ret == 0) && (child.RBGC_parent != root)) - api_ret = WC_NO_ERR_TRACE(UNEXPECTED_STATE_E); + api_ret = WC_TEST_RET_ENC_EC(UNEXPECTED_STATE_E); /* refused with the bank intact: root alive, members in service */ if (api_ret == 0) { @@ -37266,24 +37425,30 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_lifecycle_test(void) if (api_ret == WC_NO_ERR_TRACE(STILL_REFERENCED_E)) api_ret = 0; else if (api_ret == 0) - api_ret = WC_NO_ERR_TRACE(UNEXPECTED_STATE_E); + api_ret = WC_TEST_RET_ENC_EC(UNEXPECTED_STATE_E); + else + api_ret = WC_TEST_RET_ENC_EC(api_ret); } if ((api_ret == 0) && ((wc_rng_bank_root_rng_get(bank) != root) || (wc_RNG_GetStatus(root) != WC_DRBG_OK))) { - api_ret = WC_NO_ERR_TRACE(UNEXPECTED_STATE_E); + api_ret = WC_TEST_RET_ENC_EC(UNEXPECTED_STATE_E); } if (api_ret == 0) { api_ret = wc_rng_bank_checkout(bank, &inst, 0, 10, WC_RNG_BANK_FLAG_NONE); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); if (api_ret == 0) { api_ret = wc_RNG_GenerateBlock(WC_RNG_BANK_INST_TO_RNG(inst), block, (word32)sizeof(block)); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); { int ci_ret = wc_rng_bank_inst_checkin(&inst); if ((api_ret == 0) && (ci_ret != 0)) - api_ret = ci_ret; + api_ret = WC_TEST_RET_ENC_EC(ci_ret); } } } @@ -37291,14 +37456,17 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_lifecycle_test(void) /* a held root is BUSY_E, likewise with no side effects */ if (api_ret == 0) { api_ret = wc_RNG_lock_get(root, 0); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); if (api_ret == 0) { int fini_ret = wc_rng_bank_fini(bank); int put_ret = wc_RNG_lock_put(root, 0); if (fini_ret != WC_NO_ERR_TRACE(BUSY_E)) api_ret = (fini_ret == 0) ? - WC_NO_ERR_TRACE(UNEXPECTED_STATE_E) : fini_ret; - else - api_ret = put_ret; + WC_TEST_RET_ENC_EC(UNEXPECTED_STATE_E) : + WC_TEST_RET_ENC_EC(fini_ret); + else if (put_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(put_ret); } } @@ -37309,41 +37477,45 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_lifecycle_test(void) if (api_ret == WC_NO_ERR_TRACE(STILL_REFERENCED_E)) api_ret = 0; else if (api_ret == 0) - api_ret = WC_NO_ERR_TRACE(UNEXPECTED_STATE_E); + api_ret = WC_TEST_RET_ENC_EC(UNEXPECTED_STATE_E); + else + api_ret = WC_TEST_RET_ENC_EC(api_ret); } if ((api_ret == 0) && (wc_RNG_GetStatus(root) != WC_DRBG_NOT_INIT)) - api_ret = WC_NO_ERR_TRACE(UNEXPECTED_STATE_E); + api_ret = WC_TEST_RET_ENC_EC(UNEXPECTED_STATE_E); if (api_ret == 0) { api_ret = wc_rng_bank_fini(bank); if (api_ret == WC_NO_ERR_TRACE(STILL_REFERENCED_E)) api_ret = 0; else if (api_ret == 0) - api_ret = WC_NO_ERR_TRACE(UNEXPECTED_STATE_E); + api_ret = WC_TEST_RET_ENC_EC(UNEXPECTED_STATE_E); + else + api_ret = WC_TEST_RET_ENC_EC(api_ret); } /* intact: the members are still in service */ if ((api_ret == 0) && (wc_RNG_GetStatus(WC_RNG_BANK_OFFSET_TO_RNG(bank, 0)) != WC_DRBG_OK)) { - api_ret = WC_NO_ERR_TRACE(UNEXPECTED_STATE_E); + api_ret = WC_TEST_RET_ENC_EC(UNEXPECTED_STATE_E); } /* the child's release unpins the root; teardown then converges. */ if (child_inited) { int free_ret = wc_FreeRng(&child); if ((api_ret == 0) && (free_ret != 0)) - api_ret = free_ret; + api_ret = WC_TEST_RET_ENC_EC(free_ret); child_inited = 0; (void)child_inited; } if (bank_inited) { int fini_ret = wc_rng_bank_fini(bank); if ((api_ret == 0) && (fini_ret != 0)) - api_ret = fini_ret; + api_ret = WC_TEST_RET_ENC_EC(fini_ret); } WC_FREE_VAR(bank, HEAP_HINT); if (api_ret != 0) - ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out_l); + ERROR_OUT(api_ret, out_l); /* encoded at the failure site */ } #endif /* HAVE_WC_RNG_BANK && ROOT_RNG && RBGC && LOCK && RBGC_REFCOUNT */ @@ -37365,13 +37537,15 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_lifecycle_test(void) WC_RNG_BANK_FLAG_CAN_WAIT | WC_RNG_BANK_FLAG_QUIET, 10, HEAP_HINT, devId); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); if (api_ret == 0) bank_inited = 1; if ((api_ret == 0) && (wc_RNG_DRBG_GetRBGCStratum(WC_RNG_BANK_OFFSET_TO_RNG(bank, 0)) != 0)) { - api_ret = WC_NO_ERR_TRACE(UNEXPECTED_STATE_E); + api_ret = WC_TEST_RET_ENC_EC(UNEXPECTED_STATE_E); } if (api_ret == 0) { time_t ts1 = XTIME(0); @@ -37380,27 +37554,33 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_lifecycle_test(void) WC_RNG_BANK_FLAG_RBGC); if (api_ret == WC_NO_ERR_TRACE(WRONG_TYPE_OBJECT_E)) { if (XTIME(0) - ts1 >= 3) - api_ret = WC_NO_ERR_TRACE(WC_TIMEOUT_E); + api_ret = WC_TEST_RET_ENC_EC(WC_TIMEOUT_E); else api_ret = 0; } else if (api_ret == 0) { - api_ret = WC_NO_ERR_TRACE(UNEXPECTED_STATE_E); + api_ret = WC_TEST_RET_ENC_EC(UNEXPECTED_STATE_E); + } + else { + api_ret = WC_TEST_RET_ENC_EC(api_ret); } } /* the members are untouched by the refusal: a primary reseed walk * of the same range succeeds */ - if (api_ret == 0) + if (api_ret == 0) { api_ret = wc_rng_bank_reseed_range(bank, 0, -1, NULL, 0, 3, WC_RNG_BANK_FLAG_CAN_WAIT); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); + } if (bank_inited) { int fini_ret = wc_rng_bank_fini(bank); if ((api_ret == 0) && (fini_ret != 0)) - api_ret = fini_ret; + api_ret = WC_TEST_RET_ENC_EC(fini_ret); } WC_FREE_VAR(bank, HEAP_HINT); if (api_ret != 0) - return WC_TEST_RET_ENC_EC(api_ret); + return api_ret; /* encoded at the failure site */ } #endif /* HAVE_WC_RNG_BANK && ROOT_RNG && RBGC && STRATUM_IMMUTABLE */ @@ -37422,25 +37602,33 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_lifecycle_test(void) WC_RNG_BANK_FLAG_CAN_WAIT | WC_RNG_BANK_FLAG_QUIET, 10, HEAP_HINT, devId); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); if (api_ret == 0) bank_inited = 1; root = (api_ret == 0) ? wc_rng_bank_root_rng_get(bank) : NULL; if ((api_ret == 0) && (root == NULL)) - api_ret = WC_NO_ERR_TRACE(MISSING_RNG_E); - if (api_ret == 0) + api_ret = WC_TEST_RET_ENC_EC(MISSING_RNG_E); + if (api_ret == 0) { api_ret = wc_rng_bank_root_rng_reinit(bank, NULL, 0, NULL, 0, 0); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); + } if ((api_ret == 0) && (wc_RNG_GetStatus(root) != WC_DRBG_OK)) - api_ret = WC_NO_ERR_TRACE(UNEXPECTED_STATE_E); - if (api_ret == 0) + api_ret = WC_TEST_RET_ENC_EC(UNEXPECTED_STATE_E); + if (api_ret == 0) { api_ret = wc_RNG_GenerateBlock(root, block, (word32)sizeof(block)); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); + } if (bank_inited) { int fini_ret = wc_rng_bank_fini(bank); if ((api_ret == 0) && (fini_ret != 0)) - api_ret = fini_ret; + api_ret = WC_TEST_RET_ENC_EC(fini_ret); } WC_FREE_VAR(bank, HEAP_HINT); if (api_ret != 0) - return WC_TEST_RET_ENC_EC(api_ret); + return api_ret; /* encoded at the failure site */ } #ifdef WC_RNG_HAVE_RBGC @@ -37465,29 +37653,38 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_lifecycle_test(void) WC_RNG_BANK_FLAG_CAN_WAIT | WC_RNG_BANK_FLAG_QUIET, 10, HEAP_HINT, devId); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); if (api_ret == 0) bank_inited = 1; root = (api_ret == 0) ? wc_rng_bank_root_rng_get(bank) : NULL; if ((api_ret == 0) && (root == NULL)) - api_ret = WC_NO_ERR_TRACE(MISSING_RNG_E); + api_ret = WC_TEST_RET_ENC_EC(MISSING_RNG_E); /* take member 0 out of service, then the root */ if (api_ret == 0) { api_ret = wc_rng_bank_checkout(bank, &inst, 0, 10, WC_RNG_BANK_FLAG_NONE); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); if (api_ret == 0) { api_ret = wc_FreeRng(WC_RNG_BANK_INST_TO_RNG(inst)); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); { int ci_ret = wc_rng_bank_inst_checkin(&inst); if ((api_ret == 0) && (ci_ret != 0)) - api_ret = ci_ret; + api_ret = WC_TEST_RET_ENC_EC(ci_ret); } } } - if (api_ret == 0) + if (api_ret == 0) { api_ret = wc_FreeRng(root); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); + } if ((api_ret == 0) && (wc_RNG_GetStatus(root) != WC_DRBG_NOT_INIT)) - api_ret = WC_NO_ERR_TRACE(UNEXPECTED_STATE_E); + api_ret = WC_TEST_RET_ENC_EC(UNEXPECTED_STATE_E); /* the root's verdict, promptly: well inside the 3 s timeout */ if (api_ret == 0) { @@ -37496,43 +37693,52 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_lifecycle_test(void) WC_RNG_BANK_FLAG_CAN_WAIT); if (api_ret == WC_NO_ERR_TRACE(NOT_READY_E)) { if (XTIME(0) - ts1 >= 3) - api_ret = WC_NO_ERR_TRACE(WC_TIMEOUT_E); + api_ret = WC_TEST_RET_ENC_EC(WC_TIMEOUT_E); else api_ret = 0; } else if (api_ret == 0) { - api_ret = WC_NO_ERR_TRACE(UNEXPECTED_STATE_E); + api_ret = WC_TEST_RET_ENC_EC(UNEXPECTED_STATE_E); + } + else { + api_ret = WC_TEST_RET_ENC_EC(api_ret); } } if ((api_ret == 0) && (wc_RNG_GetStatus(WC_RNG_BANK_OFFSET_TO_RNG(bank, 0)) != WC_DRBG_NOT_INIT)) { - api_ret = WC_NO_ERR_TRACE(UNEXPECTED_STATE_E); + api_ret = WC_TEST_RET_ENC_EC(UNEXPECTED_STATE_E); } /* root recovered (the gate is free), then the member */ - if (api_ret == 0) + if (api_ret == 0) { api_ret = wc_rng_bank_root_rng_reinit(bank, NULL, 0, NULL, 0, 0); - if (api_ret == 0) + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); + } + if (api_ret == 0) { api_ret = wc_rng_bank_recover_inst(bank, 0, 3, WC_RNG_BANK_FLAG_CAN_WAIT); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); + } if (api_ret == 0) { member = WC_RNG_BANK_OFFSET_TO_RNG(bank, 0); if ((wc_RNG_GetStatus(member) != WC_DRBG_OK) || (wc_RNG_DRBG_GetRBGCStratum(member) != 1)) { - api_ret = WC_NO_ERR_TRACE(UNEXPECTED_STATE_E); + api_ret = WC_TEST_RET_ENC_EC(UNEXPECTED_STATE_E); } } if (bank_inited) { int fini_ret = wc_rng_bank_fini(bank); if ((api_ret == 0) && (fini_ret != 0)) - api_ret = fini_ret; + api_ret = WC_TEST_RET_ENC_EC(fini_ret); } WC_FREE_VAR(bank, HEAP_HINT); if (api_ret != 0) - return WC_TEST_RET_ENC_EC(api_ret); + return api_ret; /* encoded at the failure site */ } #endif /* WC_RNG_HAVE_RBGC */ #endif /* HAVE_WC_RNG_BANK && ROOT_RNG && !LOCK */ @@ -37599,6 +37805,8 @@ static void rng_lock_fork_child_held(WC_RNG* rng, int adopt, /* The invalidation purged the aperture: the parent's banked seed, which * the parent still holds, is not on offer here. */ api_ret = wc_RNG_DRBG_NextSeedCurrent(rng, &cur); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); if ((api_ret == 0) && (cur == WC_DRBG_NEXT_SEED_READY)) { r->step = 3; r->code = (int)cur; return; } @@ -37610,12 +37818,16 @@ static void rng_lock_fork_child_held(WC_RNG* rng, int adopt, * latched) word -- which discharges the latch -- and the lease is a * live one of this process. */ api_ret = wc_FreeRng_PreLocked(rng); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); if (api_ret != 0) { r->step = 4; r->code = api_ret; return; } api_ret = wc_InitRng_ex2(rng, HEAP_HINT, devId, RNG_LOCK_FORK_FLAGS | WC_RNG_INIT_FLAG_PRESERVE_LOCK); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); if (api_ret != 0) { r->step = 5; r->code = api_ret; return; } @@ -37624,6 +37836,8 @@ static void rng_lock_fork_child_held(WC_RNG* rng, int adopt, * the latch is the recovery -- a credited reseed, stamped with this * pid, same as any post-fork first use. */ api_ret = wc_RNG_GenerateBlock(rng, block, sizeof(block)); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); if (api_ret != 0) { r->step = 6; r->code = api_ret; return; } @@ -37634,15 +37848,21 @@ static void rng_lock_fork_child_held(WC_RNG* rng, int adopt, r->step = 7; r->code = api_ret; return; } api_ret = wc_RNG_lock_put(rng, 0); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); if (api_ret != 0) { r->step = 8; r->code = api_ret; return; } /* and now it is a free instance of this process */ api_ret = wc_RNG_lock_get(rng, 0); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); if (api_ret != 0) { r->step = 9; r->code = api_ret; return; } api_ret = wc_FreeRng_PreLocked(rng); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); if (api_ret != 0) { r->step = 10; r->code = api_ret; return; } @@ -37661,14 +37881,20 @@ static void rng_lock_fork_child_free(WC_RNG* rng, r->code = 0; api_ret = wc_RNG_lock_get(rng, 0); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); if (api_ret != 0) { r->step = 1; r->code = api_ret; return; } api_ret = wc_RNG_GenerateBlock(rng, block, sizeof(block)); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); if (api_ret != 0) { r->step = 2; r->code = api_ret; return; } api_ret = wc_FreeRng_PreLocked(rng); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); if (api_ret != 0) { r->step = 3; r->code = api_ret; return; } From e6d5f1869de7bb449996e6bc5ff995400eb37586 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Wed, 28 Oct 2026 23:52:13 -0500 Subject: [PATCH 10/12] wolfcrypt/test/test.c: add test coverage in rng_lifecycle_test() for wc_rng_bank_root_rng_reseed_now() and wc_rng_bank_root_rng_reseed_if_due(). also, in wc_rng_bank_root_rng_reseed_if_due(), add WC_NO_ERR_TRACE() around the ALREADY_E, to avoid polling noise. --- wolfcrypt/src/rng_bank.c | 2 +- wolfcrypt/test/test.c | 93 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 94 insertions(+), 1 deletion(-) diff --git a/wolfcrypt/src/rng_bank.c b/wolfcrypt/src/rng_bank.c index c5e9a5ce1c7..4ce65e8237a 100644 --- a/wolfcrypt/src/rng_bank.c +++ b/wolfcrypt/src/rng_bank.c @@ -1683,7 +1683,7 @@ WOLFSSL_API int wc_rng_bank_root_rng_reseed_if_due( wc_drbg_reseed_ctr_t cur_ctr = 0; ret = wc_RNG_DRBG_GetReseedCtr(&bank->root_rng, &cur_ctr); if ((ret == 0) && (cur_ctr < threshold)) - ret = ALREADY_E; + ret = WC_NO_ERR_TRACE(ALREADY_E); /* avoid polling noise. */ } else { ret = 0; diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index c8ece868624..ae6b47177b6 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -37242,6 +37242,99 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_lifecycle_test(void) if (api_ret != 0) api_ret = WC_TEST_RET_ENC_EC(api_ret); } + + if (api_ret == 0) { + byte scratch[4]; + /* Increment the reseed counter. */ + api_ret = wc_RNG_lock_get(root, 0); + if (api_ret == 0) { + int unlock_ret; + api_ret = wc_RNG_GenerateBlock(root, scratch, + (word32)sizeof(scratch)); + if (api_ret == 0) { + /* Make sure _root_rng_reseed_now() is trying the lock. */ + api_ret = wc_rng_bank_root_rng_reseed_now(bank, NULL, 0); + if (api_ret == WC_NO_ERR_TRACE(BUSY_E)) + api_ret = 0; + else + api_ret = WC_TEST_RET_ENC_EC(api_ret); + } + else + api_ret = WC_TEST_RET_ENC_EC(api_ret); + unlock_ret = wc_RNG_lock_put(root, 0); + if ((unlock_ret != 0) && (api_ret == 0)) + api_ret = WC_TEST_RET_ENC_EC(unlock_ret); + } + else + api_ret = WC_TEST_RET_ENC_EC(api_ret); + } + if (api_ret == 0) { + wc_drbg_reseed_ctr_t start_root_reseed_ctr; + start_root_reseed_ctr = RESEED_CTR(root); + if ((start_root_reseed_ctr <= 1) || + (start_root_reseed_ctr >= WC_RESEED_INTERVAL)) + { + api_ret = WC_TEST_RET_ENC_I(start_root_reseed_ctr); + } + if (api_ret == 0) { + api_ret = wc_rng_bank_root_rng_reseed_if_due( + bank, NULL, 0, start_root_reseed_ctr + 1); + if (api_ret == WC_NO_ERR_TRACE(ALREADY_E)) { + if (RESEED_CTR(root) != start_root_reseed_ctr) + api_ret = WC_TEST_RET_ENC_I(RESEED_CTR(root)); + else + api_ret = 0; + } + else + api_ret = WC_TEST_RET_ENC_EC(api_ret); + } + if (api_ret == 0) { + api_ret = wc_rng_bank_root_rng_reseed_if_due( + bank, NULL, 0, start_root_reseed_ctr); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); + } + if (api_ret == 0) { + if (RESEED_CTR(root) > 1) + api_ret = WC_TEST_RET_ENC_I(RESEED_CTR(root)); + } + } + if (api_ret == 0) { + byte scratch[4]; + /* Increment the reseed counter. */ + api_ret = wc_RNG_lock_get(root, 0); + if (api_ret == 0) { + int unlock_ret; + api_ret = wc_RNG_GenerateBlock(root, scratch, + (word32)sizeof(scratch)); + if (api_ret != 0) + api_ret = WC_TEST_RET_ENC_EC(api_ret); + unlock_ret = wc_RNG_lock_put(root, 0); + if ((unlock_ret != 0) && (api_ret == 0)) + api_ret = WC_TEST_RET_ENC_EC(unlock_ret); + } + else + api_ret = WC_TEST_RET_ENC_EC(api_ret); + } + if (api_ret == 0) { + wc_drbg_reseed_ctr_t start_root_reseed_ctr; + start_root_reseed_ctr = RESEED_CTR(root); + if ((start_root_reseed_ctr <= 1) || + (start_root_reseed_ctr >= WC_RESEED_INTERVAL)) + { + api_ret = WC_TEST_RET_ENC_I(start_root_reseed_ctr); + } + if (api_ret == 0) { + api_ret = wc_rng_bank_root_rng_reseed_now(bank, NULL, 0); + if (api_ret == 0) { + if (RESEED_CTR(root) > 1) + api_ret = WC_TEST_RET_ENC_I(RESEED_CTR(root)); + } + else + api_ret = WC_TEST_RET_ENC_EC(api_ret); + } + } + /* a member recovers under its holder's first generate */ if (api_ret == 0) { api_ret = wc_rng_bank_checkout(bank, &inst, 1, 10, From 03efa925566627b86cab83a972f8920f4810a80d Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Thu, 29 Oct 2026 00:02:38 -0500 Subject: [PATCH 11/12] linuxkm/lkcapi_sha_glue.c: add some comments in wc_linuxkm_entropy_daemon() explaining logging logic. --- linuxkm/lkcapi_sha_glue.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/linuxkm/lkcapi_sha_glue.c b/linuxkm/lkcapi_sha_glue.c index 675712cadff..ce60d5f4386 100644 --- a/linuxkm/lkcapi_sha_glue.c +++ b/linuxkm/lkcapi_sha_glue.c @@ -3162,6 +3162,9 @@ static int wc_linuxkm_entropy_daemon(void *arg) "wc_entropyd: root_rng reinitialization failed: " "%d\n", ret); } + /* else transient codes (a busy seed source -- gate + * contention is spun out, not returned) are deliberately + * unlogged: the next pass retries. */ } else { unsigned long uncredited_nonce = random_get_entropy(); @@ -3200,6 +3203,7 @@ static int wc_linuxkm_entropy_daemon(void *arg) "wc_entropyd: root_rng recovery reseed failed: " "%d\n", ret); } + /* else don't log -- see above comment. */ } } #else /* !WC_RNG_BANK_HAVE_ROOT_RNG */ @@ -3220,6 +3224,7 @@ static int wc_linuxkm_entropy_daemon(void *arg) "wc_entropyd: root_rng reinitialization failed: " "%d\n", ret); } + /* else transient -- don't log. */ } #ifdef WC_RNG_HAVE_ENTROPY_EPOCH else if (wc_RNG_entropy_needs_recovery(root_rng) != 0) { @@ -3237,6 +3242,7 @@ static int wc_linuxkm_entropy_daemon(void *arg) "wc_entropyd: root_rng recovery reseed failed: " "%d\n", ret); } + /* else transient -- don't log. */ } #endif /* WC_RNG_HAVE_ENTROPY_EPOCH */ } From f9dc9e1f2ef9f4223c1b412022d44c8fff664fda Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Thu, 29 Oct 2026 09:12:53 -0500 Subject: [PATCH 12/12] wolfcrypt/test/test.c: fix double-WC_TEST_RET_ENC_EC flub in rng_epoch_test(). --- wolfcrypt/test/test.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index ae6b47177b6..ab633c5a41f 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -36671,8 +36671,6 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rng_epoch_test(void) if (api_ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out_l); api_ret = wc_RNG_DRBG_ScheduleReseed(rng); - if (api_ret != 0) - api_ret = WC_TEST_RET_ENC_EC(api_ret); if (api_ret != 0) { (void)wc_SetSeed_Cb(WC_GENERATE_SEED_DEFAULT); ERROR_OUT(WC_TEST_RET_ENC_EC(api_ret), out_l);