# DF-2922 — proplib type confusion in sys_vquotactl → kernel memory corruption / panic / livelock

## What

`sys/kern/vfs_quota.c:385` passes the user-controlled `"arguments"` proplib
object to `cmd_set_usage_all()`, which calls `prop_array_iterator(args)` at
`sys/kern/vfs_quota.c:242` **without validating that the object is an
array**.  `prop_array_iterator()` (`sys/libprop/prop_array.c:538-546`)
executes `mtx_lock(&pa->pa_rwlock)` — in-kernel proplib rwlocks are real
sleep mutexes (`sys/libprop/prop_object_impl.h:295-299`) — **before** the
`prop_object_is_array()` type check inside `_prop_array_iterator_locked()`.

For a non-array object the 40-byte `struct mtx` at `offsetof(struct
_prop_array, pa_rwlock) == 16` aliases other fields of the real object:

| "arguments" type | aliased mtx fields | observed result |
|---|---|---|
| `<dict>` (control) | real mutex at same offset | clean EINVAL-free return (control passes) |
| `<false/>` (static `_prop_bool_false`) | `pb_value`=lock word, gap/past-object=owner/ident | **Fatal trap 12**: page fault in `strncpy` via `_mtx_lock_ex → mtx_wait_link` (wmesg copy from aliased `mtx_ident`), `panic with 1 spinlocks held` — 3/3 fresh boots |
| `<true/>` | slowpath: `mtx_exlink` write lands past the bool globals | **Fatal trap 9 (GPF)** at `_mtx_lock_ex+0xdc` `movq %r12,(%rax)` — the exlink-list write of a kernel stack pointer through the aliased pointer |
| `<integer>31337</integer>` | `pn_link` rb_node ↔ mtx fields | **Fatal trap 9 (GPF)** at `_mtx_lock_ex+0xc4` `movq 0x8(%rax),%rcx` — read through the aliased exlink chain |
| `<string>` | `ps_mutable` low bits = never-zero lock word | **silent full-system livelock**: QEMU at 102% CPU, guest OS dead, no console output (the `__mtx_lock_ex` retry loop spins forever on a heap-pointer lock word while holding the mount's `ac_spin`) |

All triggered by an **unprivileged** user (uid 1001) with `vfs.quota_enabled=1`
(the same reachability already established by DF-0141).

Additionally the fault happens with `ac_spin` held (`cmd_set_usage_all`
takes the mount spinlock at vfs_quota.c:228 *before* calling
`prop_array_iterator`) — the compounding "sleep/spin under spinlock" defect.

## Files

* `typeconfuse.c` — trigger (mode selectable)
* `build.sh` / `run.sh`
* `panic.false.txt`, `panic.true.txt`, `panic.number.txt` — serial console captures
* `panic.dump.txt` — auto-backtrace from crash dump (`debugger_on_panic=0`)
* `disasm.txt` — disassembly proving the faulting stores/loads are the
  `__mtx_lock_ex` exlink-list manipulation
* `bss-layout.txt` — `nm` proof of the `_prop_bool_*` global layout the
  aliased mutex overwrites
* `fix.diff` — validated fix (type checks + stack initialization)
* `verdict.json` / `manifest.json`

## Build

    cc -O -o typeconfuse typeconfuse.c -lprop

## Run (on a guest booted with vfs.quota_enabled=1)

    # control (returns cleanly):
    ./typeconfuse /boot dict
    # each of the following kills the stock kernel deterministically:
    ./typeconfuse /boot false     # fatal trap 12, page fault, spinlock held
    ./typeconfuse /boot true      # fatal trap 9 GPF (write through aliased ptr)
    ./typeconfuse /boot number    # fatal trap 9 GPF (read through aliased ptr)
    ./typeconfuse /boot string    # silent full-system kernel livelock

## Expected output (baseline, unpatched)

`false`: `mode=false path=/boot pid=N` then the console shows
`Fatal user address access from kernel mode from typeconfuse at
ffffffff809d780f` / `panic: page fault` / `panic with 1 spinlocks held`.

On the patched kernel every variant returns `vquotactl returned 22`
(EINVAL) and the guest stays up.
