# DF-2926 — sys_vquotactl unbounded plist + guaranteed per-call leak → kmalloc-limit panic (unprivileged DoS)

## What

`sys_vquotactl()` internalizes an arbitrarily large user plist
(`prop_dictionary_copyin`, sys/kern/vfs_quota.c:346) and **leaks it on
every path** (the DF-0146 family: neither the input `dict`, the `cmd`
cstring, nor `pa_out` are ever released — vfs_quota.c:345-412).  There is
no bound on the plist size and no cap on call rate.

Sustained unprivileged vquotactl traffic therefore grows the kernel's
`M_PROP_DICT`/`M_PROP_ARRAY`/`M_PROP_NUMBER` allocations monotonically
until the malloc type hits its `ks_limit`, at which point `_kmalloc_obj()`
**panics by design**:

    sys/kern/kern_kmalloc.c:702-707
        if (ttl >= type->ks_limit) {
            if (flags & M_NULLOK) return(NULL);
            panic("%s: malloc limit exceeded", type->ks_shortdesc);

and proplib's internalizer/externalizer allocate with plain M_WAITOK (no
M_NULLOK), so the exhaustion converts directly into a kernel panic.

## Observed (both organic, unprivileged, vfs.quota_enabled=1)

    panic: prop dictionary: malloc limit exceeded
      _kmalloc <- _prop_dictionary_expand <- prop_dictionary_set
        <- _prop_dictionary_internalize_continue            (input path)

    panic: prop dictionary: malloc limit exceeded
      _kmalloc <- _prop_dictionary_expand <- prop_dictionary_set
        <- prop_dictionary_set_uint32 <- sys_vquotactl+0x40d (output path —
           cmd_get_usage_all building the reply, vfs_quota.c:191-197)

The second trace matters: once the leaked objects approach the limit,
**any** vquotactl output allocation can trip the panic — no huge input
required for the final blow.

## Build / run

    cc -O -o vqalloc vqalloc.c -lprop
    ./vqalloc /boot 128          # unprivileged; panics the kernel in <1 min

## Fix direction

1. Release every prop object on every path of sys_vquotactl (fixes the
   DF-0146 leak — the monotonic growth driver).
2. Bound the accepted plist size for the quota command (copyin a limited
   externalized size, reject oversized commands).
3. Proplib hardening: internalize with M_NULLOK and return ENOMEM
   instead of relying on the panic-on-limit policy.
