DF-4532 / verdict.json
{ "finding_id": "DF-4532", "status": "inconclusive", "reproduced": 0, "impact": "none", "confidence": "certain", "verdict": "REAL BUG, runtime-gated on absent hardware. drm_open() calls device_busy(dev->dev->bsddev) on every successful open (drm_file.c:351) but no live path ever calls device_unbusy(): drm_close() (drm_file.c:366-448) has none, drm_release() is dead on DragonFly (returns at :604), and the only device_unbusy() in sys/dev/drm (drm_drv.c:1230) sits in drm_cdevpriv_dtor() which is never registered (zero devfs_set_cdevpriv() callers in the tree - mechanically proven by static_proof.sh). device_busy() increments dev->busy and forces DS_BUSY (subr_bus.c:1844-1853, recursively busying the PCI parent); device_detach() refuses DS_BUSY devices (subr_bus.c:2129-2134). Net: one open of /dev/dri/cardN by any video-group user wedges the drm device (and its PCI parent) in DS_BUSY forever - kldunload/detach fails EBUSY even with zero fds open. Impact bounded to the privileged detach/hot-replace lifecycle (root-only to observe), hence Low; no memory-safety consequence (the count guards no bounds decision).", "exploit_chain": "", "evidence": "static_proof.log (PASS: all busy/unbusy sites, dtor-unregistered proof, detach-refuses-DS_BUSY excerpt); VERDICT.md; fix.diff (applies clean); fix_build.log (patched drm_file.c compiles RC=0 with exact kernel CFLAGS)", "kernel_refs": [ "sys/dev/drm/drm_file.c:351", "sys/dev/drm/drm_file.c:366", "sys/dev/drm/drm_file.c:445", "sys/dev/drm/drm_drv.c:1230", "sys/kern/subr_bus.c:1844", "sys/kern/subr_bus.c:2129" ], "poc_changes": "Wrote from scratch: trigger.sh (baseline/unload/open/reload cycle) and static_proof.sh (mechanical tree-level proof).", "attempts": 1, "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": 60, "guest_dirty": 0, "build_cmd": "(none needed - shell trigger + static proof)", "run_cmd": "sh static_proof.sh; sh trigger.sh i915 (on DRM hardware)", "code_hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "notes": "Same-anchor conflict note: DF-4533s fix.diff also anchors at drm_close()s tail - merge manually if applying both. DF-4378 (pass 2, drm_drv.c) covers the missing destroy_dev/cdev leak; this finding is the newbus busy-count side of the same broken teardown port and was not previously reported.", "recommended_fix": "Add device_unbusy(dev->dev->bsddev) at the tail of drm_close(), balancing drm_open()s device_busy() (see fix.diff).", "fix_status": "not_testable", "fix_baseline_reproduced": 0, "fix_patched_reproduced": 0, "fix_verdict": "Patch compiles clean (RC=0) with the exact X86_64_GENERIC kernel CFLAGS on the guest; applies cleanly to the pristine tree. Runtime validation needs a DRM device (absent on this guest); the fix restores the busy/unbusy pairing by construction.", "fix_evidence": "findings/poc/DF-4532/fix.diff; findings/poc/DF-4532/fix_build.log" } |