# DF-5683 VERDICT

## Status: REPRODUCED (leak, unprivileged) — and FIXED by fix.diff (validated on a rebuilt kernel)

## Environment
- Guest: DragonFly 6.5-DEVELOPMENT x86_64, kernel `#0: Thu Jul 2 06:02:54 UTC 2026`
  (baseline) / `#1: Sat Oct 10 00:36:54 UTC 2026` (patched, fix applied).
- `vfs.quota_enabled=1` boot tunable (feature is OFF by default; the syscall
  returns EOPNOTSUPP otherwise — same gate DF-2231 documented).
- Driver runs as unprivileged uid 1001 (`maxx`).

## How it reproduces
`vquotactl(2)` (syscall 530) with
`<plist><dict><key>command</key><string>get usage all</string><key>arguments</key><string>x</string></dict></plist>`
walks exactly the leaking path:

  sys_vquotactl (vfs_quota.c:328)
    -> prop_dictionary_copyin(&pref, &dict)          [prop_kern.c:454]
    -> prop_dictionary_get_cstring -> cmd (kmalloc copy)
    -> prop_array_create() pa_out; cmd_get_usage_all() fills it
    -> goto done; dict = prop_dictionary_create()    (shadows the copyin'd dict)
    -> prop_dictionary_copyout(&pref, dict)          [prop_kern.c:554]
    -> return — **no prop_object_release anywhere**, no nlookup_done on the
       nlookup error paths (vfs_quota.c:351-356).

### Baseline measurements (vmstat -m, three checkpoints t0/t1/t2 = 0/1000/2000 calls)
| malloc type | t0 InUse | t1 | t2 | per-call |
|---|---|---|---|---|
| propdict      | 178  | 3100 | 6030 | +2.9 |
| prop_dictionary| 170 | 3100 | 6030 | +2.9 |
| proparay      | 9    | 1009 | 1960 | +1.0 |
| prop_array    | 9    | 1009 | 1960 | +1.0 |
| propstng      | 323  | 2270 | 4220 | +1.9 |
| prop_string   | 319  | 2260 | 4220 | +1.9 |

≈ **+1.6-1.9 KB live kernel heap per unprivileged syscall**, perfectly linear,
zero errors, never freed (InUse never drops). With `vm.kmem_size` ≈ 390 MB,
~250k calls (minutes) exhaust kernel heap.

An equivalent one-shot demo with base-system tooling (as uid 1001):
`vquota show /` — each invocation performs exactly one vquotactl round trip.

## Fix validation (fix.diff / fix_combined.diff)
Applied to the guest's /usr/src (vfs_quota.c leak fixes: prop_object_release
of dict/pa_out/odict on every exit, `prop_dictionary_get_cstring_nocopy` for
cmd, nlookup_done on error paths), `make nativekernel KERNCONF=X86_64_GENERIC`,
installkernel, reboot into kernel #1.

Patched kernel, 2000 root-driven calls (root passes the DF-5684 priv gate and
exercises the identical leak path):
- proparay 1→2, propdict 150→152, propstng 314→315  (**+2 objects total**,
  i.e. per-boot transient, not per-call) vs +9,770 objects on baseline.
- LEAK ELIMINATED. Unprivileged callers additionally get EPERM from the
  companion privilege fix (DF-5684) before any allocation.

## Why this is a real finding beyond DF-2231
DF-2231 covers the *copyin* size handling in sys/libprop/prop_kern.c
(unbounded pref_len, dead prop_object_copyin_limit). This finding is the
*consumer lifetime* bug: even a perfectly-sized, fully-parsed dictionary is
leaked wholesale by sys_vquotactl on every call, on the success path, with
no malformed input required.

## kernel_refs
sys/kern/vfs_quota.c:345-346, 351-356, 362, 374, 405-408
sys/libprop/prop_kern.c:454-458 (prop_dictionary_copyin), 554-557 (copyout)
sys/libprop/prop_dictionary_util.c:183-198 (get_cstring allocates a copy)

## Fix status (batch validation)
Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest `make -j6 nativekernel KERNCONF=X86_64_GENERIC` completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.
