DF-4786 / verdict.json
{ "finding_id": "DF-4786", "status": "reproduced", "reproduced": 1, "impact": "panic", "confidence": "certain", "verdict": "NULL-pointer write in drm_atomic_normalize_zpos() (drm_blend.c:502-503) proven live: calling the stock drm.kos exported drm_atomic_normalize_zpos with the exact state shape a plane-only zpos property change produces (plane zpos 0->1, plane bound to crtc index 0, crtcs[0].new_state==NULL) panics instantly at drm_atomic_normalize_zpos+0x6b: orb $0x10,0xa(%rax) - fault VA 0xa, supervisor write - a bit-level match for new_crtc_state->zpos_changed=true with new_crtc_state==NULL. drm_atomic_get_new_crtc_state() is by contract NULL for CRTCs outside the transaction (drm_atomic.h:441) and nothing in the ioctl path (set_property_atomic drm_mode_object.c:458, drm_atomic_check_only drm_atomic.c:961) materializes the CRTC state for a plane-only transaction. Reachable from DRM_IOCTL_MODE_OBJ_SETPROPERTY / MODE_ATOMIC by the DRM-master session user on any driver that sets mode_config.normalize_zpos (drm_atomic_helper.c:916-920); NO in-tree DF driver sets that flag yet, so the in-tree trigger is latent - the PoC kld substitutes the driver half and exercises the unmodified kernel half. Impact ceiling: local DoS (fixed-offset NULL write, no controlled corruption). fix.diff (NULL check + -EINVAL) validated: patched drm.ko rebuilt from source, same PoC SURVIVES, guest stays up.", "exploit_chain": "", "evidence": "panic.txt: Stopped at drm_atomic_normalize_zpos+0x6b: orb $0x10,0xa(%rax), fault virtual address = 0xa, supervisor write data, page not present; run.patched.log: zposnull: SURVIVED drm_atomic_normalize_zpos + KLDLOAD_RETURNED_0 (no panic) with fix.diff-applied drm.ko; VERDICT.md for the full chain", "kernel_refs": [ "sys/dev/drm/drm_blend.c:497-505", "sys/dev/drm/include/drm/drm_atomic.h:437-447", "sys/dev/drm/drm_atomic.c:961-1006", "sys/dev/drm/drm_mode_object.c:458-502", "sys/dev/drm/drm_atomic_helper.c:907-931", "sys/dev/drm/drm_ioctl.c:681" ], "poc_changes": "PoC authored fresh for this finding (kld module zposnull.c + Makefile mirroring the drm modules include flags; needed SRCS+=opt_ddb.h opt_drm.h bus_if.h device_if.h pci_if.h for generated headers and removal of a redundant extern already declared in drm_blend.h)", "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": 540, "guest_dirty": 0, "build_cmd": "cd /root/zposnull && sh build.sh (make -f Makefile with SYSDIR=/usr/src/sys, drm include flags; produces zposnull.ko)", "run_cmd": "kldload drm && kldload /root/zposnull/zposnull.ko (baseline: panic; patched: kldload /usr/src/sys/dev/drm/drm/drm.ko first, then PoC -> SURVIVED)", "code_hash": "6e0b34a018eaf63f7fd297ccecd3f8b2e227abbf8897344a763de3649d3fda55", "notes": "Guest kld-based repro: substitutes the missing normalize_zpos driver (no in-tree driver sets mode_config.normalize_zpos); kernel half (drm.ko) is stock/unmodified for the baseline panic. Same code present in current Linux mainline (no NULL check) - latent upstream too. After validation guest was reset with-src (source unpatched, modules cleared).", "recommended_fix": "In drm_atomic_normalize_zpos, return -EINVAL when drm_atomic_get_new_crtc_state() returns NULL before writing zpos_changed (see fix.diff)", "fix_status": "fixed", "fix_baseline_reproduced": 1, "fix_patched_reproduced": 0, "fix_verdict": "fix.diff applied to guest /usr/src/sys/dev/drm/drm_blend.c, drm.ko rebuilt from source (module-only), identical PoC re-run: no panic, module load returns 0, guest stays up (run.patched.log). Baseline stock drm.ko panicked identically before the fix (panic.txt).", "fix_evidence": "run.patched.log (zposnull: SURVIVED drm_atomic_normalize_zpos, KLDLOAD_RETURNED_0 (no panic)); drm-build-patched.log; panic.txt (baseline); build_patched.sh / run_patched.sh exact commands" } |