DF-4584 / verdict.json
{ "finding_id": "DF-4584", "status": "inconclusive", "reproduced": 0, "impact": "none", "confidence": "likely", "verdict": "Code-proven race, unverifiable on the audit guest. radeon_bo_get_surface_reg (radeon_object.c:601) walks and steals the device-global rdev->surface_regs[] under ONLY the requesting BOs reservation (assert at :609); the victim BO in reg->bo is dereferenced (:628, :641) and written (:642, surface_reg=-1) with no lock, while the victims final-unref path radeon_ttm_bo_destroy -> radeon_bo_clear_surface_reg (:656, reg->bo=NULL at :667) -> kfree (:85) runs unlocked. NULL-deref panic (interleaving B) or a 4-byte 0xFFFFFFFF write into freed radeon_bo slab (interleaving A). Unprivileged reach: GEM_SET_TILING stores RADEON_TILING_SURFACE (radeon_gem.c:503), mmap fault -> radeon_bo_fault_reserve_notify (:793) -> check_tiling(:804) -> get_surface_reg with all 8 regs busy. Trigger built cleanly on the guest but the guest has no radeon GPU (virtio VGA 0x1111:1234 only), no /dev/dri, and no radeonkms.ko in /boot/kernel, so the path is live-unreachable there: open(/dev/dri/renderD128) = ENOENT (run.log). Status inconclusive = attempted, hardware absent, neither reproduced nor refuted on-guest.", "exploit_chain": "unpriv user: create 8+ VRAM GEM BOs, GEM_SET_TILING RADEON_TILING_SURFACE, mmap+fault each -> all 8 surface regs busy; thread A faults a 9th SURFACE BO (steal path), thread B creates+faults+closes SURFACE BOs (destroy -> clear) -> A reads reg->bo after Bs clear: NULL deref -> panic; or Bs kfree lands between As read (:638) and use (:641/:642) -> UAF read + fixed-value 4-byte write at offsetof(radeon_bo, surface_reg) in freed slab -> groomable toward control (not developed: no HW).", "evidence": "VERDICT.md (interleaving analysis), run.log (open ENOENT + no radeonkms.ko + virtio VGA pciconf), build.log (BUILD_OK), fix.diff (device-lock fix)", "kernel_refs": [ "sys/dev/drm/radeon/radeon_object.c:601", "sys/dev/drm/radeon/radeon_object.c:609", "sys/dev/drm/radeon/radeon_object.c:621-630", "sys/dev/drm/radeon/radeon_object.c:637-643", "sys/dev/drm/radeon/radeon_object.c:656-669", "sys/dev/drm/radeon/radeon_object.c:69-86", "sys/dev/drm/radeon/radeon_object.c:793-812", "sys/dev/drm/radeon/radeon_gem.c:503-519" ], "poc_changes": "Self-authored trigger (no seed existed): self-contained uapi replicas, two pthread racers, render-node-first open; added missing errno.h after first guest compile.", "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": 45, "guest_dirty": 0, "build_cmd": "cc -O2 -Wall -pthread -o df4584_trigger df4584_trigger.c", "run_cmd": "./df4584_trigger /dev/dri/renderD128; ./df4584_trigger /dev/dri/card0", "code_hash": "6cd709138cac6be20add86e4f0ffbed66a77f6053508146e1fac7997e70025bf", "notes": "Guest lacks radeon HW and radeonkms.ko entirely (env.txt); per the strict poc-status enum this is recorded as inconclusive (attempted, environment-absent) instead of the usual not_testable label. has_moved==1 callers of radeon_bo_check_tiling do not exist in this tree; get_surface_reg is reached only via radeon_bo_kmap (:289) and fault_reserve_notify (:804).", "recommended_fix": "Add device-level rdev->surface_regs_lock held across radeon_bo_get_surface_reg()s registry walk/steal and radeon_bo_clear_surface_reg()s body (fix.diff); victim liveness follows because destroy calls clear before kfree.", "fix_status": "not_applicable", "fix_baseline_reproduced": 0, "fix_patched_reproduced": 0, "fix_verdict": "Fix validation impossible on this guest: baseline itself is not reproducible (no radeon hardware/driver), so no patched-kernel comparison can be made. fix.diff is inspection-validated only.", "fix_evidence": "fix.diff (git-apply-able vs read-only sys/ tree; never applied); see VERDICT.md Recommended fix" } |