# DF-3372 — sockaddr fields read before sa_len validation in TCP bind/connect

## What

`tcp_usr_bind` (sys/netinet/tcp_usrreq.c:316-321), `tcp_usr_connect`
(:597-602), `tcp_usr_preconnect` (:965-968), `tcp6_usr_bind` (:406-411) and
`tcp6_usr_connect` (:640-656, incl. the `prison_remote_ip` call) cast the
user-supplied sockaddr to `sockaddr_in`/`sockaddr_in6` and read
`sin_addr` (offsets 4..7) / `sin6_addr` (offsets 8..23) **before any length
validation**. The deep validators (`in_pcbbind` in_pcb.c:648,
`in_pcbladdr_find` in_pcb.c:954, `in6_pcbbind` in6_pcb.c:129,
`in6_pcbladdr` in6_pcb.c:282) all check `sa_len` first — but they run
*after* these pre-checks.

`getsockaddr()` (sys/kern/uipc_syscalls.c:1513-1532) kmallocs **exactly
`namelen` bytes** (2..255) and copies only that many user bytes, so:

- v4, `namelen` 2..4: `sin_addr` is read entirely beyond the copied data
  (recycled, uninitialized 8-byte slab chunk contents; `zoneindex()` maps
  sizes 2..8 to the 8-byte class).
- v6, `namelen` 2..23: `sin6_addr` (offsets 8..23) is read up to 16 bytes
  past an 8-byte allocation (cross-object read into neighboring slab
  chunks); `prison_remote_ip` (kern_jail.c:469-472) reads the same bytes
  with no length check of its own.

Same defect family as DF-3357 (udp_usrreq.c), where the UDP code *used*
the OOB address as a packet destination. In TCP the bytes only steer the
multicast rejection (`EAFNOSUPPORT` vs the later `EINVAL` from the length
check) — a 1-bit oracle.

## PoC

- `oob_oracle.c` — grooms the M_SONAME 8-byte slab class with a
  controlled `sin_addr` via UDP bind(2) (which allocates+frees the same
  chunk), then binds/connects a TCP socket with a 2-byte sockaddr.
  Success criterion: errno flips 22 (EINVAL) → 47 (EAFNOSUPPORT)
  when the groom address is multicast.
- Probes: `probe.c`-series (`probe*.log`) sweep groom vectors
  (bind/sendto, 8/16-byte, 2..256 reps, 48-process fleet).

## Result (honest)

The read is **certain from the code path** and the multicast decision is
**live** (a 5-byte sockaddr whose copied byte 4 = 224 deterministically
returns `EAFNOSUPPORT`; = 8 returns `EINVAL` — proving the handler consumes
`sin_addr` regardless of `sa_len` < 16). However the *recycled-byte* oracle
could not be flipped on the stock kernel across ~600k groomed attempts:
DF's slab allocator frees cross-CPU chunks to the owning CPU's `z_RChunks`
(kern_slaballoc.c:1491-1547), which a userland-thread allocation never
immediately reuses, and the v6 cross-object neighbor byte is structurally
the next chunk's `sa_len` (=8, never 0xff). So: defect real, observable
impact on this system: none demonstrated → verdict `not_reproduced`
(reproduced=0, impact=none).

## Build / run

```
cc -O2 -include unistd.h -o oob_oracle oob_oracle.c
./oob_oracle
```

## Fix sketch

Validate length (and family) before touching address fields in each of the
five sites, e.g. in `tcp_usr_bind`/`tcp_usr_connect`/`tcp_usr_preconnect`:

```c
if (nam->sa_len < sizeof(struct sockaddr_in))
        return EINVAL;          /* or goto out; as appropriate */
```
