⬢ DragonFlyBSD Kernel Audit
DF-4910 / verdict.json
← back to finding ↓ download raw
{
  "finding_id": "DF-4910",
  "status": "untested",
  "reproduced": 0,
  "impact": "none",
  "confidence": "likely",
  "verdict": "Code-proven race: dm_dp_destroy_mst_connector (amdgpu_dm_mst_types.c:386-392) tears down aconnector->port/dc_sink from drm_dp_destroy_connector_work with no mode_config.mutex (drm_dp_mst_topology.c:3103-3116 deliberately lockless), while dm_dp_mst_get_modes (amdgpu_dm_mst_types.c:232-244) check-then-sets the same dc_sink under mode_config.mutex from the unpriv DRM_IOCTL_MODE_GETCONNECTOR path (drm_connector.c:1759-1763). The losing interleaving leaks the dc_sink into link->remote_sinks[] with dc_sink->priv dangling at the kfreed aconnector, permanently consumes one of MAX_SINKS_PER_LINK(4) slots, and after 4 hits makes the known DF-1966 NULL-deref panic deterministic; secondary UAF-read windows exist in amdgpu_dm_update_freesync_caps (amdgpu_dm.c:5855) and amdgpu_dm_connector_mode_valid (amdgpu_dm.c:3324-3331). NOT runtime-tested: the audit guest has no AMD display device (only the QEMU virtual VGA), amdgpu.ko loads but binds nothing, /dev/dri does not exist \u2014 recorded in run.log/env.txt. Fix (mode_config.mutex around the teardown) authored in fix.diff but not buildable-into a testable configuration on this guest because the buggy path requires AMD DM hardware with a DP MST topology.",
  "exploit_chain": "unpriv GETCONNECTOR hammer x MST topology flap (physical replug / hostile DP dongle) -> unlocked destroy skips dc_sink release while probe installs it -> dc_sink leaked with ->priv dangling at freed aconnector; after 4 races every later dc_link_add_remote_sink fails (dc.c:1752) -> next hotplug NULL-deref panic in dm_dp_mst_get_modes (DF-1966 amplifier); UAF-read windows at amdgpu_dm.c:5855/3324 are read-only primitives, no write primitive identified.",
  "evidence": "VERDICT.md (full line-by-line trace + killed candidates), run.log (guest: NO_AMD_PCI_DEVICE / NO_DEV_DRI, trigger builds and runs, path unreachable), env.txt (uname 6.5-DEVELOPMENT, amdgpu.ko present but unbound), fix.diff (mode_config.mutex serialization).",
  "kernel_refs": [
    "sys/dev/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c:232",
    "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:244",
    "sys/dev/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c:386",
    "sys/dev/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c:387",
    "sys/dev/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c:389",
    "sys/dev/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c:390",
    "sys/dev/drm/drm_dp_mst_topology.c:3093",
    "sys/dev/drm/drm_dp_mst_topology.c:3116",
    "sys/dev/drm/drm_probe_helper.c:400",
    "sys/dev/drm/drm_connector.c:1759",
    "sys/dev/drm/amd/display/dc/core/dc.c:1752",
    "sys/dev/drm/amd/display/dc/core/dc.c:1757",
    "sys/dev/drm/amd/display/dc/core/dc.c:1834",
    "sys/dev/drm/amd/display/dc/dc.h:45",
    "sys/dev/drm/amd/display/amdgpu_dm/amdgpu_dm.c:3324",
    "sys/dev/drm/amd/display/amdgpu_dm/amdgpu_dm.c:5845",
    "sys/dev/drm/amd/display/amdgpu_dm/amdgpu_dm.c:5855"
  ],
  "poc_changes": "No seeded PoC existed; wrote mst_race_trigger.c from scratch (DRM ioctl constants embedded since guest lacks installed drm headers).",
  "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": 240,
  "guest_dirty": 0,
  "build_cmd": "cc -O2 -Wall -o mst_race_trigger mst_race_trigger.c",
  "run_cmd": "./run.sh  (build + pciconf probe + ./mst_race_trigger /dev/dri/card0)",
  "code_hash": "e1e328bc79557afa2716b4288d052327b0e0e5574e04616e38504070d56f65d5",
  "notes": "Guest was probed with kldload amdgpu (loads, binds nothing \u2014 only QEMU virtual VGA chip 0x1234 present); the follow-up kldunload wedged the linker lock so the guest was reset with-src and is clean. amdgpu.ko exists at /boot/kernel/amdgpu.ko. Distinct root cause from known DF-4748 (helpers read stale priv of a stream-owned sink) and DF-1966 (unchecked add_remote_sink NULL) \u2014 this finding is the unlocked teardown racing the probe, creating the leak/dangling window in the first place.",
  "recommended_fix": "Hold dev->mode_config.mutex across the aconnector->port/dc_sink teardown in dm_dp_destroy_mst_connector (see fix.diff).",
  "fix_status": "not_testable",
  "fix_baseline_reproduced": 0,
  "fix_patched_reproduced": 0,
  "fix_verdict": "Fix cannot be validated on this guest: baseline cannot run (no AMD display hardware, no /dev/dri). fix.diff is a minimal, deadlock-audited serialization patch (the cbs only caller invokes it lockless).",
  "fix_evidence": "fix.diff; call-site proof drm_dp_mst_topology.c:3103-3116 holds no locks when calling cbs->destroy_connector."
}