# DF-5830 — `sys_sendmsg()` leaks the control mbuf when `kern_sendmsg()` bails out early

**Severity:** Medium (unprivileged, permanent kernel-memory exhaustion; local DoS)
**Class:** CWE-401 missing release of kernel resource; bucket `base:kern` / DoS
**File:** `sys/kern/uipc_syscalls.c`
**Status:** reproduced on guest (stock kernel), fix validated on patched kernel.

## What

`sendmsg(2)` with ancillary (control) data on a **non-socket fd** (or any fd for
which `holdsock()` fails: `EBADF`, `ENOTSOCK`) leaks one `MT_CONTROL` mbuf —
permanently — inside the kernel. Any local user can loop this without limit.

## Build

```
cc -O2 -Wall -o leaksendmsg leaksendmsg.c     # any unprivileged user
```

## Run

```
netstat -m | sed -n 1p          # note mbufs-in-use
./leaksendmsg 20000             # 20000 failing sendmsg() on a regular-file fd
netstat -m | sed -n 1p          # +20000 mbufs, never reclaimed
./leaksendmsg 20000 ok          # control experiment: real socketpair, NO growth
```

## Expected output (vulnerable kernel)

```
OK: 20000 sendmsg calls on regular-file fd (all ENOTSOCK)
7/146632      ->    20007/146632 mbufs in use (current/max):
```
(linear: each round of N calls adds exactly N mbufs; count never drops, even
after the leaking process exits).

## Full-wedge demonstration (throwaway guest only!)

```
./leaksendmsg 3000000 &
```
exhausts the whole mbuf pool (~146k mbufs / ~37-77 MB) in under a minute:
serial console spews `Warning: objcache(mbuf) exhausted on cpuN!`, kernel
denies network-memory requests system-wide, and the leaking process parks in
state `D` (wchan `objcache`) **immune to `kill -9`** — only a reboot recovers.
See `run.3.log` for the full transcript.

## Patched kernel (fix.diff)

`7/146632 -> 7/146632 -> 7/146632` across the identical protocol; happy-path
(control consumed on a real socket) and EFAULT-copyin path both regression-clean.
See `fix_validation.log`.

## Evidence files

- `run.log`   — baseline: control experiment + 3 linear leak rounds
- `run.2.log` — fresh-boot (post `vm.sh reset with-src`) re-confirmation
- `run.3.log` — full-pool exhaustion wedge, `kill -9` immunity, kernel's own
  "154453 mbufs allocated to ancillary data" attribution
- `wedge_evidence.txt`, `console.txt` — raw snapshots
- `fix_validation.log`, `fix.diff`, `env.txt`, `manifest.json`, `verdict.json`
