rand_thread_loop feeds generator output back into the entropy pools, so the MIN_POOL_SIZE reseed gate can be satisfied with zero external entropy
| Field | Value |
|---|---|
| ID | DF-2821 |
| Status | new |
| Severity | Low |
| CVSS 3.1 | CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:N/I:N/A:L |
| CWE | CWE-338 Use of Cryptographically Weak PRNG |
| File | sys/kern/kern_nrandom.c |
| Lines | 886-898 (gate subr_csprng.c:188) |
| Area | kern |
| Confidence | likely |
| Discovered | 2026-08-31 |
| Pass | 2 (GLM 5.3 second pass) |
| Bucket | base:kern |
| Reported | pending |
| Known CVE | none |
| CVE match | novel |
Summary
Every ~100-200ms the random helper thread adds the PREVIOUS iteration's read_random() output (32 bytes) into the same state's pools as RAND_SRC_THREAD1, plus per-cpu globaldata counters. csprng_reseed's only freshness gate is pool[0].bytes >= MIN_POOL_SIZE β a raw byte counter with no entropy estimate. Under total external-entropy starvation (quiet appliance/vkernel, no RDRAND/virtio-rng/keyboard) the self-feedback and predictable per-cpu counters keep the byte count above threshold forever, so reseeds keep deriving new keys purely from old generator state and boot-time timing; /dev/random's limited reads never stall and the CSPRNG presents a continuously-reseeded facade while its key evolution is a deterministic function of the initial weak seed. The entropy-starvation-masking complement to DF-0220 (pre-first-reseed window) and DF-0068 (IBAA gate) β a distinct post-reseed self-feed mechanism. On entropy-starved machines long-term keys (TCP ISN secrets, kern_uuid, sppp seeds) continue to be derived with no fresh unpredictability while operators believe the blocking gate guarantees entropy; the failure is silent and unbounded in time.
Recommended fix
Never credit self-generated or low-assurance data toward the reseed gate: track an estimated-entropy counter per pool incremented only by external sources, gate reseeds on that counter, and feed generator chaining through a separate non-credited channel (add_buffer_randomness_state_nocredit) β sketch diff in the row above.
Timeline
- 2026-08-31 Discovered during pass-2 audit of kern_nrandom.c (GLM 5.3).
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-2821 Β· 1 files| File | Type | Description | Size | |
|---|---|---|---|---|
| fix.diff | β | 795 B | view raw |
Fix verification
not_testableSelf-feedback removal compile-validated; starvation precondition not producible for A/B.
compile-validated in the fix-all kernel build (make nativekernel, 0 errors)
Confirmed kernel references
Detail
Evidence (decisive lines)
kern_nrandom.c:886-898: add_buffer_randomness_state(buf=read_random output) + :898 read_random refresh
PoC changes
n/a
Verified recommended fix
--- sys/kern/kern_nrandom.c 2026-06-29 12:51:19.000000000 +0000
Verdict
Code-verified entropy-accounting flaw; not observable on a live guest. rand_thread_loop (sys/kern/kern_nrandom.c:886-898) feeds the PREVIOUS iterations read_random() output (buf) into the same pools as RAND_SRC_THREAD1, plus per-cpu counters, while csprng_reseeds only freshness gate is pool[0].bytes >= MIN_POOL_SIZE - a raw byte counter with no entropy estimate. Under total external-entropy starvation the self-feedback keeps the counter above threshold forever, so reseeds derive keys purely from old state and boot timing while /dev/random never stalls. On this guest interrupts, the systimer, RDRAND and the storage layer all inject entropy, so the starvation precondition cannot be produced (and the internal pool counters are not exported via sysctl to measure the split between credited external entropy and self-feedback). The fix (stop crediting generator output as pool input) is compile-validated; a complete fix needs per-pool estimated-entropy accounting.
No comments yet.