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)
PoC verification
Evidence pack
findings/poc/DF-2926 Β· 8 files| File | Type | Description | Size | |
|---|---|---|---|---|
| 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
- Release every prop object on every path of sys_vquotactl (fixes the DF-0146 leak β the monotonic growth driver).
- Bound the accepted plist size for the quota command (copyin a limited externalized size, reject oversized commands).
- Proplib hardening: internalize with M_NULLOK and return ENOMEM instead of relying on the panic-on-limit policy.
Fix verification
not_testablenot_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
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).
No comments yet.