# DF-3357 — udp_send() destination sockaddr used unvalidated (heap OOB read → packet destination)

* File under audit: sys/netinet/udp_usrreq.c
* Root cause: udp_usrreq.c:1046-1048 reads `sin->sin_addr` / `sin->sin_port` from the
  sendto() destination sockaddr before any length/family validation. The only
  validation (in_pcbladdr_find, in_pcb.c:954-959) happens later at
  udp_usrreq.c:1071 and ONLY when the socket's local address is INADDR_ANY or
  multicast. A socket bound to a specific unicast address skips it entirely.
* getsockaddr() (uipc_syscalls.c:1519-1523) accepts tolen as small as 2 and
  kmallocs exactly tolen bytes; bytes 2..8 of the "sockaddr_in" are then
  uninitialized/recycled kernel heap.

## Build (on the DragonFly guest)

    cc -O -Wall -o poc udp_short_sockaddr.c

## Run (unprivileged)

    # control: wildcard-bound socket -> validation runs -> no packets
    ./poc w
    netstat -s -p udp | grep output      # before/after: delta 0

    # trigger: specifically-bound socket -> validation skipped -> packets
    ./poc <local-ip>                      # e.g. 10.0.2.15
    netstat -s -p udp | grep output      # delta +50

    # capture the emitted packets (root):
    tcpdump -i <egress-if> -n udp

## Expected / observed on the reference guest (DragonFly 6.5-DEVELOPMENT)

* trigger sendto returns success for all 50 calls,
* "datagrams output" increases by exactly 50 (control: +0),
* tcpdump shows e.g. `IP 10.0.2.15.1044 > 255.255.255.255.65535: UDP, length 1`
  — the destination address AND port (6 bytes) were never supplied by the
  process (its sockaddr was 2 bytes: {2, AF_INET}); they come from
  uninitialized recycled heap (0xFF filler on this guest's M_SONAME zone-0
  chunk; on production kernels whatever the chunk last held).

See VERDICT.md for the full narrative and logs.
