# DF-0596 — PoC: SMP race on ng_pptpgre xmitWin -> timeSent[] OOB write

Remote (PPTP-WAN-reachable) race PoC. The legacy `ng_pptpgre` node has no
per-node serialization; `ng_send_data` dispatches `rcvdata` inline on the
caller's CPU. Two GRE ack packets arriving on different CPUs execute
`ng_pptpgre_recv` concurrently. The `xmitWin` read-check-increment at
`ng_pptpgre.c:672-676` is a TOCTOU; two concurrent ack handlers can both
see `xmitWin == 15`, both pass the `< 16` check, both increment →
`xmitWin == 17`. Subsequent xmit then permits `timeSent` index 16 (one past
the end of `pptptime_t timeSent[16]`) → 8-byte heap overflow into
`recvSeq`/`xmitSeq`.

## Files

- `race.c` — sketch driver (4 threads hammering the same GRE ack to maximize
  TOCTOU overlap on the `xmitWin` growth window).
- (added by per-PoC verifier) full `race.c` rewrite with proper PPTP
  session bring-up (TCP 1723 control channel for CID discovery, then the
  burst), `build.sh`, `run.sh`, `build.log`, `run.log`, `VERDICT.md`,
  `manifest.json`, `fix.diff`.

## Target setup (DragonFlyBSD)

```
# load netgraph modules
kldload ng_socket ng_ksocket ng_pptpgre ng_iface ng_eiface

# configure a PPTP concentrator (mpd5 or a custom ngctl script). Ensure:
#   - the guest has >= 2 vCPUs
#   - ng_pptpgre node is reachable on the WAN
#   - xmitWin has grown to PPTP_XMIT_WIN-1 = 15 (~15 successful ack
#     round-trips during PPP negotiation; happens naturally)
```

## Build & run (Linux attacker)

```
cc -O2 -lpthread -o race race.c
./race <server_wan_ip> <cid> <ack_value>
```

`ack_value` must be at or beyond the current `winAck` threshold for the
server to accept the ack for window growth (i.e. reflect the next window-
grow point). On a fresh session this advances naturally; for a targeted
trigger, the verifier should sniff the live session's last-seen ack
sequence and use `ack = winAck + delta` values that pass the
`PPTP_SEQ_DIFF(ack, a->winAck) >= 0` check at `:672`.

## Expected first outcome

After the race succeeds (`xmitWin == 17`), subsequent server xmit trips the
OOB write at `ng_pptpgre.c:514`. The server's `recvSeq` is overwritten with
a large timestamp value. All subsequent received GRE data packets fail the
sequence check at `:691` and are silently dropped (`recvOutOfOrder++`).
The PPTP session is dead.

On DEBUG kernels, the corrupted sequence numbers may trigger KASSERT
failures:

```
panic: ... KASSERT in ng_pptpgre_xmit / ng_pptpgre_recv
backtrace: ng_pptpgre_xmit+0x... ng_pptpgre_recv+0x... ng_send_data+0x...
```

## Notes for the per-PoC verifier

- The race window is narrow (~10 instructions, lines :672-676); expect
  `O(1000-10000)` attempts on a 2-vCPU guest under moderate load. The
  4-thread burst maximizes overlap probability.
- The CID can be obtained by establishing a legitimate PPTP session (TCP
  1723 control channel) or brute-forced (only 65536 possibilities, but each
  wrong guess wastes server resources and may reveal the wrong-CID error).
- The overflow stays within the single `priv` `kmalloc` allocation
  (~200 bytes remaining after the write point), so it does not directly
  corrupt adjacent slab objects — the demonstrated impact is PPTP session
  DoS, not code execution. If heap grooming of a same-sized slab yields a
  controlled victim object, document it in `VERDICT.md`; otherwise the
  verdict should reflect `session_DoS_confirmed / escalation_unverified`
  and the finding stays Medium.
- Verify the fix with `git apply findings/poc/DF-0596/fix.diff` (the
  per-node spinlock patch in the finding markdown); after the fix the race
  should no longer fire.
- Same class as DF-0590 (legacy `ng_bridge` no-serialization races): the
  entire legacy netgraph tree lacks per-node SMP locking. Flagged for the
  maintainer as a systemic issue.
