# DF-5695 — per-CPU RC4 state machine is unsynchronized under LWKT preemption

**File:** `sys/libkern/arc4random.c` (pass-2 audit)
**Class:** CWE-362 race on a security primitive / RNG correctness (CWE-338)
**Verdict:** reproduced (invariant-break demonstrated on exact source;
reachability of concurrent same-CPU callers established in the deployed
kernel's call graph)

## Root cause

`karc4random()` / `karc4random64()` / `karc4random_buf()`
(sys/libkern/arc4random.c:101-162) mutate the per-CPU RC4 state
`arc4_data_pcpu[mycpuid]` (i, j, sbox, numruns) with **no serialization**.
`karc4random_buf()` even carries the guard commented out:

```c
#if 0
	/* No one call this function in ISR/ithread. */
	crit_enter();
#endif
```
(arc4random.c:147-150) — and `karc4random`/`karc4random64` have no guard at
all. The premise "no one calls this in ISR/ithread" is **false for thread
preemption**: the generator is called from

* user/syscall thread context — `kern_exec.c:1173` (execve stack-gap ASLR),
  `vm_map.c:4408` (mmap ASLR), `pf.c:6131` (outbound pf), `in_pcb.c:885`
  (ephemeral port randomization, outbound connect),
* netisr / TCP protocol thread context — inbound `pf.c:4459/4287`
  (scrub seqdiff / state seqhi) reached via `ip_input.c:393` netisr,
  `tcp_syncache.c:1070` (ISN), `in_pcb.c:414`,
* timer-thread context — `altq_red.c:408`, plus key-material callers
  `wg_cookie.c:416`, `wg_noise.c:304`, `curve25519.h:42`, `cryptoapi.c:1084`.

DragonFly LWKT **preempts** a running thread when a higher-priority thread
becomes runnable (`sys/kern/lwkt_thread.c:941` `lwkt_preempt()`, reached
via `lwkt_schedule()`'s `td_preemptable` callback). A netisr/TCP thread
woken by an IPI can therefore preempt a syscall thread *between any two
instructions* of `arc4_randbyte()` (arc4random.c:87-99), which performs 5
separate memory-visible state mutations/reads per byte.

## Demonstrated artifact (deterministic interleave harness)

`arc4_harness.c race` runs the exact `arc4_randbyte` state machine
decomposed into its five memory-visible sub-steps; a victim context is
preempted at every legal sub-step boundary (20 points inside one
`karc4random()` call) while a preempting context executes complete
`karc4random()` calls on the same state — precisely what LWKT preemption
permits. Results over 1000 trials:

```
timed_ordered_bytes=7986 divergent_from_serialized=3946 (49.4%)
cross_consumer_duplicate_words=0
```

* **49.4% of the emitted bytes cannot be produced by ANY serialized
  execution** of the same state machine — the joint output is not a valid
  RC4 stream (chimera values assembled from pre- and post-preemption
  state). A correct synchronized implementation is always equivalent to
  *some* serialization; this one is not.
* Cross-consumer word duplication does **not** occur (0 hits) — the
  practical impact is the independence/contract violation (outputs outside
  the algorithm's distribution) rather than observable value reuse.

## Impact assessment (honest)

No memory-safety effect (all indices are uint8_t → in-bounds). No
demonstrated secret-leak or prediction: chimera values remain
unpredictable functions of secret state. The defect is a concurrency
correctness violation of an exported CSPRNG primitive consumed for ISNs,
PF scrub keys, port randomization, wg/curve25519 key material —
statistical analysis of interleaved streams is no longer sound, and the
`#if 0` premise that justified removing serialization is provably false.
Severity Low. FreeBSD's in-kernel arc4random carries a mutex for exactly
this class of race.

## Reproduce

```sh
cc -O2 -o arc4_harness arc4_harness.c -lm
./arc4_harness race 1000
```

## Fix

`fix.diff`: LWKT critical sections around all three entry points (un-#if-0
the existing pair in `karc4random_buf`, add the pair to
`karc4random`/`karc4random64`). `crit_enter()` blocks both interrupts and
LWKT preemption on the local CPU; the region is bounded (~8 bytes
generation + occasional reseed via `read_random`, which never sleeps on
the `CSPRNG_UNLIMITED` path, subr_csprng.c:146), so latency impact is
negligible.
