β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-2926

Unbounded user plist Γ— unconditional per-call leak in sys_vquotactl β†’ kmalloc-type-limit panic: guaranteed unprivileged kernel panic (DF-0146 escalation)

Field Value
ID DF-2926
Status new
Severity Medium
CVSS 3.1 CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H
CWE CWE-400 / CWE-772
File sys/kern/vfs_quota.c
Lines 345-346, 362, 405-409; panic policy: kern_kmalloc.c:702-707
Area kern/vfs
Confidence certain
Discovered 2026-09-02
Pass 2 (GLM 5.3 second pass)
Bucket base:kern
Reported pending
Known CVE none
CVE match novel

Summary

sys_vquotactl() internalizes an arbitrarily large user plist and leaks it on every path (input dict, cmd cstring, output array β€” the DF-0146 family) with no size bound and no rate cap. Sustained unprivileged traffic grows M_PROP_DICT/M_PROP_ARRAY monotonically until the malloc type's ks_limit is hit, at which point _kmalloc_obj() panics by design β€” proplib allocates with plain M_WAITOK (no M_NULLOK) so exhaustion cannot fail gracefully. Not a re-report of DF-0146: the new, demonstrated result is the guaranteed unprivileged kernel panic from the multi-call interaction. Observed twice organically from uid 1001, including the decisive variant where the panic fires while building the "get usage all" REPLY β€” once near the limit, ANY vquotactl output allocation can trip it. Fix: release every prop object on every path (the DF-0146 leak fix removes the monotonic growth); bound the accepted plist size; harden proplib internalization to M_NULLOK + ENOMEM. Evidence: findings/poc/DF-2926/ (vqalloc.c, panic.1.txt, panic.2.txt) β€” unprivileged panic within ~1 minute.

Timeline

  • 2026-09-02 Discovered during pass-2 audit of vfs_quota.c (GLM 5.3); unpriv panic reproduced twice.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-2926 Β· 8 files
FileTypeDescriptionSize
vqalloc.c β€” 2.3 KB view raw
build.sh β€” 44 B view raw
run.sh β€” 93 B view raw
run.log β€” 331 B view raw
panic.1.txt β€” 580 B view raw
panic.2.txt β€” 604 B view raw
verdict.json β€” 3.0 KB view raw
README.md β€” 2.2 KB ↓ raw

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.

Fix verification

not_testable
baseline reproduced→ patch + rebuild →patched clean

not_testable: the validated single-fix kernel addresses DF-2922 only; this finding's fix (leak release + size bound) was not built within the run budget.

panic.1.txt, panic.2.txt
per-fix-DF-2926

Confirmed kernel references

Detail

Evidence (decisive lines)

['panic.1.txt β€” input path: _prop_dictionary_internalize_continue -> prop_dictionary_set -> _prop_dictionary_expand -> _kmalloc panic', 'panic.2.txt β€” OUTPUT path: sys_vquotactl+0x40d -> prop_dictionary_set_uint32 -> prop_dictionary_set -> _prop_dictionary_expand -> _kmalloc panic']

PoC changes

Trigger distilled from the organic vqlivelock writer into a standalone vqalloc.c (not executed standalone β€” both captures are organic reproductions from equivalent loops).

Verified recommended fix

Release every prop object on every sys_vquotactl path; bound the accepted plist size; harden proplib internalization with M_NULLOK + ENOMEM instead of relying on panic-on-limit.

Verdict

Unprivileged guaranteed kernel panic via unbounded plist allocation plus sys_vquotactl's unconditional per-call leak (DF-0146 family). sys_vquotactl() internalizes an arbitrarily large user plist and never releases it (input dict, cmd cstring, output array), so sustained unprivileged vquotactl traffic grows M_PROP_DICT/M_PROP_ARRAY monotonically until the malloc type hits ks_limit, at which point _kmalloc_obj() panics by design (kern_kmalloc.c:702-707) because proplib allocates with M_WAITOK (no M_NULLOK). Observed twice organically from uid 1001 loops (writer side AND, decisively, the 'get usage all' output path at sys_vquotactl+0x40d -> prop_dictionary_set_uint32), both with full console traces. Turned into a minimal trigger (vqalloc.c). Not a re-report of DF-0146: the new result is the guaranteed unprivileged panic from the leak x unbounded input x panic-on-limit policy (multi-call interaction).