DF-3128 / verdict.json
{ "finding_id": "DF-3128", "status": "reproduced", "reproduced": 1, "impact": "dos", "confidence": "certain", "verdict": "mountmsdosfs() drops FreeBSDs media-size bound on the BPB (in-tree comment at msdosfs_vfsops.c:434 admits it). A 9 GiB sparse image claiming a 1.5 TiB FAT32 volume (HugeSectors=0xC0000000, 4 GiB FAT via BigFATsecs) mounts with rc=0 after 38 s of non-interruptible kernel FAT scanning, wiring an attacker-chosen 128 MiB pm_inusemap per mount (up to 512 MiB given the 32-bit fields), stacking linearly with concurrent mounts (256 MiB demonstrated), surviving SIGKILL of the mounting process, and publishing 512 GiB of statfs blocks. FreeBSD rejects the same image instantly. Local DoS via crafted image (root, or any user with vfs.usermount=1 and an accessible device).", "exploit_chain": "craft sparse image with FAT32 BPB (RootDirEnts=0, FSVers=0, 16-bit FATsecs=0, BigFATsecs=X, HugeSectors >> device; FAT[0]=0x0FFFFFF8 to pass fillinusemaps media check) -> vnconfig -> mount_msdos -> kernel kmalloc(X*128 bytes) M_WAITOK + X*1024-entry scan; repeat per mount to exhaust kmem / pin CPUs; unprivileged only when vfs.usermount=1", "evidence": [ "run.log: mount rc=0, wall time 38s; df /dev/vn3 536870912 1 536870911; vmstat MSDOSFS_FAT 1 128M then 2 256M after second mount; TEST 2 shows both mounts present after SIGKILL of mount_msdos", "run.fixed2.log: PATCHED_RC=71 elapsed=0s with MSDOSFS_FAT 0; GOOD32_RC=0 and GOOD16_RC=0 with RW writes", "fix_build.log: nativekernel BUILD_RC=0, installkernel INSTALL_RC=0", "sys/vfs/msdosfs/msdosfs_vfsops.c:434 comment XXX FreeBSD also checks media size in above" ], "kernel_refs": [ "sys/vfs/msdosfs/msdosfs_vfsops.c:432-437", "sys/vfs/msdosfs/msdosfs_vfsops.c:439", "sys/vfs/msdosfs/msdosfs_vfsops.c:459-464", "sys/vfs/msdosfs/msdosfs_vfsops.c:484-490", "sys/vfs/msdosfs/msdosfs_vfsops.c:565-566", "sys/vfs/msdosfs/msdosfs_vfsops.c:577-581", "sys/vfs/msdosfs/msdosfs_fat.c:884-955", "sys/vm/vm_swap.c:307-317", "sys/dev/disk/vn/vn.c:562" ], "poc_changes": "seed sketch had wrong FAT[0] media-ID word (0x0FFFFF08; correct value per fat.c:933-938 is (FAT32_MASK & 0xffffff00)|0xF8 = 0x0FFFFFF8 - misfixed nibble caused silent EINVAL from fillinusemap); vn device nodes exist only for vn0-vn3 on the guest; root login shell is csh so all remote commands run via /bin/sh; craftvar.c added for field bisection", "attempts": 5, "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #0: Thu Jul 2 06:02:54 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64", "runtime_sec": 2410, "guest_dirty": 0, "build_cmd": "cd /root/df3128 && cc -O2 -Wall -o craftfat32 craftfat32.c", "run_cmd": "cd /root/df3128 && sh run.sh", "code_hash": "d1a43b6c1f5147d6aed99f69ccd39507d2c972682f7353625a1449835f6d0c3f", "notes": "Secondary observation: SIGKILL-interrupted mounts may leak one msdosfsmount struct (MSDOSFS_mount inUse +1). First fix iteration compared bytes against dev_dpsize blocks and failed the legit-FAT32 regression; corrected to DEV_BSIZE-block units. Guest reset to clean with-src snapshot after validation.", "recommended_fix": "In mountmsdosfs(), capture devsize = dev_dpsize(dev) (DEV_BSIZE blocks) after VOP_OPEN and reject when pm_HugeSectors * pm_BlkPerSec > devsize (past end of volume), matching FreeBSDs mediasize check.", "fix_status": "fixed", "fix_kernel_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #2: Sun Sep 6 22:40:16 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64", "fix_baseline_reproduced": 1, "fix_patched_reproduced": 0, "fix_verdict": "Patched kernel #2 rejects the crafted image instantly (EINVAL, 0 s, no MSDOSFS_FAT allocation, no mount) while legit newfs_msdos FAT32 (HugeSectors == device size) and FAT16 images mount read-write normally; baseline reproduced on stock #0 immediately before.", "fix_evidence": "findings/poc/DF-3128/run.fixed2.log (PATCHED_RC=71 elapsed=0s; GOOD32_RC=0; GOOD16_RC=0; RW_OK/RW16_OK writes); fix_build.log (BUILD_RC=0, INSTALL_RC=0); fix.diff" } |