# DF-3415 — in6_pcbconnect() reads sin6_addr before sa_len validation
**File:** `sys/netinet6/in6_pcb.c:343-345` (DF-3357/DF-3372 family, v6 pcb twin)

## Build (on the guest)
```sh
cd /tmp/df3415
cc -O2 -Wall -o in6_v4mapped_oob in6_v4mapped_oob.c
```

## Run (unprivileged)
```sh
su -m maxx -c ./in6_v4mapped_oob
```

## Expected output (decisive lines)
```
A(groom 00 ff ff) first errno=49 (Can't assign requested address)
B(groom 00 00 00) first errno=22 (Invalid argument)
summary over 20 rounds per variant:
  A groom 00 ff ff : EADDRNOTAVAIL=20 EINVAL=0 other=0
  B groom 00 00 00 : EADDRNOTAVAIL=0 EINVAL=20 other=0
  C no groom       : EADDRNOTAVAIL=0 EINVAL=20 other=0

RESULT: VULNERABLE -- connect(2) error code is chosen by heap bytes OUTSIDE
        the supplied sockaddr length (read before sa_len validation).
```

## What it proves
`getsockaddr()` kmallocs **exactly** the user-supplied sockaddr length
(2..255 bytes) and copies that many bytes. A `connect()` on a UDP6 socket
with a 17-byte sockaddr reaches:

- `sys/netinet6/udp6_usrreq.c:596` and
- `sys/netinet6/in6_pcb.c:344` (`IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)`)

**before** any `sa_len == 28` validation (that check only runs later, in
`in6_pcbladdr()` at `sys/netinet6/in6_pcb.c:282`). The predicate reads
`sin6_addr` file-offsets 8..19; with a 17-byte sockaddr, bytes 17..19 are
past the allocation and contain recycled heap bytes.

The PoC grooms the 24-byte `M_SONAME` slab zone (sizes 17..24 all round to
the 24-byte chunk class; `kfree()` only overwrites bytes 0..7 with the free
list link and does not scrub the rest) with a preceding rejected `bind()`,
then immediately `connect()`s with a 17-byte sockaddr. The stale groom
bytes alone decide whether the kernel returns `EADDRNOTAVAIL` (mapped
pattern `00 00 ff ff` completed by the OOB bytes) or `EINVAL` (validation
finally reached). Variant A vs B differ **only** in heap bytes that were
never part of the connect() sockaddr — a 20/20 vs 0/20 differential.

## Impact ceiling
The OOB-read content does not flow back to userspace; it selects an errno
(comparison oracle on 3+ stale heap bytes per attempt). Low severity,
kernleak bucket — consistent with the filed family (DF-3357 Medium had the
garbage address *used* as a packet destination; here it only gates two
error returns because `in6_pcbladdr()` still rejects the short sockaddr).
