Unconditional kprintf() log flood in mbnambuf_write() from crafted β or merely long-LFN β FAT directories: unbounded kernel console/msgbuf spam from unprivileged directory churn
Summary
mbnambuf_write() emits two UNCONDITIONAL kprintf()s (:1038 'non-decreasing id', :1048 'file name length too large') on every rejected LFN slot. Upstream FreeBSD gates both behind #ifdef MSDOSFS_DEBUG; DragonFly removed the guard. Each directory scan re-prints once per offending entry: msdosfs_lookup (lookup.c:274) for every open/stat/exec path walk, and msdosfs_readdir (vnops.c:1665) for every getdents. Trigger (a) crafted image: N repeated LFN slot triples -> N 'non-decreasing id' lines per scan; 10 full 13-char LFN slots -> 'file name length 130 too large'. Trigger (b) NO crafted data: a stock Windows-created volume legitimately holds LFNs up to 255 UCS-2 chars, but :1047 caps reassembly at 127 BYTES, so every lookup/getdents of such a directory prints per slot. An unpriv user looping 'ls /mnt/dir' floods the msgbuf/dmesg and the serial console (serialized, slow) without bound, and can grow /var unboundedly where console output is logged - a persistent, rate-unlimited kernel-side resource drain driven purely by on-disk metadata. The user cannot be rate-limited by the kernel. Impact ceiling: unbounded dmesg/console/log spam, console-lock contention stalls, /var exhaustion - availability nuisance, no memory corruption, no info leak. Unverified (Low/log-spam per contract); repro sketch in summary. Fix: gate both prints behind MSDOSFS_DEBUG exactly as upstream (behavioral returns unchanged). [VERIFIED pass2: reproduced - 1312 unconditional kernel log lines per 100 unpriv scans echoed on serial console; fix A/B delta 0]
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-3115 Β· 13 files| File | Type | Description | Size | |
|---|---|---|---|---|
| craft_lfn.py | β | 5.3 KB | view raw | |
| run_guest.sh | β | 1.1 KB | view raw | |
| run.log | β | 866 B | view raw | |
| console_flood_excerpt.log | β | 70.2 KB | view raw | |
| README.md | β | 677 B | β raw | |
| VERDICT.md | β | 2.2 KB | β raw | |
| build.sh | β | 140 B | view raw | |
| run.sh | β | 208 B | view raw | |
| manifest.json | β | 860 B | view raw | |
| verdict.json | β | 2.2 KB | view raw | |
| fix.diff | β | 812 B | view raw | |
| env.txt | β | 272 B | view raw | |
| compile_validation.txt | β | 1.8 KB | view raw |
DF-3115 -- verification pack
Finding: unconditional kprintf log flood in mbnambuf_write from crafted/long LFN directories
Status: reproduced / impact denial_of_service / confidence certain
What was run
host: python3 craft_lfn.py out.img A|B
How to reproduce
guest: vnconfig -c -S 2m /dev/vn0 img && mount_msdos -o ro /dev/vn0 /mnt && su -m nobody -c 100x ls/stat loop, count dmesg lines
Expected
1212 non-decreasing lines + 100 too-large lines per 100 passes; console log floods
See VERDICT.md for the full narrative and verdict.json for the persisted machine verdict. Artifacts: craft_lfn.py, run_guest.sh, run.log, console_flood_excerpt.log.
DF-3115 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
Unprivileged ls/stat churn on a crafted FAT directory drives the two unconditional kprintfs in mbnambuf_write at unbounded rate: pattern A (two id-1 LFN slots per group, 12 groups) printed 1212 non-decreasing-id lines across 100 unprivileged ls+stat passes; pattern B (10 full 13-char LFN slots, 130 bytes reassembled) printed 100 file-name-length-too-large lines across 100 ls passes. The serial console echoes every line (1312 msdosfs lines captured on the console log during the run), i.e. an unprivileged user floods msgbuf and any logging console with no kernel-side rate limit; a stock Windows LFN of 255 UCS-2 chars hits the 127-byte cap on ordinary directories. Availability only.
Kernel references
- sys/vfs/msdosfs/msdosfs_conv.c:1038
- sys/vfs/msdosfs/msdosfs_conv.c:1048
- sys/vfs/msdosfs/msdosfs_lookup.c:270
- sys/vfs/msdosfs/msdosfs_vnops.c:1662
Exploit chain
(none: availability/correctness finding; no privilege chain)
Fix
gate both prints behind MSDOSFS_DEBUG exactly as upstream FreeBSD; combined fixed kernel: same workload emits zero lines
See fix.diff in this directory (git-apply-able against the read-only sys/ tree; applied only inside the disposable verification guest).
Reproduction
build: host: python3 craft_lfn.py out.img A|B
run: guest: vnconfig -c -S 2m /dev/vn0 img && mount_msdos -o ro /dev/vn0 /mnt && su -m nobody -c 100x ls/stat loop, count dmesg lines
expected: 1212 non-decreasing lines + 100 too-large lines per 100 passes; console log floods
Notes on the PoC (what changed vs the finding sketch)
first craft attempt printed nothing: win2unixfn only reaches mbnambuf_write when the highest-ordinal LFN slot carries WIN_LAST (0x40) so the checksum re-establishes; craft_lfn.py now sets it and a matching chksum byte
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)
run.log (both patterns, before/after msgbuf counts), console_flood_excerpt.log (serial console echo of 1312 lines), craft_lfn.py (WIN_LAST + chksum details needed to reach mbnambuf_write)
PoC changes
first craft attempt printed nothing: win2unixfn only reaches mbnambuf_write when the highest-ordinal LFN slot carries WIN_LAST (0x40) so the checksum re-establishes; craft_lfn.py now sets it and a matching chksum byte
Verified recommended fix
gate both prints behind MSDOSFS_DEBUG exactly as upstream FreeBSD; combined fixed kernel: same workload emits zero lines
Verdict
Unprivileged ls/stat churn on a crafted FAT directory drives the two unconditional kprintfs in mbnambuf_write at unbounded rate: pattern A (two id-1 LFN slots per group, 12 groups) printed 1212 non-decreasing-id lines across 100 unprivileged ls+stat passes; pattern B (10 full 13-char LFN slots, 130 bytes reassembled) printed 100 file-name-length-too-large lines across 100 ls passes. The serial console echoes every line (1312 msdosfs lines captured on the console log during the run), i.e. an unprivileged user floods msgbuf and any logging console with no kernel-side rate limit; a stock Windows LFN of 255 UCS-2 chars hits the 127-byte cap on ordinary directories. Availability only.
No comments yet.