⬢ DragonFlyBSD Kernel Audit
DF-4748 / verdict.json
← back to finding ↓ download raw
{
  "finding_id": "DF-4748",
  "status": "reproduced",
  "verdict": "Reproduced with the proven verbatim-code rig (DF-4165/4247/4631 evidence class) on the DragonFly guest: 66 machine-extracted code blocks covering the entire cited lifetime chain (amdgpu_dm_helpers.c payload helpers, amdgpu_dm_mst_types.c connector lifecycle, dc_sink/dc remote-sink/dc_stream refcount machinery, dc_link.c commit path, drm_dp payload machinery, drm connector refcount plumbing) are byte-identical to /usr/src -- re-verified ON the guest by check_verbatim.sh -- and compiled there. Driving the exact production kref sequence (plug -> get_modes sets dc_sink->priv=child at mst_types.c:243; enable -> stream takes its own sink ref; unplug -> dm_dp_destroy_mst_connector drops list+connector refs (sink refcount exactly 1, stream-held) and kfree()s the connector; session reacts -> core_link_disable_stream -> deallocate_mst_payload -> helper reads stream->sink->priv) yields, deterministically (3/3 runs): guard mode SIGSEGV INSIDE the freed amdgpu_dm_connector at the mst_port read (amdgpu_dm_helpers.c:199); poison mode wild-pointer deref; groom mode CONTROLLED WRITES -- verbatim drm_dp_mst_reset_vcpi_slots/deallocate_vcpi zeroed a victim object at the attacker-chosen address (disable path) and verbatim drm_dp_init_vcpi wrote vcpi{1,551,65536,1} plus assign_payload_id mask/pointer writes (enable/re-plug path). stale/zero modes complete without faulting, honestly modeling the bugs real-world latency. fix.diff (NULL dc_sink->priv in dm_dp_destroy_mst_connector before the refs drop) A/B-validated in the same rig: fixed build clean on every scenario including groom (victim byte-identical), and compile-validated by a real amdgpu.ko module build (bsd.kmod.mk) on the guest with the patch applied. In-kernel manifestation on real ASIC remains hardware-gated (guest has Bochs vga only, no /dev/dri); the rig exercises the production code itself byte-identical, with slab state modeled at the boundaries.",
  "reproduced": true,
  "impact": "memcorrupt",
  "confidence": "certain",
  "exploit_chain": "malicious/flapping MST dock (or physical unplug) with a display active on an MST child -> topology worker frees the child amdgpu_dm_connector while the enabled dc_stream_state keeps the dc_sink alive with ->priv dangling -> display session disables the CRTC on the old stream -> deallocate_mst_payload -> dm_helpers_dp_mst_write_payload_allocation_table reads the freed connector (:197/:199) -> with slab reuse, the attacker-authored mst_port/port steer the verbatim drm_dp payload machinery (reset_vcpi_slots / deallocate_vcpi / init_vcpi / assign_payload_id) into attacker-chosen kernel memory: UAF-driven controlled writes (proven at rig-level with full pointer control); re-plug/enable path reaches the same writes via allocate_mst_payload",
  "evidence": "findings/poc/DF-4748/rig/run.log (decisive: RIG FAULT: addr=0x8004e8048 INSIDE FREED allocation dm_dp_add_mst_connector -- USE-AFTER-FREE CONFIRMED and both CONTROLLED-WRITE PROVEN blocks); run.2.log/run.3.log (determinism 3/3); rig/build.log (66 verbatim blocks byte-identical to /usr/src + fix.diff round-trip on the guest); fix_run.log (fixed build: FIX_CLEAN/FIX_BLOCKED_WRITE on every scenario); fix_build.log (real amdgpu.ko module build with fix.diff, then byte-exact revert); VERDICT.md (full matrix + path:line refs); env.txt (no GPU PCI device)",
  "kernel_refs": [
    "sys/dev/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c:197",
    "sys/dev/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c:199",
    "sys/dev/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c:289",
    "sys/dev/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c:317",
    "sys/dev/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c:243",
    "sys/dev/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c:182",
    "sys/dev/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c:375",
    "sys/dev/drm/amd/display/dc/core/dc_sink.c:80",
    "sys/dev/drm/amd/display/dc/core/dc.c:1823",
    "sys/dev/drm/amd/display/dc/core/dc_stream.c:66",
    "sys/dev/drm/amd/display/dc/core/dc_stream.c:112",
    "sys/dev/drm/amd/display/dc/core/dc_link.c:2374",
    "sys/dev/drm/amd/display/dc/core/dc_link.c:2454",
    "sys/dev/drm/amd/display/dc/core/dc_link.c:2630",
    "sys/dev/drm/drm_dp_mst_topology.c:2765",
    "sys/dev/drm/drm_dp_mst_topology.c:2593"
  ],
  "poc_changes": "Authored the full verbatim rig (the seed had only a static chain proof): gen_rig.sh machine-extracts 66 byte-identical blocks (helpers 137-172/182-266/281-305/307-338; mst_types 182-197/210-258/283-308/310-373/375-398; dc_sink 34-104; dc.c 1750-1847; dc_stream 38-157; dc_link 2322-2372/2374-2452/2454-2530/2630-2642; drm_dp payload machinery; drm connector refcount plumbing; kref/refcount/fixpt inline bodies); check_verbatim.sh re-proves byte-identity on the guest against /usr/src and the fix.diff round-trip; mst_rig.c drives plug/enable/unplug/use with 6 free-mode slab models (guard/stale/zero/poison/groom). Infrastructure shims (allocator, locks, logging, atomics, DPCD transport, dm_helpers_parse_edid_caps, dc_link PBN math, minimized carrier structs) carry no defect logic. Fix ref correction: MST-child free site is dm_dp_mst_connector_destroy (mst_types.c:193), not amdgpu_dm.c:3187 (that is the non-MST destroy); conclusion unchanged.",
  "attempts": 6,
  "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": 420,
  "guest_dirty": 0,
  "build_cmd": "sh rig/build.sh /usr/src   (on guest: regenerate slices from /usr/src + check_verbatim 66 blocks + make rig_base rig_fixed)",
  "run_cmd": "sh rig/run.sh   (8 scenarios x baseline/fixed; decisive: ./rig_base disable-guard; ./rig_base disable-groom; ./rig_base enable-groom)",
  "code_hash": "475037b1745a3df69b736d425883e49d83beb1338fd1e7fc73e2a50cbdfc7248",
  "fix_diff": "findings/poc/DF-4748/fix.diff",
  "fix_status": "fixed",
  "fix_baseline_reproduced": 1,
  "fix_patched_reproduced": 0,
  "fix_verdict": "A/B in the same verbatim rig: baseline exhibits UAF fault (guard/poison) and attacker-steered controlled writes (groom, both paths) on every run; fixed build (byte-identical pristine+fix.diff slice, checker-proven) is clean on all 8 scenarios -- helpers early-return at amdgpu_dm_helpers.c:199 on the NULLed priv, and in groom mode the victim object stays byte-identical (FIX_BLOCKED_WRITE). Additionally compile-validated in a real amdgpu.ko module build (bsd.kmod.mk) on the guest with fix.diff applied at fuzz=0; /usr/src reverted byte-exact afterwards (sha256 match).",
  "fix_evidence": "findings/poc/DF-4748/fix_run.log (fixed matrix), fix_build.log (module build), rig/check_verbatim.sh pass-2 (fixed slice == pristine + fix.diff, bidirectional)",
  "fix_kernel_uname": "DragonFly dfbsd 6.5-DEVELOPMENT #0: Thu Jul  2 06:02:54 UTC 2026 (same kernel; amdgpu.ko module rebuilt with fix, not loaded -- no AMD ASIC on guest)",
  "notes": "Hardware-gated for in-kernel manifestation (Bochs vga only, /dev/dri absent, amdgpu not loaded) -- NOT a false positive: the production code path itself was executed byte-identical on the guest and the UAF/controlled-write primitives are deterministic in it. Latency honestly shown: stale/zero slab models complete without faulting (docks mgr still valid / port pre-nulled), so real-world crashes depend on reuse -- exactly what groom models and proves. Original static chain proof (verify.sh, static_proof.log 8/8) retained. Sibling findings honored: DF-1837 (local-EDID memmove, not re-tested here), DF-4631 (radeon twin, same pattern).",
  "recommended_fix": "NULL dc_sink->priv in dm_dp_destroy_mst_connector before dropping the connector-side sink reference (one-liner, see fix.diff); helpers already treat NULL priv gracefully (early return at amdgpu_dm_helpers.c:199)."
}