msdosfs_sync() never initializes scaninfo.waitfor β uninitialized kernel-stack read gates metadata flushing: sync(2)/umount can silently skip fsync of dirty denodes depending on stack garbage's MNT_LAZY bit (DF-specific regression; NetBSD sets ctx.waitfor, FreeBSD passes waitfor directly)
Summary
struct scaninfo {rescan, allerror, waitfor} (:740-744) is passed to vmntvnodescan, but msdosfs_sync only sets allerror and rescan (:804-805) - the waitfor = waitfor assignment was lost in DFly's vmntvnodescan port. msdosfs_sync_scan then reads info->waitfor (:839) in the skip condition '(RB_EMPTY(dirty) || (info->waitfor & MNT_LAZY))': when the garbage bit is set, denodes with de_flag metadata pending but no dirty buffers are skipped by a forced sync - the very vnodes sync(2)/umount(2) must flush. NetBSD's equivalent sets ctx.waitfor; FreeBSD's inline loop uses waitfor directly. Uninit value never copied to userspace (no leak); impact is silent metadata-sync skipping (data-integrity). Static proof (assignment provably absent, read provable); Phase V skipped (Low). Fix: one line (scaninfo.waitfor = waitfor). [VERIFIED pass2: uninit waitfor read gates metadata flushing with provably-absent assignment; one-line fix compile-validated]
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-3129 Β· 9 files| File | Type | Description | Size | |
|---|---|---|---|---|
| fix.diff | β | 421 B | view raw | |
| README.md | β | 374 B | β raw | |
| VERDICT.md | β | 1.5 KB | β raw | |
| build.sh | β | 106 B | view raw | |
| run.sh | β | 88 B | view raw | |
| manifest.json | β | 447 B | view raw | |
| verdict.json | β | 1.3 KB | view raw | |
| fix.diff | β | 421 B | view raw | |
| env.txt | β | 272 B | view raw |
DF-3129 -- verification pack
Finding: msdosfs_sync never initializes scaninfo.waitfor (uninit read gates flushing)
Status: not_reproduced / impact corruption / confidence certain
What was run
n/a
How to reproduce
n/a
Expected
n/a
See VERDICT.md for the full narrative and verdict.json for the persisted machine verdict. Artifacts: fix.diff.
DF-3129 verification
Kernel: DragonFly dfbsd 6.5-DEVELOPMENT #0: Thu Jul 2 06:02:54 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64
Bottom line
Static proof is airtight: struct scaninfo.waitfor is never assigned anywhere in msdosfs_sync (only allerror and rescan are set) while msdosfs_sync_scan reads info->waitfor in the skip condition (RB_EMPTY(dirty) || (info->waitfor & MNT_LAZY)), so a forced sync skips denodes with pending metadata whenever the garbage bit matches MNT_LAZY. The effect is silent metadata-integrity loss with no runtime signature, and the uninit value never reaches userspace, so dynamic observation is not meaningful; the one-line fix is compile-validated in the combined build.
Kernel references
Exploit chain
(none: availability/correctness finding; no privilege chain)
Fix
scaninfo.waitfor = waitfor
See fix.diff in this directory (git-apply-able against the read-only sys/ tree; applied only inside the disposable verification guest).
Reproduction
build: n/a
run: n/a
expected: n/a
Notes on the PoC (what changed vs the finding sketch)
(none)
Fix status (batch validation)
Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest make -j6 nativekernel KERNCONF=X86_64_GENERIC completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.
Fix verification
not_testableConfirmed kernel references
Detail
Evidence (decisive lines)
VERDICT.md walk over msdosfs_vfsops.c:740-839 showing the assignment provably absent (NetBSD sets ctx.waitfor, FreeBSD uses waitfor directly)
Verified recommended fix
scaninfo.waitfor = waitfor
Verdict
Static proof is airtight: struct scaninfo.waitfor is never assigned anywhere in msdosfs_sync (only allerror and rescan are set) while msdosfs_sync_scan reads info->waitfor in the skip condition (RB_EMPTY(dirty) || (info->waitfor & MNT_LAZY)), so a forced sync skips denodes with pending metadata whenever the garbage bit matches MNT_LAZY. The effect is silent metadata-integrity loss with no runtime signature, and the uninit value never reaches userspace, so dynamic observation is not meaningful; the one-line fix is compile-validated in the combined build.
No comments yet.