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

cmd_get_usage_all walks accounting RB trees without ac_spin β†’ RB_NEXT traversal cycle (unkillable kernel spin) and torn 64-bit reads

Field Value
ID DF-2924
Status new
Severity Medium
CVSS 3.1 CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:N/I:L/A:H
CWE CWE-662 / CWE-835
File sys/kern/vfs_quota.c
Lines 183-215 (walk :187, :202) vs :158-171/:228-268 (mutation)
Area kern/vfs
Confidence likely
Discovered 2026-09-02
Pass 2 (GLM 5.3 second pass)
Bucket base:kern
Reported pending
Known CVE none
CVE match novel

Summary

cmd_get_usage_all() iterates ac_uroot/ac_groot and reads ac_bytes/ac_limit with NO lock, while vfs_stdaccount and cmd_set_usage_all insert/rebalance and bzero the same trees under ac_spin. RB_FOREACH/RB_NEXT chase rbe_parent pointers; a concurrent rotation reverses a parent/child edge so the successor walk can revisit nodes β€” a cycle means the reading thread spins forever in kernel mode (no lock held, never sleeps, ignores SIGKILL). Two unprivileged processes (quota_enabled=1): one spamming "set usage all" with fresh uid sets, one spamming "get usage all" β†’ potential permanent CPU-eating kernel livelock; plus unsynchronized torn counter reads. Not cleanly reproducible on the guest (sustained traffic first trips DF-2926's proplib-limit panic in <60 s; low-rate run did not livelock) β€” defect directly visible in source, classified likely. Fix: hold ac_spin across the walk (bounded work) or a seqlock for lock-free readers.

Timeline

  • 2026-09-02 Discovered during pass-2 audit of vfs_quota.c (GLM 5.3).

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-2924 Β· 3 files
FileTypeDescriptionSize
vqlivelock.c β€” 3.0 KB view raw
verdict.json β€” 2.3 KB view raw
README.md β€” 1.6 KB ↓ raw

DF-2924 β€” cmd_get_usage_all() walks accounting RB trees without ac_spin

Code

sys/kern/vfs_quota.c:187 and :202 β€” RB_FOREACH over ac_uroot/ac_groot and reads of ac_bytes/ac_limit (:183-184) with NO spinlock, while concurrent writers mutate the same trees under ac_spin: * vfs_stdaccount() vfs_quota.c:158-171 (RB_INSERT + chunk updates) * cmd_set_usage_all() :228-268 (bzero + mass re-insert)

RB_FOREACH/RB_NEXT chase rbe_parent pointers; a concurrent rotation reverses a parent/child edge, so the successor walk can revisit nodes β€” a traversal CYCLE means the reading thread spins forever in kernel mode (no lock held, never sleeps, unkillable). Additionally all counter reads are unsynchronized torn 64-bit reads (accounting garbage).

Trigger (both sides unprivileged when vfs.quota_enabled=1): writer: "set usage all" with fresh uid sets in a loop (rotations); reader: "get usage all" in a tight loop. vqlivelock.c implements this.

Result on guest

Could not obtain a clean measurement window: the sustained traffic first exhausts the proplib malloc limit via the sys_vquotactl leak and panics the kernel ("prop dictionary: malloc limit exceeded" β€” see DF-2926) in under 60 s, both at 4096- and 128-entry arrays; a 60 s low-rate run did not livelock. The locking defect is directly visible in the source (walk without lock vs mutation under lock); classification: likely, not reproduced (masked by DF-2926).

Fix

Take ac_spin around cmd_get_usage_all's walk (it already only reads; contention is bounded), or use a seqlock/version counter for lock-free readers.

Fix verification

not_testable
baseline no→ patch + rebuild →patched clean

not_testable: no baseline reproduction to compare against.

README.md
per-fix-DF-2924

Confirmed kernel references

Detail

Evidence (decisive lines)

['README.md result section β€” DF-2926 panic preempted the measurement both times', 'source citations: unlocked walk at vfs_quota.c:183-215 vs locked mutation at :158-171 and :228-268']

PoC changes

Array size reduced 4096->128 for the second attempt to stay under the proplib limit (still hit it).

Verified recommended fix

Hold ac_spin across cmd_get_usage_all()'s tree walk and counter reads (or a seqlock for readers).

Verdict

cmd_get_usage_all() (vfs_quota.c:187/:202) iterates the accounting RB trees and reads ac_bytes/ac_limit without holding ac_spin while vfs_stdaccount (:158-171) and cmd_set_usage_all (:228-268) mutate/rebalance the same trees under the spinlock. RB_NEXT chases rbe_parent pointers, so a concurrent rotation can make the successor walk revisit nodes => traversal cycle => unkillable kernel-mode spin; plus torn 64-bit counter reads. The locking defect is plain in the source; on the guest every attempt to exercise it was masked by DF-2926 (sustained vquotactl traffic exhausts the proplib malloc limit and panics the kernel within ~60 s, at 4096- and 128-entry arrays alike; a 60 s low-rate run did not livelock). Classified likely / not reproduced (confounder documented).