# DF-0755 — Unlocked global trace index tcp_debx races into runaway out-of-bounds write (TCPDEBUG-only)

## Summary

`sys/netinet/tcp_debug.c:84-95` increments the process-global trace index
`tcp_debx` (`struct tcp_debug *td = &tcp_debug[tcp_debx++];`) and wraps it with
a separate non-atomic `if (tcp_debx == TCP_NDEBUG) tcp_debx = 0;`. There is no
lock. On SMP the load/+1/store of `tcp_debx++` and the wrap check race, so
under concurrent TCP trace events the index can run away past `TCP_NDEBUG`
(=100) and writes `struct tcp_debug` off the end of the array into BSS.

**LATENT**: `tcp_debug.c` is `optional tcpdebug` (`sys/conf/files:1830`) and
`options TCPDEBUG` is **not** in `X86_64_GENERIC` (only in `LINT64`). Every
`tcp_trace()` call site is `#ifdef TCPDEBUG`. On the shipped default kernel the
vulnerable code is absent (`nm /boot/kernel/kernel | grep -c tcp_trace` = 0).

## How to reproduce

The in-kernel path is dead on a default GENERIC kernel, so the reproduction is
a userspace harness replicating the **exact C pattern** from `tcp_debug.c:84-95`.

```sh
./build.sh
./run.sh
```

### Expected output

`run.sh` prints the unfixed harness followed by the fixed harness:

```
############ UNFIXED ############
TCP_NDEBUG (array bound) = 100
max slot index used      = 8000000+      (varies, in the millions)
OOB writes (slot>=100)   = ~8,000,000
RESULT: RACE TRIGGERED -- index ran away past tcp_debug[] bound

############ FIXED ############
TCP_NDEBUG (array bound) = 100
max slot index used      = 99
OOB writes (slot>=100)   = 0
RESULT: INDEX BOUNDED -- spinlock keeps tcp_debx in [0,99]
```

On a default `X86_64_GENERIC` kernel the kernel-level stressors
(`tcp_oob_trigger`, `tcp_oob_aggressive`) are a **no-op** (the `tcp_trace`
symbol is not in the kernel). To exercise the live path you must build a
kernel with `options TCPDEBUG`; even then, the race is narrow and did not fire
in short stress on the 6-vCPU guest.

## Impact

- **Default GENERIC**: none (code not compiled in).
- **`TCPDEBUG` kernel**: BSS out-of-bounds write under sustained concurrent TCP
  tracing. Realistic ceiling = DoS/panic (BSS corruption / INVARIANTS trip /
  page fault). **Not exploitable to `uid=0`**: write content is a `struct
  tcpcb` snapshot (not attacker-controlled bytes) landing in global BSS with no
  attacker-interesting victim object adjacent.

## Fix

`fix.diff` wraps `tcp_debx` in a `struct spinlock` and takes it around the
increment + wrap, so the index cannot exceed `TCP_NDEBUG-1`. See `VERDICT.md`
for the full analysis and validation.
