# DF-0011 — PoC

`nopasscred_panic.c` — local DoS via the missing `sbcreatecontrol()` NULL
check in the `SO_PASSCRED` synthesis path of `uipc_send()`. `flood_trigger.c`
is an auxiliary hold-open variant used during analysis.

## The bug

`uipc_send()` SOCK_DGRAM, `sys/kern/uipc_usrreq.c:694-699`:

```c
if (ncon == NULL) {                 /* no existing SCM_CREDS cmsg found */
    ncon = sbcreatecontrol(&cred, sizeof(cred), SCM_CREDS, SOL_SOCKET);
    unp_internalize(ncon, msg->send.nm_td);   /* ncon may be NULL */
    *mp = ncon;
}
```

`sbcreatecontrol()` (`sys/kern/uipc_sockbuf.c:585-604`) returns NULL when its
`m_getl(..., M_NOWAIT, MT_CONTROL, 0, NULL)` fails — i.e. when the **plain
"mbuf" objcache** (`sys/kern/uipc_mbuf.c:798`, `nmbufs` deep) is exhausted.
The return is never checked, so `unp_internalize(NULL)` runs and at
`uipc_usrreq.c:1706` does `cm = mtod(control, ...)` = load of `mh_data` from a
NULL mbuf ⇒ `offsetof(struct m_hdr, mh_data) == 0x10` ⇒ page fault at vaddr
`0x10` in kernel mode ⇒ panic.

## Trigger

An unprivileged local user who (a) exhausts the plain "mbuf" objcache and
(b) sends to an `AF_UNIX` `SOCK_DGRAM` receiver that has `SO_PASSCRED` set,
**without** an `SCM_CREDS` cmsg.

**How the precondition is met (the crux):** each `AF_UNIX` `SOCK_DGRAM`
datagram pinned in a receiver buffer consumes 2 plain mbufs (`MT_CONTROL` +
`MT_SONAME`) but only 1 pkthdr mbuf (`MT_DATA` data). Pinned plain:pkthdr
ratio is therefore 2:1. Exhausting the 72904-deep plain cache pins only ~36500
pkthdr, leaving the pkthdr cache ~half empty — so the trigger's data pkthdr
mbuf still allocates (`M_WAITOK` in `sosend`) and execution reaches
`sbcreatecontrol()`, whose plain `m_get(M_NOWAIT)` then FAILS ⇒ NULL ⇒ panic.

The PoC runs a pinner thread (ramp plain-mbuf pressure) **concurrently** with a
trigger thread (fire no-control `SO_PASSCRED` sends), so a trigger send lands
on the instant of plain-cache exhaustion instead of overshooting into a wedge.

## Build

```
cc -o nopasscred_panic nopasscred_panic.c -lpthread
```

## Run

As an **unprivileged** user (this PANICS the kernel — local DoS):

```
./nopasscred_panic
```

## Expected output (bug present)

A kernel panic on the serial console:

```
Warning: objcache(mbuf) exhausted on cpuN!
Fatal trap 12: page fault while in kernel mode
fault virtual address = 0x10
Stopped at      unp_internalize.isra.12+0x11:   movq    0x10(%rdi),%rbx
```

(The finding markdown guessed fault address `0x0`; the real address is `0x10`,
the `mh_data` offset in `struct m_hdr` — same bug, sharper address.)
Reproduced twice from independent fresh `vm.sh reset` boots with an identical
signature. On a patched kernel (NULL check added) the trigger send returns
`ENOBUFS` instead and the program prints "no panic".
