{
  "finding_id": "DF-5218",
  "status": "inconclusive",
  "reproduced": false,
  "impact": "none",
  "confidence": "likely",
  "verdict": "Source-level proven, runtime hardware-gated. The dc_sink lifecycle file itself (dc_sink.c:34-104) is correct; its retain/release contract (dc_sink.c:68-83) is violated by two DM forced-connector paths: (1) emulated_link_detect (amdgpu_dm.c:741-826) retains prev_sink at :754 with no release on any exit and overwrites link->local_sink at :810 without dropping the link ref \u2014 one permanently stranded struct dc_sink (~1KB) per forced-detect cycle (S3 resume/HPD of a headless forced connector): unbounded kernel heap leak; (2) update_connector_after_detect branch A releases a BORROWED aconnector->dc_sink at amdgpu_dm.c:993 (never retained on store at :995/:1044/:3278) \u2014 on a still-live local_sink this drops the links only ref (premature free with link->local_sink dangling), and on an EDID-change cycle the sink is already freed by dc_link_detect (dc_link.c:902) so :993 is dc_sink_release on freed memory: kref_put UAF write and dc_sink_free double-free of a groomable ~1KB slab block. Preconditions: boot-time connector force (video=/drm.edid_firmware=, drm_connector.c:137-140) + AMD DC GPU (DCE10=Tonga/Fiji, dc_resource.c:71-76) + (UAF) physical EDID change. The QEMU guest has only the QEMU virtual VGA (vendor 0x1234); kldload amdgpu binds zero devices and dc_sink_create never executes (env.txt), so no runtime reproduction was possible; an executable model of the exact ledger (ledger_model.c) was built and run on the guest (cc 8.3) and reproduces both violations (leak: 9 live sinks after 8 cycles; UAF: 1 release-on-freed + 1 double-free attempt) with the fixed pairing returning to 1 live sink / 0 UAF (run.log).",
  "exploit_chain": "With an AMD DC GPU present and a boot-forced connector: (a) leak \u2014 leave forced connector headless, suspend/resume repeatedly; each emulated_link_detect strands one dc_sink (heap exhaustion DoS). (b) UAF \u2014 attach display, cause two detect cycles with differing EDIDs (monitor/KVM swap); dc_link_detect frees the old sink at dc_link.c:902, then amdgpu_dm.c:993 kref_puts the freed object; if the slab block was reused in the window between the free and the release, the decrement corrupts the victim object and a resulting 0-count triggers dc_sink_free (destruct + kfree) on it \u2014 arbitrary-free primitive on a ~1KB kmalloc zone, groomable toward slab corruption; on this stock guest no uid0 chain was attempted because the driver never initializes.",
  "evidence": "VERDICT.md (full line-cited ledger proof + reachability chain + hardware gate); run.log (guest: ledger_model bug-leak -> 9 live sinks; bug-uaf -> stale sink freed before the :993 release: YES, 1 UAF write, 1 double-free attempt; fixed -> 1 live sink, 0 UAF); env.txt (uname, pciconf chip=0x11111234 QEMU VGA, kldload amdgpu binding zero devices); fix.diff (pairing fix); ledger_model.c (code_hash below).",
  "kernel_refs": [
    "sys/dev/drm/amd/display/dc/core/dc_sink.c:68",
    "sys/dev/drm/amd/display/dc/core/dc_sink.c:85",
    "sys/dev/drm/amd/display/amdgpu_dm/amdgpu_dm.c:741",
    "sys/dev/drm/amd/display/amdgpu_dm/amdgpu_dm.c:754",
    "sys/dev/drm/amd/display/amdgpu_dm/amdgpu_dm.c:810",
    "sys/dev/drm/amd/display/amdgpu_dm/amdgpu_dm.c:993",
    "sys/dev/drm/amd/display/amdgpu_dm/amdgpu_dm.c:995",
    "sys/dev/drm/amd/display/amdgpu_dm/amdgpu_dm.c:1044",
    "sys/dev/drm/amd/display/dc/core/dc_link.c:492",
    "sys/dev/drm/amd/display/dc/core/dc_link.c:902"
  ],
  "poc_changes": "No seed PoC existed. Authored ledger_model.c: a userspace model of the verbatim dc_sink/dc_link/amdgpu_dm operation sequence with instrumented refcounts and freed-object detection (the kernel path itself is hardware-gated); plus build.sh/run.sh; first host build attempt failed (audit host has no cc) \u2014 final build/run performed on the DFly guest via base64 transport.",
  "attempts": 2,
  "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": 480,
  "guest_dirty": 0,
  "build_cmd": "cc -O2 -Wall -o /tmp/ledger_model /tmp/lm.c   (on DFly guest; equivalent: ./build.sh)",
  "run_cmd": "/tmp/ledger_model bug-leak; /tmp/ledger_model bug-uaf; /tmp/ledger_model fixed   (equivalent: ./run.sh)",
  "code_hash": "919a36b66e09452093d7481317ae2708f04b69e90f92dbcb7d1a20386287328a",
  "notes": "dc_sink.c file-local code is clean (kzalloc->construct->kref_init->release/destruct pairing correct; dc_container_id provably never allocated anywhere in the tree so its kfree in destruct is dead-safe; no clone path exists despite the dc.h:713 comment). Neighboring known findings excluded: DF-1837 (EDID extension-count overflow in dm_helpers_read_local_edid \u2014 the copy INTO dc_sink.dc_edid), DF-1966 (NULL dc_link_add_remote_sink deref), DF-3995 (ddc_pin UAF in dc_link.c). remote_sinks[] concurrent add/remove race killed: probe paths hold drm mode_config mutex. Guest probing dirtied the module state (kldload/kldunload amdgpu; unload hung) \u2014 guest was reset with-src and verified clean (amdgpu not loaded) before returning.",
  "recommended_fix": "emulated_link_detect: mirror dc_link_detects pairing (release the link ref via a link_disconnect_sink-style transfer, drop the temp ref on every exit); update_connector_after_detect branch A: stop releasing/retaining the borrowed aconnector->dc_sink (fix.diff).",
  "fix_status": "not_testable",
  "fix_baseline_reproduced": 0,
  "fix_patched_reproduced": 0,
  "fix_verdict": "fix.diff authored against read-only sys/ (never applied). Baseline cannot run on this guest (no AMD GPU \u2014 the amdgpu driver never initializes, so neither baseline nor patched kernel exercises the changed lines); a patched-kernel build would prove nothing about behavior. The corrected pairing semantics are demonstrated by ledger_model.cs fixed mode: exactly 1 live sink after 8 cycles and zero UAF hits, versus the buggy ledgers 9 live sinks and 1 double-free attempt.",
  "fix_evidence": "fix.diff; run.log (fixed-mode output); VERDICT.md section 6."
}