# DF-3360 VERDICT

## Reproduced? No — the defect is certain, the shipped build neutralizes it.

### What was proven, and how

1. **Source-level reachability (certain).** `pf_normalize_tcpopt()`
   (sys/net/pf/pf_norm.c:1907-1957) pulls `cnt = th_off*4 - 20` bytes into
   `u_char opts[TCP_MAXOLEN]` (= exactly 40, sys/netinet/tcp.h:149). The
   walk accepts an option with `optlen == optp[1]` as long as
   `2 <= optlen <= cnt`. A final option `[0x02, 0x02]` after 38 bytes of
   filler (th_off == 15) leaves `cnt == 2`, passes the check, and executes
   `mss = (u_int16_t *)(optp + 2)` = **opts+40**:
   - `ntohs(*mss)` reads 2 bytes past the array (pf_norm.c:1941);
   - when the stale stack word (big-endian) exceeds `r->max_mss`,
     `*mss = htons(r->max_mss)` **writes** 2 bytes past the array
     (pf_norm.c:1944).

2. **Compiled into the shipped binary (certain).** Disassembly of
   /boot/kernel/pf.ko (INVARIANTS gcc80 -O2, see
   pf_normalize_tcpopt.dis): the loop guard is only
   `optlen < 2 || optlen > cnt`; the access is
   `movzwl 0x2(%r15)` (pf_normalize_tcpopt+0xba) with `%r15` walkable to
   opts+38, and the store `mov %ax,0x2(%r15)` (+0x102). No bound vs the
   40-byte array exists.

3. **Runtime path execution (observed).** `scrub in on lo0 all max-mss 64`
   + 100,000 crafted packets: rule evaluations climbed to 416,675 and the
   packets were not dropped early (the option pull succeeds; the scrub
   rule has no port/address constraints). 200,000 more packets with
   max-mss 1 (write condition almost always true). Guest stable
   throughout, no console messages, no panic.

4. **Why nothing manifests on this build.** Frame layout from the same
   disassembly: `opts` lives at `rbp-0x58`; `opts+40..41` = `rbp-0x30` —
   an 8-byte alignment-padding slot between the array and the first
   pushed register (saved rbx at rbp-0x28). No instruction in the
   function references `-0x30(%rbp)`. The OOB read returns stale stack;
   the conditional OOB write corrupts only that dead padding.

### Impact assessment

- **On the shipped gcc80 -O2 build: no runtime impact** (padding), except
  a subtle secondary effect: `th->th_sum` is recomputed with a delta
  derived from the OOB-read stale bytes and the header is copied back
  into the packet — for transit traffic this leaks 2 bytes of stale
  kernel stack per packet into an observable checksum change.
- **On any rebuild where a live local/spill lands at opts+40**
  (different compiler version, -O level, LTO, KASAN-style redzones, or
  simply a change in register pressure): a remotely-triggerable 2-byte
  kernel stack write adjacent to saved registers with a rule-chosen
  value — memory-corruption class.

Confidence in the *defect*: certain. Runtime manifestation on the
audited binary: none. Status: not_reproduced / impact none (for this
build), severity filed as Medium.

### Fix
One line: `if (optlen != TCPOLEN_MAXSEG) break;` in the MAXSEG case
(fix.diff). Not kernel-rebuilt for this finding (single-fix build budget
was spent on DF-3362, whose fix is validated; the change is a pure
additional guard on an option-parsing branch and cannot regress the
well-formed path, which always has optlen == 4).

## Fix status (batch validation)
Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest `make -j6 nativekernel KERNCONF=X86_64_GENERIC` completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.
