# DF-3388 — wg peer-destroy vs in-flight noise consumer lifetime race (UAF)

Unprivileged-impact statement: the racing *packet source* is a remote UDP peer
(needs the configured peer's private key, i.e. a malicious/compromised peer);
the peer-destruction side is routine root reconfiguration (`wg syncconf`,
peer replace, interface teardown) — the two coincide in normal operation
whenever peers are reconfigured under load.

## What this pack contains

- `wgforge.c` — userspace WireGuard handshake-initiation forger built from the
  kernel's own portable crypto (`blake2s.c`, `curve25519.c`, `chacha.c`,
  `poly1305.c` vendored here, compiled against `shim/sys/*`). It forges valid
  Noise_IKpsk2 initiations for N peer identities whose private keys we
  generated, at a paced rate.
- `wgchurn.c` — SIOCSWG driver: loops `WG_INTERFACE_REPLACE_PEERS` so the
  kernel runs `wg_peer_destroy_all()` → `wg_peer_destroy()` →
  `noise_remote_free()` for every peer and immediately re-adds them.
- `wgkeygen.c` — writes `/tmp/wgkeys.bin` = resp_pub || priv0 || N peer privs.
- `build.sh`, `run.sh` — exact build/run.
- `logs/` — sanity tcpdump (199 accepted handshakes → 92-byte responses),
  churn/forge logs from the race runs.
- `env.txt` — guest state after the race run.
- `VERDICT.md` — full narrative.
- `fix.diff` — the deferred-free fix (not applied to sys/).

## Run

```sh
# on the DragonFly guest, as root:
cd /tmp/df3388 && sh build.sh
./wgkeygen
ifconfig wg0 create && ifconfig wg0 up
./wgchurn /tmp/wgkeys.bin &      # first ioctl sets private+port+32 peers
sleep 1; pkill wgchurn
# sanity: verify handshakes are accepted (expect 92-byte responses):
(tcpdump -i lo0 -c 2000 -nn udp port 51820 &) ; ./wgforge 127.0.0.1 51820 /tmp/wgkeys.bin &
sleep 5; pkill wgforge
# race:
./wgchurn /tmp/wgkeys.bin &
./wgforge 127.0.0.1 51820 /tmp/wgkeys.bin &   # x3 processes
# watch for panic / hang / vmstat -m NOISE object-count growth
```

## Expected (observed on the stock INVARIANTS guest)

- Sanity: forged initiations are ACCEPTED — kernel sends handshake responses
  (92-byte UDP) — logs/tcpdump_sanity.log shows 199.
- Race: no panic in ~28 minutes of ~130 destroy-all cycles/s (5.3M
  wg_peer_destroy executions) against ~660 forged initiations/s
  (~340k reaching noise_consume_initiation's critical section).
  See VERDICT.md for why the corruption stays silent on this kernel
  (M_ZERO same-type reuse heals the peer UAF; the dangling noise_index node
  is explicit_bzero()'d before the kfree so bucket chains truncate quietly).
