DF-2477 / verdict.json
{ "finding_id": "DF-2477", "status": "reproduced", "reproduced": 1, "impact": "corruption", "confidence": "certain", "verdict": "The verbatim md_drvcleanup (md.c:466-496) takes one unlocked atomic_fetchadd_int(&refcnt, 0) snapshot and then frees every md_s (kfree(sc->secp) :492, kfree(sc) :493) with no lock mdopen/mdclose hold (they take only sc->tok, which belongs to the freed object) and without clearing dev->si_drv1. The rig drives the exact interleaving: drvcleanup passes the refcnt==0 check, a racing verbatim mdopen completes at the drain boundary (rc=0, refcnt -> 1), then drvcleanup frees the md_s anyway. Result: refcnt==1 on freed memory, dev->si_drv1 dangling, and a read through the dangling pointer after a marker reclaim returns 0xc7c7c7c7 - use-after-free confirmed (the next mdopen/mdclose would take sc->tok on freed memory). md is compiled into the stock kernel (no unload at runtime here), so the trigger is module-config-specific, as filed. Fix v2 (destroy_dev drain first, then re-check refcnt, free only at 0) A/B: drvcleanup returns EBUSY, nothing freed, reclaim-read returns intact values (not the marker) - no UAF.", "exploit_chain": "root-config path (module unload while opening); privesc ceiling: use-after-free on privileged-triggered unload; no unpriv trigger.", "evidence": "run.log: racing mdopen rc=0 refcnt=1 (open SUCCEEDED against an sc that was then freed); si_drv1 never NULLed; reclaim-read 0xc7c7c7c7; fixed: EBUSY, no frees in log, reclaim-read intact.", "kernel_refs": [ "sys/dev/disk/md/md.c:471", "sys/dev/disk/md/md.c:472", "sys/dev/disk/md/md.c:492", "sys/dev/disk/md/md.c:493", "sys/dev/disk/md/md.c:133" ], "poc_changes": "interleave implemented as a synchronous call at the interposed disk_destroy/destroy_dev boundary (single-threaded model of the two-CPU interleaving); first two designs (kthread stall) deadlocked and were replaced.", "attempts": 3, "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": 900, "guest_dirty": 0, "build_cmd": "sh /root/rigs/dfv_md/gen.sh && make -m /usr/src/share/mk (in dfv_md)", "run_cmd": "kldload dfv_md.ko; sysctl kern.dfv_md_run=1; sysctl -n kern.dfv_md_status", "code_hash": "b0e0fd711f06ebc68896a1a8404ecd262a5cac6ddb5bde80f8859f4ceb7d154a", "notes": "md is compiled into the stock kernel (no md module in kldstat), so the MOD_UNLOAD path is not drivable at runtime here; the deterministic interleave rig (open at the drain boundary) demonstrates the race consequence on the verbatim functions. Fixed drvcleanup returns EBUSY and frees nothing.", "recommended_fix": "destroy_dev (drain) first, then re-check refcnt before any free", "fix_status": "fixed" } |