# DF-0144 — copyin return value silently discarded before prop_dictionary_copyin

## Verdict: REPRODUCED (local kernel panic / DoS, unprivileged)

The discarded `copyin()` result is confirmed live as a **deterministic kernel
panic** triggered by an unprivileged user. The impact is stronger than the
"stale EFAULT" the finding describes: the uninitialized `struct plistref` is
fed to `_prop_object_copyin`, which interprets it as a serialized object and
drives `kmalloc` until `kernel_map` is exhausted → panic.

## The bug (sys/kern/vfs_quota.c:345-346)

```c
error = copyin(vqa->pref, &pref, sizeof(pref));        /* line 345 */
error = prop_dictionary_copyin(&pref, &dict);          /* line 346 — overwrites rc */
```

`copyin()`'s return is overwritten on the very next line without being tested.
With an invalid `pref` pointer (`vqa->pref = 0x1`), `copyin` fails with EFAULT
and leaves `pref` **uninitialized**; `prop_dictionary_copyin(&pref, &dict)`
then runs on garbage. `sys_vquotactl` performs **no privilege check**, so any
user can reach this.

## Evidence (live panic)

Trigger (as unprivileged `maxx`, `vfs.quota_enabled=1`):
```
syscall(SYS_vquotactl, "/tmp", (void*)0x1)
```

Serial console (`dfbsd-qemu/boot.log`):
```
panic: kmem_slab_alloc(): kernel_map ran out of space!
kmem_slab_alloc() at kmem_slab_alloc+0x42b
kmem_slab_alloc() at kmem_slab_alloc+0x42b
_kmalloc() at _kmalloc+0x5be
_prop_object_copyin.isra.0() at _prop_object_copyin.isra.0+0x35
sys_vquotactl() at sys_vquotactl+0x50
sys_xsyscall() at sys_xsyscall+0x89
Debugger("panic")
```

`vm.sh status` → **down** immediately after. The call chain
`sys_vquotactl+0x50 → _prop_object_copyin → _kmalloc → kmem_slab_alloc(panic)`
is exactly the discarded-copyin path (line 346 reached with bad `pref`).

## Exploit chain

`none` (no memory-corruption primitive to escalate) — this is a **DoS panic**.
The "primitive" is an unbounded kernel allocation driven by attacker-influenced
garbage; it reliably exhausts `kernel_map` and panics before any useful
corruption object can be groomed. There is no write/UAF to convert to `uid=0`;
the realistic impact ceiling is **reliable local DoS** (unprivileged kernel
panic), which is the demonstrated outcome.

## Fix

`fix.diff` adds the missing `if (error) return (error);` after the `copyin`.
`git apply --check` passes. On a fixed kernel the same syscall returns EFAULT
(errno 14) cleanly without calling `prop_dictionary_copyin`.

## Fix validation

See Phase 8 — the single-fix kernel returns EFAULT (no panic) on the same
trigger that panicked the unpatched baseline.
