DF-4840 / verdict.json
{ "finding_id": "DF-4840", "status": "reproduced", "reproduced": 1, "impact": "panic", "confidence": "certain", "verdict": "drm_mode_obj_set_property_ioctl() (drm_mode_object.c:504-533) uses the object returned by drm_mode_object_find() with no reference and no lock. Objects registered without free_cb (MODE drm_modes.c:74, CRTC drm_crtc.c:296, PLANE drm_plane.c:193, PROPERTY drm_property.c:117) get no kref (drm_mode_object.c:145-148). DRM_MODE_OBJECT_MODE objects are destroyed at runtime by unprivileged DRM_IOCTL_MODE_GETCONNECTOR reprobes (drm_connector.c:1759 fill_modes under mode_config.mutex -> drm_probe_helper.c:415/514 prune -> drm_mode_destroy=unregister+kfree drm_modes.c:90-98) while the SETPROPERTY ioctl (DRM_MASTER-gated, drm_ioctl.c:679) holds no lock. Verified on the guest with stock drm.ko functions: (1) 110-295 SPONTANEOUS UAF window hits per 30-60s in an observation probe of the exact find->obj->properties window (run.log); (2) full attacker-controlled chain under forced interleave at the exact preemption point between :515 and :519 - fabricated drm_object_properties/drm_property consumed by the verbatim :519-532 dispatch, canary indirect call via plane->funcs (planted at freed-block+112) fired through STOCK drm_mode_plane_set_obj_prop, dispatch returned 0, 14-15 chains per run; (3) panic at attacker-chosen data: mode 8 = GPF Stopped at drm_mode_plane_set_obj_prop+0x36 via groomed function pointer 0x4141414141414140, mode 7 = GPF at property->dev=0x5151515151515100. Honest negative: the stock COMPILED ioctl window is 3 instructions (objdump) and never hit spontaneously in ~7.3e10 calls on this QEMU/KVM guest (run.3.log) - the race is certain, the spontaneous stock-window win-rate on this guest is below observable threshold. Fix validated: mode_config.mutex held across find->dispatch (fix.diff, CANRECURSE makes set_property_legacys nested lock_all safe): patched drm.ko rebuilt, objdump confirms lock-before-find, A/B shows old sequence 36 hits/14 controlled calls vs fixed sequence 0 hits in 4.2e8 forced iterations, real patched ioctl 2e9 calls no regression.", "exploit_chain": "unpriv GETCONNECTOR (count_modes=0) destroys modes <-> DRM-master MODE_OBJ_SETPROPERTY(mode id) races find->use on the freed 240-byte drm_display_mode; heap groom reclaims the block so obj->properties (block+24) points at fabricated drm_object_properties; drm_mode_obj_find_prop_id walk (drm_mode_object.c:421-423) returns fabricated drm_property (id match); dispatch reads property->dev (controlled deref) and legacy path calls drm_mode_plane_set_obj_prop -> plane->funcs->set_property where plane is container_of(freed block) and funcs is block+112 (controlled indirect call). Demonstrated: controlled call canary + GPF panics at attacker-chosen addresses through stock drm.ko code. uid0 endgame (fabricated structures in userland pages on the no-SMAP/no-KASLR guest, pivot via stock dispatch to a write primitive) documented but not completed; requires a real DRM card for the pure-userspace trigger (guest has none - kld fabricated the environment, DF-4786 precedent).", "evidence": "run.log (mode4 spontaneous 110 hits/30s + mode5 15 CONTROLLED INDIRECT CALL lines, mode1 40.5e9-call contrast); panic.txt (mode8: Stopped at drm_mode_plane_set_obj_prop+0x36: ret GPF via groomed fp 0x4141414141414140); panic.2.txt (mode7 GPF at property->dev=0x5151515151515100); run.3.log (stock-window 7.3e10-call honest negative incl. IRQ-cpu+ping-flood); VERDICT.md (A/B fix numbers: mode5 36 hits vs mode9 0 hits in 4.2e8 forced iterations; patched objdump lock-before-find); build_patched.sh + drm-build-patched.log", "kernel_refs": [ "sys/dev/drm/drm_mode_object.c:504", "sys/dev/drm/drm_mode_object.c:515", "sys/dev/drm/drm_mode_object.c:519", "sys/dev/drm/drm_mode_object.c:522", "sys/dev/drm/drm_mode_object.c:145", "sys/dev/drm/drm_modes.c:74", "sys/dev/drm/drm_modes.c:90", "sys/dev/drm/drm_connector.c:1759", "sys/dev/drm/drm_probe_helper.c:415", "sys/dev/drm/drm_probe_helper.c:514", "sys/dev/drm/drm_ioctl.c:663", "sys/dev/drm/drm_ioctl.c:679", "sys/dev/drm/drm_plane.c:462" ], "poc_changes": "No seed pack existed. Authored from scratch: kld fabricates a minimal drm_device (idr_mutex/crtc_idr/mutex/connection_mutex, DRIVER_MODESET non-atomic driver) and races stock drm.ko functions; struct-offset pitfalls found and fixed during bring-up: drm_display_mode does NOT start with drm_mode_object (head[16] first, drm_modes.h:208-229 -> properties at block+24, not +8) and plane->funcs sits at base+96 within the 240-byte block; DFly kthread_create has no flags arg; linux mutex shim needs lockinit.", "attempts": 9, "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": 2100, "guest_dirty": 0, "build_cmd": "sh build.sh (bsd.kmod.mk against /usr/src; stock drm.ko symbols resolved)", "run_cmd": "kldload drm; kldload ./modeuaf.ko; sysctl hw.modeuaf.mode=4|5|8|9; sysctl hw.modeuaf.stop=1; kldunload modeuaf", "code_hash": "29c64346c94a39e006f5f780f7eaa8a313a880f87faaa948d31feb0908eaf657", "notes": "Evidence ladder: spontaneous window hits (probe of the same window widened by one atomic store) -> forced-interleave full controlled chain (canary indirect call through stock drm_mode_plane_set_obj_prop, ret=0) -> panics at attacker-chosen call/deref targets -> fix A/B (0 hits fixed vs 36 hits unfixed under identical forced interleave). Stock compiled window (~3 instr, objdump drm.ko+0x3b..0x47) never hit spontaneously in 7.3e10 attempts on this guest - stated plainly, not papered over. Same reachability posture as DF-4786 (latent on the card-less audit guest, live on any system with a real DRM driver; master-gated ioctl side, fully unprivileged destroy side). DF-4506s lockless-idr-find pattern does NOT instantiate on crtc_idr: find+kref are under idr_mutex and all dynamically-created classes unregister before kfree.", "recommended_fix": "Hold dev->mode_config.mutex across find->dispatch in drm_mode_obj_set_property_ioctl() (see fix.diff); longer-term, register mode objects with a free_cb/kref like connectors/fbs/blobs.", "fix_status": "fixed", "fix_baseline_reproduced": 1, "fix_patched_reproduced": 0, "fix_verdict": "fix.diff applied to guest /usr/src, drm.ko rebuilt (drm-build-patched.log). Baseline (unfixed sequence, forced interleave): 36 hits / 14 controlled indirect calls. Patched-sequence control (mode_config.mutex across find->read, identical forced interleave): 0 hits in 4.2e8 iterations. Real patched ioctl: 2e9 calls, no deadlock/regression (mode_config.mutex is LK_CANRECURSE so set_property_legacys drm_modeset_lock_all nesting is safe). objdump of the patched ioctl shows lockmgr acquisition before the inlined find.", "fix_evidence": "findings/poc/DF-4840/fix.diff + build_patched.sh + drm-build-patched.log + VERDICT.md section 5" } |