# DF-2597 — TCP-MD5 (TCP_SIGNATURE) signature option overruns 40-byte `opt[]` stack buffer

## Verdict: NOT REPRODUCED — real source-level defect in DEAD, UNBUILDABLE code

The cited overflow (`sys/netinet/tcp_output.c:779-800`) is a **genuine source
defect** — the 20-byte signature option (TCPOLEN_SIGNATURE=18 + NOP/EOL=2) is
appended to the 40-byte `u_char opt[TCP_MAXOLEN]` stack buffer with **no bound
check**, so any prior options (MSS+window-scale+SACK-permitted+timestamp on a
SYN → optlen=24, or timestamp + SACK blocks in ESTABLISHED → optlen up to 40)
push the signature write 4–20 bytes past the end of `opt[]`, corrupting the
adjacent stack frame (`optlen`, `hdrlen`, `ipoptlen`, saved registers).

**But the code path is completely unreachable on every kernel buildable from
current DragonFly master**, for two compounding reasons:

### (1) TCP_SIGNATURE is not in the default GENERIC kernel
`options TCP_SIGNATURE` is commented out in `sys/conf/options:271` and is
absent from `sys/config/X86_64_GENERIC`. The vulnerable block is wrapped in
`#ifdef TCP_SIGNATURE`, and so is the only way to arm it from userspace — the
`case TCP_SIGNATURE_ENABLE:` handler in `sys/netinet/tcp_usrreq.c:1556`. On the
running audit-source kernel (`6.5-DEVELOPMENT #0`, INVARIANTS ON):

```
[*] setsockopt(TCP_SIGNATURE_ENABLE) -> -1 errno=42 (Protocol not available)
```

`setsockopt` falls through to `default: error = ENOPROTOOPT`, `TF_SIGNATURE`
can never be set, and `tcp_output.c:779-800` is compiled out entirely. **Runtime
unreachable from any unprivileged (or even privileged) action on the default
kernel.** Verified: `nm /boot/kernel/kernel | grep -ci tcpsignature` = 0.

### (2) TCP_SIGNATURE is UNBUILDABLE on current master — its dependency (IPsec SADB) was deleted
The TCP-MD5 feature (`tcpsignature_compute` in `sys/netinet/tcp_subr.c:2199`,
and the digest-write call site at `tcp_output.c:1058`) depends on the KAME/IPsec
SADB subsystem: `key_allocsa()`, `struct secasvar`, `key_freesav()`,
`key_sa_recordxfer()`, `_KEYBUF()`/`_KEYLEN()`, and `IPSEC_DIR_OUTBOUND`. **None
of these exist anywhere in the current `sys/` tree** — the IPsec subsystem was
removed (no `sys/netproto/ipsec/`, no `key.h`, no `ipsec.h`). Attempting to
build a kernel with `options TCP_SIGNATURE` fails to compile:

```
/usr/src/sys/netinet/tcp_output.c:1058:37: error: 'IPSEC_DIR_OUTBOUND' undeclared
```
and even forcing past that, `tcp_subr.c` fails:
```
tcp_subr.c:2328:29: error: dereferencing pointer to incomplete type 'struct secasvar'
tcp_subr.c:2236:9: warning: implicit declaration of function 'key_allocsa'
tcp_subr.c:2331:2: warning: implicit declaration of function 'key_freesav'
```

So **no kernel (default or custom) can be built from current master source with
this code path live.** The TCP_SIGNATURE code is orphaned dead code that
references a subsystem that no longer exists in the OS.

### Conclusion / classification

This is the valid hard blocker from Phase 6: *the vulnerable code path is
dead/unreachable at runtime AND no harness can exercise it* — here elevated to
*the code cannot even be compiled into any kernel*. The bug is a **latent
source-level defect** (a missing `optlen + TCPOLEN_SIGNATURE + 2 <= TCP_MAXOLEN`
bound check) that would be a real High-severity stack overflow *if and only if*
the IPsec SADB subsystem were present and `options TCP_SIGNATURE` were enabled.
On the audited kernel it has **zero runtime impact**.

Because there is no live memory-corruption primitive (the code never runs, never
even links), there is **no escalation chain to develop** — Phase 6 escalation
applies only to a live write primitive, which does not exist here.

## Mechanism trace (source-confirmed, were the code reachable)
1. `tcp_output()` declares `u_char opt[TCP_MAXOLEN]` on its stack — `TCP_MAXOLEN = 60 - sizeof(tcphdr) = 40` (`sys/netinet/tcp.h:149`, `:148`).
2. On a SYN with default sysctls (`tcp_do_rfc1323=1` sets `TF_REQ_SCALE|TF_REQ_TSTMP` in `tcp_subr.c:759`; `tcp_do_sack` default) the option writers at `tcp_output.c:719-766` produce: MSS(4)+window-scale(4)+SACK-permitted(4)+timestamp(12) = **optlen=24** (`TCPOLEN_MAXSEG`=4 `tcp.h:86`, `TCPOLEN_WINDOW`=3 padded to 4, `TCPOLEN_SACK_PERMITTED_ALIGNED`=4 `tcp.h:93`, `TCPOLEN_TSTAMP_APPA`=12 `tcp.h:101`).
3. In ESTABLISHED with timestamp + 3 SACK blocks (`tcp_sack_fill_report` at `tcp_sack.c:920` adds `TCPOLEN_SACK_ALIGNED`=4 + 3×`TCPOLEN_SACK_BLOCK`=8, bounded to ≤40), optlen reaches **40**.
4. The signature block at `tcp_output.c:779-800` then unconditionally writes TCPOPT_SIGNATURE+TCPOLEN_SIGNATURE (2) + 16 zero bytes + NOP+EOL (2) = **20 bytes** at `opt+optlen` with no check, overrunning `opt[]` by **4 bytes** (SYN) to **20 bytes** (ESTABLISHED). The post-hoc `KASSERT(optlen <= TCP_MAXOLEN)` at `:801` is a debug-only trip, not a guard.

## Fix-validation status: `not_testable`
Phase 8 requires a clean *before/after* on a kernel that runs the vulnerable
code. Here the vulnerable code **cannot be compiled into any kernel** (the IPsec
dependency is gone), so there is no "before" baseline to reproduce against and no
"after" patched kernel to test. `fix.diff` applies cleanly (`git apply --check`
OK) and is a correct bound check, but it cannot be validated by building and
booting, because the surrounding `#ifdef TCP_SIGNATURE` code does not compile on
current master.

## What `fix.diff` does (defense-in-depth for if IPsec/TCP-MD5 is reintroduced)
Wraps the signature-append block in a bound check:
`if ((tp->t_flags & TF_SIGNATURE) && optlen + TCPOLEN_SIGNATURE + 2 <= TCP_MAXOLEN)`.
When there is no room, the signature is omitted (the segment fails MD5
verification on the peer — graceful degradation) rather than overrunning `opt[]`.
Matches the spirit of the finding markdown's recommended fix; supersedes it with a
precise, line-accurate guard.

## Reproduce
```
cd findings/poc/DF-2597 && ./build.sh && ./run.sh
# Expected on the default audit-source kernel:
#   [*] setsockopt(TCP_SIGNATURE_ENABLE) -> -1 errno=42 (Protocol not available)
#   [!] RESULT: bug UNREACHABLE on this kernel (latent code defect).
#   RUN_EXIT=5
```
