# DF-0142 — Sleeping allocation (M_WAITOK kmalloc) while holding `ac_spin`

**Bug:** `sys/kern/vfs_quota.c` performs `kmalloc(..., M_WAITOK)` (a sleeping
allocation) while holding the per-mount spinlock `mp->mnt_acct.ac_spin`, in 4
callers (`vfs_stdaccount`, `cmd_set_usage_all`, `cmd_set_limit_uid`,
`cmd_set_limit_gid`) via `unode_insert()` / `gnode_insert()`. When the slab
allocator must block (memory pressure → `vm_map_lock`/`vm_wait` →
`tsleep` → `lwkt_switch`) while the spinlock is held, the INVARIANTS
`lwkt_switch()` KASSERT (`sys/kern/lwkt_thread.c:649`) fires and panics the
default GENERIC kernel. Local DoS.

**Class:** sleeping-alloc-under-spinlock (kernel panic / local DoS). NOT memory
corruption — the assertion aborts before any corruption; no escalation chain.

## Reproduce

**Admin precondition** (realistic — admin deploying VFS quotas; same as
DF-0141):
```
echo 'vfs.quota_enabled=1' >> /boot/loader.conf
reboot
```

**Build & run as unprivileged user** (`maxx`, uid 1001):
```
./build.sh
./run.sh             # panic harness: memory pressure + new-chunk vquotactl spam
                     #   ./aggressive /tmp 6 8   (may take ~60-80s)
```

A lighter, no-pressure check that just proves the path is reachable:
```
cc -o df0142_poc df0142_poc.c -lprop
./df0142_poc -n /tmp     # 6250 ok ops, no panic (fast path does not sleep)
```

**Expected (BUG PRESENT — unpatched #0 GENERIC, INVARIANTS ON, quotas=1):**
under the pressure harness the guest **panics** with
`panic with 1 spinlocks held` and a stack showing
`lwkt_switch ← tsleep ← lockmgr_exclusive`; ssh dies; `vm.sh status` ⇒ down.
Serial `dfbsd-qemu/boot.log` holds the panic dump (the crash proof).

**Expected (FIXED kernel):** the same `./aggressive /tmp 6 8` run **completes
without panic**; `vm.sh status` ⇒ up; `aggressive: window elapsed, killing
kids`. Quota accounting still works (the `M_NOWAIT` path returns NULL under
extreme pressure and the callers skip the update gracefully instead of
panicking).

> The panic is **memory-pressure-dependent** (non-deterministic on a single
> call). The harness reliably reproduces it on the baseline (reproduced twice);
> if a single run does not panic, retry — the trigger needs the spammers to hit
> `unode_insert`/`gnode_insert` during the memory+swap shortage window.

## Files
- `df0142_poc.c` — no-pressure trigger: proves the `kmalloc`-under-`ac_spin`
  path is reachable from an unprivileged user (6250 successful ops).
- `aggressive.c` — panic harness: memory hogs + new-chunk vquotactl spammers
  that force the slab allocator to block under `ac_spin`.
- `fix.diff` — the verified fix (`M_WAITOK → M_NOWAIT` + NULL handling in all
  4 callers).
- `panic.txt` — baseline panic signature from `boot.log`.
- `fix_run.log` — patched-kernel run (no panic).
- `VERDICT.md` — full analysis + mechanism trace.
- `manifest.json` — artifact catalog.
