# DF-0315 — `wg_peer` use-after-free PoC

Finding: **UAF — `wg_peer_destroy` frees the peer struct while data-plane paths
(`wg_output`/`wg_input`/`wg_encrypt`/`wg_decrypt`) dereference it without a peer
refcount.**  `sys/net/wg/if_wg.c`, severity High, CWE-416.

## Files

| file | what |
|------|------|
| `wgrace.c`                              | the racer: sets up wg0+peer, races `wg_output` (UDP senders) vs `wg_peer_destroy` (root `SIOCSWG` destroy/re-add loop), with optional heap groomers |
| `build.sh` / `run.sh`                   | exact build & decisive-run commands |
| `build.log`                             | full compiler output (clean) |
| `run.log`                               | decisive run (NOEP + destroy race + 1024-bucket exec churn): no panic, guest up |
| `env.txt`                               | uname, cc, kldstat(if_wg), ncpu, slab/debug sysctls |
| `endpoint_crash_separate_bug.txt`       | a DIFFERENT wg panic found during testing (NOT DF-0315) |
| `fix.diff`                              | `git apply`-able refcount fix (verified `--check` clean) |
| `VERDICT.md`                            | full narrative + line-by-line code proof + negative-result reasoning |
| `manifest.json`                         | machine-readable catalog |

## Build (as maxx)

```
cd poc/DF-0315 && cc -O2 -pthread -o wgrace wgrace.c
```

## Run

The racer **must run as root** for the `SIOCSWG` destroy side
(`SYSCAP_RESTRICTEDROOT`, `if_wg.c:2671`).  The `wg_output` (data-plane) side is
what an unprivileged local user drives; the destroy side is what root does.

```
# DECISIVE DF-0315 test (isolated from the separate wg-send crash via NOEP):
ssh dfbsd
  cd ~maxx/poc/DF-0315 && env NOEP=1 NSEND=4 NGROOM=6 ./wgrace 55

# Optional: the SEPARATE wg-send crash (NOT DF-0315), for reference:
  env NOPRIV=1 NORACE=1 NSEND=1 NGROOM=0 SDELAY=2000 ./wgrace 20
```

## Expected

* **DF-0315 (decisive, `NOEP`)** — on a vulnerable kernel that lets the UAF
  window be hit, a page-fault panic in the `wg_output` / `wg_peer_send_staged`
  path shortly after start (signature in `dfbsd-qemu/boot.log`).  **On this
  master DEV `X86_64_GENERIC` kernel it does NOT panic**: the run exits cleanly
  after ~16.8M sends / ~22k destroy-readd cycles and the guest stays up.  See
  `VERDICT.md` for why (narrow window + slow destroy + GENERIC slab leaves the
  freed peer's fields stale-but-valid).
* **Separate crash (`NOPRIV`+endpoint)** — panics in `udp_send+0x2cf` within
  ~20 s; this is a *different* bug (no peer destruction involved), documented in
  `endpoint_crash_separate_bug.txt`.

## Environment knobs (env vars)

`NOEP=1` peer has no endpoint (isolates the wg_output UAF from the separate
wg-send crash).  `NOPRIV=1` omit interface private key (handshake can't be
created).  `NORACE=1` disable destroy/re-add (control).  `NOGROOM=1` disable
groomers.  `NSEND=n` / `NGROOM=n` thread counts.  `SDELAY=us` inter-send delay.
