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

Uninitialized kernel stack stored into accounting state and disclosed via "get usage all" (ac_limit/ac_bytes/space)

Field Value
ID DF-2925
Status new
Severity Medium
CVSS 3.1 CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N
CWE CWE-457 Use of Uninitialized Variable
File sys/kern/vfs_quota.c
Lines 277+280 (primary), 249+263, 293-294, 314-315
Area kern/vfs
Confidence certain
Discovered 2026-09-02
Pass 2 (GLM 5.3 second pass)
Bucket kernleak
Reported pending
Known CVE none
CVE match novel

Summary

cmd_set_limit() declares uint64_t limit uninitialized, discards prop_dictionary_get_uint64()'s return (which leaves *valp untouched when the key is missing or not a number), and stores the stale stack slot into mp->mnt_acct.ac_limit; cmd_get_usage_all() echoes ac_limit to userspace. Same pattern for space in cmd_set_usage_all and the locals of cmd_set_limit_uid/gid. Unprivileged disclosure of 8 bytes of kernel stack per call. VERIFIED: leaked values track the previous syscall's leftover slot β€” the disclosed slot is steerable by the caller's prior syscall; a pointer-seeding syscall yields kernel addresses (KASLR-relevant). On the fix-validated kernel (zero-init, included in DF-2922's fix.diff) every run returns 0. Evidence: findings/poc/DF-2925/ (vqleak.c, run.200.log, run.patched.log).

Timeline

  • 2026-09-02 Discovered during pass-2 audit of vfs_quota.c (GLM 5.3); leak reproduced 200 runs + fix validated.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-2925 Β· 8 files
FileTypeDescriptionSize
vqleak.c β€” 3.0 KB view raw
build.sh β€” 42 B view raw
run.sh β€” 27 B view raw
run.log β€” 470 B view raw
run.200.log β€” 396 B view raw
run.patched.log β€” 162 B view raw
verdict.json β€” 2.7 KB view raw
README.md β€” 1.3 KB ↓ raw

DF-2925 β€” uninitialized kernel stack disclosed through quota accounting

cmd_set_limit() (sys/kern/vfs_quota.c:273-284) declares uint64_t limit uninitialized, discards the return of prop_dictionary_get_uint64() (which leaves *valp untouched when the key is absent or not a number β€” sys/libprop/prop_dictionary_util.c), and stores the stale stack slot into mp->mnt_acct.ac_limit (:280). cmd_get_usage_all() then echoes ac_limit to userspace (vfs_quota.c:184). Same pattern for space in cmd_set_usage_all() (:249 β†’ :255/:263) and for the limit/uid/gid locals of cmd_set_limit_uid/gid (:293-294, :314-315).

Unprivileged readout of 8 bytes of kernel stack per call. The leaked slot tracks whatever the previous syscall left at that stack depth (observed: the previous vquotactl's loop counters β€” the value follows the caller's prior syscall activity, i.e. steerable stack disclosure; with a pointer-seeding syscall before vquotactl this leaks kernel addresses).

Build / run

cc -O -o vqleak vqleak.c -lprop
./vqleak /boot 5

Expected (unpatched)

run  0: set=0 get=0  leaked ac_limit=0x0000000000000010 ...
run  N: leaked ac_limit=<previous syscall's leftover> (varies, nonzero)

On the DF-2922-patched kernel the same command reports ac_limit=0.

Fix verification

fixed
baseline reproduced→ patch + rebuild →patched clean

On kernel #1 with the zero-initialization in fix.diff, the same PoC reports ac_limit=0 on every run (was nonzero stack garbage on #0).

VERDICT.md in DF-2922 pack (fix validation section); run on patched kernel: all runs 0x0
DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #1: Thu Sep 3 13:57:47 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64

Confirmed kernel references

Detail

Evidence (decisive lines)

['run.log β€” 5-run sample: leaked ac_limit=0x10/0x0/0x3/0x4/0x5 (not attacker-set, varies with prior syscall)', "run.200.log β€” 200-run sweep showing the slot tracks the previous call's loop counter"]

PoC changes

prop_object_is_array() is not exported to userland β€” removed from the reply parser.

Verified recommended fix

Zero-initialize limit/space/id before the prop_dictionary_get_* calls (included and validated in DF-2922's fix.diff).

Verdict

Uninitialized kernel stack disclosed to unprivileged users through quota accounting. cmd_set_limit() (vfs_quota.c:273-284) stores the never-initialized uint64_t 'limit' into mp->mnt_acct.ac_limit when the "limit" key is absent (prop_dictionary_get_uint64 leaves *valp untouched on failure, prop_dictionary_util.c), and cmd_get_usage_all() echoes ac_limit to userspace (vfs_quota.c:184). Observed: consecutive unprivileged calls leaked 0x10, 0, 3, 4, 5, ... i.e. the previous syscall's leftover stack slot (the value tracks prior activity = steerable disclosure; same pattern for 'space' at :249/:263 and the locals at :293/:314). On the DF-2922-patched kernel (which zero-initializes these) the same PoC reports 0 across all runs β€” fix validated on the same build.