# DF-2713 — VERDICT

## Status: REPRODUCED (impact: leak — kernel heap disclosure, unprivileged)

## How it was reproduced
Guest: DragonFly 6.5-DEVELOPMENT #0 (X86_64_GENERIC, INVARIANTS), 6 CPUs,
4 GB RAM, `kern.ipc.nmbclusters=33296`.

PoC `leak_cmsgpad.c` (built with guest cc 8.3, run as unprivileged `maxx`):

1. UDP socket on 127.0.0.1 with IP_RECVDSTADDR + IP_RECVTTL + IP_RECVTOS
   enabled; each loopback datagram produces 3 kernel-built cmsgs in one
   control mbuf each; per receive the padding amounts are 4+7+7 = 18 bytes
   (`pad_bytes=5400` over 300 iterations confirms the arithmetic:
   CMSG_SPACE(4)=24 vs CMSG_LEN(4)=20; CMSG_SPACE(1)=24 vs CMSG_LEN(1)=17).
2. Phase A (baseline): 300-521 non-zero stale bytes per 5400 padding bytes
   without any grooming — the leak exists on a freshly booted system.
3. Phase B (same-process groom): between receives the PoC issues failing
   `sendmsg(2)` calls on an AF_UNIX socketpair with a 40-byte control
   buffer filled with 0xCC.  sys_sendmsg() (sys/kern/uipc_syscalls.c:940-947)
   allocates a plain `m_get(M_WAITOK, MT_CONTROL)` mbuf — the *same*
   objcache the receive-path control mbufs come from — copyins the 0xCC
   bytes, and `sosend()` frees the mbuf on the EINVAL error path
   (sys/kern/uipc_socket.c:1240).  The next `sbcreatecontrol()` recycles
   that mbuf and the un-overwritten padding retains 0xCC.
   Result: **2396-2400 0xCC bytes leaked per 300-receive run** and the
   decisive hexdump:
   ```
   +00: 14 00 00 00 00 00 00 00 07 00 00 00 cc cc cc cc
   +10: 7f 00 00 01 cc cc cc cc ...
   ```
   = IP_RECVDSTADDR cmsg (cmsg_len=0x14=20, data `7f 00 00 01` =
   127.0.0.1) followed by `cc cc cc cc` of attacker-supplied stale heap.
4. Phase C (cross-process): a fork()ed child grooms with 0x5A on its own
   sockets; the parent's cmsg padding contained 4 x 0x5A bytes per run
   (run.2.log) — disclosure of *another process's* freed mbuf contents.

Three executions (run.log, run.2.log, run.3.log) all show marker bytes in
the padding — not a one-shot artifact.

## Root cause chain (path:line)
- sys/kern/uipc_sockbuf.c:595  `m->m_len = CMSG_SPACE(size)`
- sys/kern/uipc_sockbuf.c:597-601 only `CMSG_LEN(size)` bytes initialized
- sys/kern/uipc_syscalls.c:1175-1189 recvmsg copyout loop copies
  `m->m_len` bytes per control mbuf, padding included
- Builders: sys/netinet/ip_input.c:2200-2272 (SCM_TIMESTAMP 0-pad,
  IP_RECVDSTADDR, IP_RECVTTL, IP_RECVTOS, IP_RECVIF),
  sys/netinet6/ip6_input.c ip6_savecontrol (IPV6_PKTINFO etc.),
  sys/kern/uipc_usrreq.c:695 (SCM_CREDS)

## Not a duplicate
DF-2702 is the SCM_RIGHTS MSG_PEEK raw-pointer leak at the soreceive sink
(uipc_socket.c).  DF-0010/DF-2557 are about uninitialized *cmsgcred
content* on the SO_PASSCRED path.  DF-0389 is rtsock sockaddr padding.
This finding is the generic CMSG_SPACE tail-padding of every
sbcreatecontrol()-built cmsg, surfaced by the recvmsg copyout in
uipc_syscalls.c.

## Exploitability ceiling
kernleak: repeatable, deterministic-content (groomable) disclosure of
stale kernel mbuf heap to any unprivileged local user; up to ~18 stale
bytes per received datagram (more with IP_RECVIF / IPv6 options / SCM_CREDS
stacked, and repeatable without limit).  Cross-process content disclosure
demonstrated (Phase C).

## Fix validation
fix.diff bzeros the full CMSG_SPACE in sbcreatecontrol().  Applied to the
guest's /usr/src copy, `make nativekernel` + `make installkernel` +
reboot; the same PoC then reported 0 marker bytes and 0 non-zero padding
bytes in all phases (fix_run.log).  Baseline (unpatched) vs patched
output included.
