DF-4900 / verdict.json
{ "finding_id": "DF-4900", "status": "reproduced", "reproduced": 1, "impact": "panic", "confidence": "certain", "verdict": "Reproduced deterministically on the audit guest: 512 sequential dmsetup creates succeed (distinct minors, exact clone-bitmap recycle verified in run.log Part A); the 513th create allocates unit 512 whose dkmakeminor encoding aliases the live unit-0 devices minor, so dm_dev_insert (dm_dev.c:191) matches by minor and KKASSERT(dmv != NULL) at dm_dev.c:195 panics the kernel (panic.2.txt, backtrace through dm_dev_create_ioctl/dmioctl). Root cause: devfs_clone_bitmap_get(&dm_minor_bitmap, 0) at dm_dev.c:234 passes limit=0 (unlimited) while the minor encoding (diskslice.h:248, DKMAXUNITS=512) holds 9 unit bits. Fix validated by in-guest module rebuild (build.log): with fix.diff the 513th create is refused cleanly and the guest stays up (fix_run.log).", "exploit_chain": "operator/root opens /dev/mapper/control (0640 root:operator) -> 513 sequential DM_DEV_CREATE ioctls with unique names -> unit 512 allocated (clone bitmap unbounded) -> dkmakewholedisk(512) == dkmakewholedisk(0) -> dm_dev_insert by-minor lookup hits the live unit-0 device -> KKASSERT panic (deterministic DoS). On non-assert builds: duplicate live minors -> dm_dev_lookup_minor returns first TAILQ match -> by-minor open-tracking and remove/status/suspend/table ioctls operate on the WRONG device; destroy of a unit>=512 device feeds dkunit() truncation (dm_dev.c:290) to devfs_clone_bitmap_put, freeing the aliased live devices unit bit (double-allocation).", "evidence": "run.log (Part A recycle + 512 live devices), panic.2.txt (controlled panic with DF4900 console markers; first observation in panic.accidental-run.txt), fix_run.log + build.log (patched module: 513th refused, guest alive), fix.diff", "kernel_refs": [ "sys/dev/disk/dm/dm_dev.c:234", "sys/dev/disk/dm/dm_dev.c:191", "sys/dev/disk/dm/dm_dev.c:195", "sys/dev/disk/dm/dm_dev.c:290", "sys/sys/diskslice.h:248", "sys/kern/subr_disk.c:700", "sys/vfs/devfs/devfs_helper.c:221" ], "poc_changes": "no seed PoC; authored from scratch after deriving the exact minor encoding (predicted 0x1e110007 for unit 0 from diskslice.h before running; guest matched)", "attempts": 2, "guest_uname": "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", "runtime_sec": 1500, "guest_dirty": 1, "build_cmd": "cd /usr/src/sys/dev/disk/dm && make (module-only rebuild for fix validation)", "run_cmd": "sh /root/poc_phase1.sh; sh /root/poc_phase2.sh (baseline) / sh /root/fix_test.sh (patched)", "code_hash": "692f402d988a206642cf91a99f0a5b82c296716dbe078b31e7e64fc1912135cd", "notes": "Same KKASSERT site as known DF-2442 but a different, race-free trigger (deterministic minor collision from unit-space exhaustion vs concurrent name collision) and a different fix (allocator bound). Privilege bar identical to the filed dm family (control node 0640 root:operator). Guest was reset with-src after validation.", "recommended_fix": "Bound the clone-bitmap allocation to the encodable unit space: devfs_clone_bitmap_get(&dm_minor_bitmap, DKMAXUNITS-1) with ENOSPC rollback in dm_dev_create, and mask the put value in dm_dev_destroy.", "fix_status": "fixed", "fix_baseline_reproduced": 1, "fix_patched_reproduced": 0, "fix_verdict": "fix.diff applied to the guests /usr/src copy, dm.ko rebuilt (build.log) and loaded: baseline kernel panics on the 513th create (panic.2.txt); patched module refuses it cleanly with an error, guest stays up, remove_all succeeds (fix_run.log). Bad behavior GONE.", "fix_evidence": "findings/poc/DF-4900/build.log, findings/poc/DF-4900/fix_run.log, findings/poc/DF-4900/fix.diff" } |