⬢ DragonFlyBSD Kernel Audit
DF-0220 / leak_sample.txt
← back to finding ↓ download raw
================================================================================
DF-0220 — cross-boot RNG comparison (2026-10-12, three fresh boots)
================================================================================
Method: vm.sh reset with-src (reverts disk) -> boot -> sh boot_probe.sh at the
earliest possible ssh (uptime 17-18 s).  Unprivileged user (maxx, uid 1001)
reads 64 bytes from /dev/urandom, getrandom(2) and kern.random (sysctl,
CTLFLAG_ANYBODY).  boot{1,2,3}_probe.txt hold the full untrimmed outputs.

CLAIM UNDER TEST (finding): outputs are byte-for-byte IDENTICAL across boots
(deterministic zero-key chacha20 keystream before the first reseed).

RESULT: REFUTED on all three interfaces, all three boot pairs:

== /dev/urandom ==
  boot1 vs boot2: differ in 59/64 bytes
  boot1 vs boot3: differ in 60/64 bytes
  boot2 vs boot3: differ in 60/64 bytes
  boot1 vs all-zero-ref: differ (59/64 bytes nonzero)
== getrandom(2) ==
  boot1 vs boot2: differ in 60/64 bytes
  boot1 vs boot3: differ in 60/64 bytes
  boot2 vs boot3: differ in 60/64 bytes
  boot1 vs all-zero-ref: differ (60/64 bytes nonzero)
== kern.random sysctl ==
  boot1 vs boot2: differ in 60/64 bytes
  boot1 vs boot3: differ in 60/64 bytes
  boot2 vs boot3: differ in 60/64 bytes
  boot1 vs all-zero-ref: differ (60/64 bytes nonzero)

(The 4-5 coincidentally-equal bytes per pair are consistent with mixed-mode
output structure, not with a shared deterministic stream; an actual
deterministic stream would match 64/64.)

Unprivileged early-boot window scan (zero_scan, 32-byte blocks, uid=1001,
uptime 18-33 s of each boot):
  boot1: samples: urandom=736 getrandom=736 kern.random=736; all-zero: 0 0 0
  boot2: samples: urandom=736 getrandom=736 kern.random=736; all-zero: 0 0 0
  boot3: samples: urandom=736 getrandom=736 kern.random=736; all-zero: 0 0 0
  -> 6624 blocks sampled in the first ~33 s after boot, ZERO all-zero blocks.

Per-CPU csprng state at the earliest ssh moment of each fresh boot
(csprng_force_zero info, root; full dumps in boot{1,2,3}_probe.txt):
  boot1 (uptime ~33 s): all states reseed_cnt>=1, key=NONZERO;
      state[0] (BSP): reseed_cnt=1, last_reseed=184467440735.644667 (garbage)
  boot2: state[0] reseed_cnt=1, last_reseed=184467440735.668736; others 4-13
  boot3: state[0] reseed_cnt=1, last_reseed=184467440735.652136; others 4-13

  The BSP's frozen timestamp IS the getmicrouptime() value captured when its
  first (and only -- see DF-5766) reseed ran inside rand_initialize() at
  SI_BOOT2_POST_SMP, i.e. BEFORE init(8) existed.  Different usec fraction
  per boot, same mechanism.  No state ever presents reseed_cnt==0 to
  userspace: the earliest observable moment already shows every state keyed.

================================================================================
WHY: root cause (line-cited) -- see VERDICT.md for the full trace
================================================================================
rand_initialize() (kern_nrandom.c:485-560, SYSINIT SI_BOOT2_POST_SMP :562)
pre-feeds EVERY per-CPU state's pool[0] with ~15 KB -- dominated by
sizeof(struct globaldata)=14976 B via RAND_SRC_THREAD2 (:539-543) -- BEFORE
any read_random can run on that state.  csprng_get_random()
(subr_csprng.c:134-149) always attempts a reseed BEFORE its emit loop
(:151-166), and ratecheck()'s {0,0} first-call special case
(kern_time.c:1153-1157) guarantees that attempt fires on each state's first
read.  With pool[0] >= MIN_POOL_SIZE=96 (subr_csprng.c:54,:188) the reseed
succeeds and keys the cipher (chacha_keysetup :231) before a single byte is
emitted.  Userspace cannot exist before SI_SUB_KTHREAD_INIT=0xe000000 >>
SI_BOOT2_POST_SMP=0x1cc0000, so the un-keyed window is never observable by
an unprivileged (or any) user.

For what the window WOULD emit (mechanism proof with a root-manufactured
state), see kmem_window_decisive.log, kmem_window_chain.log, chain_model.py.