DF-3882 / verdict.json
{ "finding_id": "DF-3882", "status": "not_reproduced", "reproduced": 0, "impact": "none", "confidence": "likely", "verdict": "Source-certain race, kernel path hardware-gated: drm_dp_mst_handle_down_rep captures txmsg=mstb->tx_slots[slot] under qlock (drm_dp_mst_topology.c:2337), drops the lock (:2339), then writes ~700B of hub-controlled reply data via drm_dp_sideband_parse_reply (:2353) and txmsg->state (:2362) plus reads mstb after its ref was dropped (:2359/:2363). If the senders 4*HZ wait in drm_dp_mst_wait_tx_reply expires inside that window it clears the slot (:831) and the caller kfrees txmsg (:1649) -- both handler writes then land in freed memory. The hub times the reply release at the deadline and can retry indefinitely. The pthread/mmap interleave model demonstrates the use-after-free deterministically (SIGSEGV into munmapd txmsg, 3/3 runs, run.log). Guest has no GPU (/dev/dri absent, env.txt), so the kernel path itself is not_reproduced here.", "exploit_chain": "malicious hub parks DOWN_REP_MSG_RDY until the 4s tx timeout boundary -> sender kfree(txmsg) races the unlocked parse window -> up to 700 bytes of hub reply data written at fixed offset into freed ~1KB kmalloc zone + 4-byte state write; the hubs own hotplug churn grooms the same zone -> classic groom-and-overwrite of whatever reoccupies the chunk.", "evidence": "run.log (3/3 SIGSEGV UAF demonstrations with interleaving printout), VERDICT.md (line-numbered both-sides trace), fix.diff (validated: applies cleanly to pristine sys/dev/drm/drm_dp_mst_topology.c)", "kernel_refs": [ "sys/dev/drm/drm_dp_mst_topology.c:2337", "sys/dev/drm/drm_dp_mst_topology.c:2339", "sys/dev/drm/drm_dp_mst_topology.c:2353", "sys/dev/drm/drm_dp_mst_topology.c:2362", "sys/dev/drm/drm_dp_mst_topology.c:831", "sys/dev/drm/drm_dp_mst_topology.c:1649" ], "poc_changes": "no seed; interleave model authored from scratch (munmap models kfree so the UAF write faults visibly)", "attempts": 3, "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT #0: Thu Jul 2 06:02:54 UTC 2026 x86_64", "runtime_sec": 30, "guest_dirty": 0, "build_cmd": "cc -O2 -Wall -pthread -o txmsg_uaf_race_model txmsg_uaf_race_model.c", "run_cmd": "./txmsg_uaf_race_model (x3)", "code_hash": "abe159f7b5e49fe2955277f0f288fb2314d01a12d4b84f4ca384cb9cf7f324a9", "notes": "Race window is microseconds but attacker-retriable without limit; multi-CPU makes it schedulable. Distinct from DF-3699 (lkpi queue_work) -- this is a kref/lifetime gap, not workqueue plumbing.", "recommended_fix": "Parse the reply into a stack-local drm_dp_sideband_msg_reply_body and only publish it into txmsg under qlock after re-validating mstb->tx_slots[slot]==txmsg; hold the mstb ref until after the last slot access", "fix_status": "not_testable", "fix_baseline_reproduced": 0, "fix_patched_reproduced": 0, "fix_verdict": "fix.diff restructures the handoff (local reply + slot re-validation under qlock + mstb put moved after last use); applies cleanly via patch --dry-run to the pristine tree; kernel-side validation impossible on this guest (no DP hardware), so fix_status=not_testable.", "fix_evidence": "findings/poc/DF-3882/fix.diff" } |