DF-3233 / verdict.json
{ "finding_id": "DF-3233", "status": "reproduced", "reproduced": 1, "impact": "panic", "confidence": "certain", "verdict": "Verified on the stock INVARIANTS guest (X86_64_GENERIC #0): a crafted UFS1 image (fs_qbmask 0x1fff->0x1ffff, 200704-byte directory of 6256 zero-slack 32-byte entries plus one d_ino=0/reclen=512 free slot at 0x30e00) mounts cleanly; an unprivileged uid-1001 open(O_CREAT) returns EEXIST for a name that does not exist (the OOB read side consuming wild bytes through *res), and the immediately following unprivileged touch /mnt/d/zz produces Fatal trap 12: supervisor write data, page not present stopped at memset+0xd5 inside the ufs_direnter entry-copy path driven by dirbuf = bp->b_data + (0x30e00 & fs_qbmask) = +69120 on an 8192-byte buffer (60928 bytes past its end). Reproduced twice from two fresh vm.sh reset boots with the identical faulting instruction (IP 0xffffffff80bca655) and KVA-dependent fault addresses. Root cause is in-file: ffs_subr.c:80/:151 return *res = bp->b_data + (uoffset & fs_qbmask) with no relation enforced between the attacker-controlled fs_qbmask and the blksize()-derived buffer size; mount validates neither (ffs_vfsops.c:642-646). Underlying primitive is an OOB read+write of attacker-named dirent bytes at a crafted offset (slot position and fs_qbmask both attacker-chosen); the demonstrated floor is the deterministic reboot-equivalent fatal trap.", "exploit_chain": "crafted image (root mount or vfs.usermount=1) -> unprivileged creat in crafted directory -> ufs_lookup slot FOUND at crafted offset 0x30e00 -> ufs_direnter -> ffs_blkatoff returns b_data+69128 (60928 past 8192-byte buffer) -> ep/nep reads + bcopy/memset writes through wild pointer -> fatal supervisor WRITE (observed) or silent neighbour-buffer corruption when the KVA slot is mapped (code-proven, read side observed via the phantom EEXIST). uid0 chain not completed: victim buffer at the adjacent KVA slot not reliably steerable on this guest (per-buffer MAXBSIZE reservations).", "evidence": [ "findings/poc/DF-3233/panic.txt: Fatal trap 12 supervisor write, Stopped at memset+0xd5, current process = touch (run 1)", "findings/poc/DF-3233/panic.2.txt: identical faulting instruction across a fresh boot (run 2)", "findings/poc/DF-3233/run.log + run.2.log: EEXIST for a non-existent name (OOB read side), then fatal trap on touch", "findings/poc/DF-3233/craft3233.c: image builder showing slot math (0x30e00 & 0x1ffff = 0x10e00 -> dirbuf +60928 past buffer)" ], "kernel_refs": [ "sys/vfs/ufs/ffs_subr.c:80", "sys/vfs/ufs/ffs_subr.c:151", "sys/vfs/ufs/fs.h:487-488", "sys/vfs/ufs/ufs_lookup.c:793", "sys/vfs/ufs/ufs_lookup.c:806-870", "sys/vfs/ufs/ffs_vfsops.c:642-646" ], "poc_changes": "n/a (finding + PoC authored together in this pass; two builder fixes during bring-up: cg-relative inode addressing for a cg1 inode, and NBLOCKS round-up so the last directory block is actually written)", "attempts": 4, "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": 1450, "guest_dirty": 1, "build_cmd": "sh /root/df3233/run.sh 0x30e00 (cc -D_KERNEL_STRUCTURES -I/usr/src/sys craft3233.c; cc trigger.c)", "run_cmd": "su -m maxx -c /tmp/trig3233 /mnt/d ; su -m maxx -c touch /mnt/d/zz", "code_hash": "0218300c7fd2038eae771a932d4d1f2dc332ce474102cbb480591f6533b937c1 (craft3233.c), ee0dee16373924ebbff4c9a217df10c3a027db68b4803a7177b059fc6cb57287 (trigger.c)", "notes": "First-trigger EEXIST = the OOB read side consuming wild bytes (name verifiably absent by ls). Fatal write trap reproduced 2/2 fresh boots. Same mount-validation root-cause family as DF-0903/DF-3212 but a distinct sink file/function (ffs_subr.c ffs_blkatoff -> directory entry write path).", "recommended_fix": "Mount-time geometry validation in ffs_mountfs requiring fs_qbmask==fs_bsize-1 (and fs_qfmask==fs_fsize-1, fs_bmask/fs_fmask their complements, fragshift/fshift/bshift consistent) \u2014 rejects the crafted image at mount; see fix.diff.", "fix_status": "fixed", "fix_kernel_uname": "DragonFly dfbsd 6.5-DEVELOPMENT #1: Wed Sep 9 00:49:30 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 #1 (shared fix.diff: fs_qbmask must equal fs_bsize-1 for modern inode formats): the DF-3233 crafted image (qbmask 0x1ffff) is rejected at mount with ffs_mountfs: /mnt: inconsistent fragment geometry / mount EINVAL (fix_patched.log); the fatal supervisor-write trap is unreachable because the trigger filesystem can no longer mount. Note: the first fix attempt had inverted mask semantics (fs_bmask is the round-DOWN mask ~(bsize-1), fs_qbmask the positive bsize-1) which false-positived the guests own /boot; corrected and re-validated (guest boots cleanly, stock images mount).", "fix_evidence": "findings/poc/DF-3233/fix_patched.log" } |