# DF-0484 — SYN-cookie global crypto state unsynchronized across netisr CPUs

## Verdict: NOT REPRODUCED at runtime — race is real by inspection; impact = defeats SYN-cookie mitigation (logic/DoS)

## The bug (real, by source inspection)
`sys/netinet/tcp_syncache.c`:
- `static MD5_CTX syn_ctx;` (:1358) — a **single global** MD5 context.
- `static struct { u_int32_t ts_secbits[4]; u_int ts_expire; }
  tcp_secret[SYNCOOKIE_NSECRETS];` (:1351-1354) — **single global** secret
  table. No per-CPU copy, **no lock**.
- `syncookie_generate()` (:1382-1426) does `MD5Init(&syn_ctx)` … `MD5Update` …
  `MD5Final`, and reads/writes `tcp_secret[idx].ts_secbits[]` / `.ts_expire`.
- `syncookie_lookup()` (:1428-1463) does the same `MD5Init/Update/Final` on the
  **same** `syn_ctx` and reads `tcp_secret[idx]`.

TCP input runs per-CPU in netisr; inbound SYNs are distributed across CPUs by
mbuf hash. With ≥2 CPUs, `syncookie_generate` (from `syncache_add`, :1068, on
syncache overflow) and `syncookie_lookup` (from `syncache_expand`, :914) can
run **concurrently** on different CPUs against the *same* `syn_ctx` and
`tcp_secret[]` slot. MD5 is a streaming transform: interleaved
`Init/Update/Final` on one context corrupts the digest, and a torn
`ts_secbits` read/write makes the generated cookie `sc_iss` (:1406-1425) and
the recomputed digest (:1442-1463) disagree ⇒ legitimate final ACKs are
rejected ⇒ the SYN-cookie flood mitigation fails **exactly when it engages**.

This is CWE-362 (race). It is a **logic / DoS** defect (no memory corruption,
no info leak, no write primitive) — there is no escalation chain to develop.

## Why not reproduced at runtime
Triggering requires: ≥2 CPUs receiving SYNs concurrently, syncache overflow
forcing syncookie mode, *and* the rare MD5 interleave that actually corrupts a
cookie a live connection then depends on. It is a low-probability race whose
effect (one dropped legitimate ACK) is indistinguishable from normal packet
loss on a SYN-flooded link. No deterministic PoC is feasible in the audit
window; the defect is established by the unsynchronized single-global-state
design, which is self-evidently racy on an SMP netisr.

## Privilege boundary
**Network-reachable, unauthenticated** (AV:N, PR:N): any peer that can send
SYNs to a listening socket contributes to the race. No local privilege needed.

## Fix (validated as applies + compiles + boots)
`fix.diff` serialises the crypto+secret access with a spinlock:

```c
static struct spinlock syncookie_sl = SPINLOCK_INITIALIZER(0, 0);
```
`spin_lock(&syncookie_sl)` … (secret refresh + MD5Init/Update/Final) …
`spin_unlock(&syncookie_sl)` in both `syncookie_generate` and `syncookie_lookup`
(releasing before the early `return NULL` in lookup).

Built into the combined single-fix kernel (#1, `kern.version #1`); boots clean.
(A per-CPU `tcp_secret`+`MD5_CTX` would remove the contention but is a larger
change; the spinlock is the minimal correct fix.) `fix_status = not_testable`
(the race has no deterministic runtime marker to compare).

## Files
- `trace.md` — line-by-line source trace of the race
- `fix.diff` — spinlock around the syncookie crypto+secret
- `README.md`, `VERDICT.md`, `manifest.json`
