DF-2705 / verdict.json
{ "finding_id": "DF-2705", "status": "untested", "reproduced": 0, "impact": "none", "confidence": "likely", "verdict": "vop_write's quota glue (sys/kern/vfs_vopops.c:476-478, 489, 495-497) never charges VFS_ACCOUNT usage for files with va_nlink==0, while vop_nremove (:1665-1668) refunds the entire size on last-link removal and vq_write_ok checks against only the charged usage โ so unlinking an open fd then writing unbounded completely bypasses per-uid/gid/mount quota limits (and N concurrent writers can TOCTOU-overshoot by (N-1)x limit). Certain as logic, but the whole mechanism is gated on vfs_quota_enabled (sys/kern/vfs_quota.c:111-113), default 0, boot-tunable only (CTLFLAG_RD) โ non-default admin config. Phase V skipped: reproducing requires booting the shared guest with vfs.quota_enabled=1 and vquota limit setup for a Low accounting-bypass; every line of the chain was verified statically.", "exploit_chain": "open+write to limit -> unlink while open (usage refunded, nlink=0) -> keep writing through fd: checks pass trivially, charges never happen -> unbounded growth on quota-limited mount", "evidence": [ "sys/kern/vfs_vopops.c:476-478 do_accounting gated on va_nlink>0", "sys/kern/vfs_vopops.c:489 vq_write_ok check vs charged usage", "sys/kern/vfs_vopops.c:495-497 charge skipped when do_accounting==0", "sys/kern/vfs_vopops.c:1665-1668 last-link removal refunds full size", "sys/kern/vfs_quota.c:111-113 vfs_quota_enabled default 0, boot tunable only", "findings/poc/DF-2705/VERDICT.md full chain + cross-refs" ], "kernel_refs": [ "sys/kern/vfs_vopops.c:472", "sys/kern/vfs_vopops.c:477", "sys/kern/vfs_vopops.c:489", "sys/kern/vfs_vopops.c:495", "sys/kern/vfs_vopops.c:1666", "sys/kern/vfs_quota.c:111" ], "poc_changes": "no PoC built: needs non-default boot tunable + vquota setup on the shared guest; Low severity", "attempts": 0, "guest_uname": "", "runtime_sec": 0, "guest_dirty": 0, "build_cmd": "", "run_cmd": "", "code_hash": "", "notes": "Cross-file item for orchestrator (belongs to sys/kern/vfs_quota.c row): vfs_stdaccount/unode_insert kmalloc(M_WAITOK) under mp->mnt_acct.ac_spin (vfs_quota.c:153-165) โ blocking alloc under spinlock, same pattern at :236-306. Recommend separate finding there.", "recommended_fix": "Charge unlinked-open files (drop the va_nlink>0 gate on do_accounting in vop_write) or account on block allocation inside the FS; refunds in vop_nremove already balance." } |