⬢ DragonFlyBSD Kernel Audit
DF-4974 / verdict.json
← back to finding ↓ download raw
{
  "finding_id": "DF-4974",
  "status": "not_reproduced",
  "reproduced": 0,
  "impact": "none",
  "confidence": "likely",
  "verdict": "Statically proven missing validation: radeon_crtc_cursor_set2 (sys/dev/drm/radeon/radeon_cursor.c:301-316) pins an arbitrary-size user GEM BO as the cursor with only a width/height<=max check (radeon_cursor.c:295-299) and never requires the BO to cover the fetch window the hardware is programmed for (CUR_SIZE at radeon_cursor.c:218-224 -> w*h*4 bytes, up to 64*64*4=16KiB; pre-AVIVO adds yorigin*256 at radeon_cursor.c:241-243). radeon uses the legacy cursor path (radeon_display.c:661-662, drm_crtc_init with no cursor plane -> drm_plane.c:935-938 dispatch) which performs no core-side size validation, unlike the universal-plane path that wraps the handle in a size-checked framebuffer. On real radeon HW a DRM master (unpriv seat owner; ioctl is DRM_MASTER-gated, drm_ioctl.c:659/680) can supply a 1-page VRAM BO with width=height=64 and the display engine scans 12KiB past the BO end, painting adjacent VRAM contents as cursor pixels (info exposure of other VRAM, e.g. other users window contents). Not reproducible on this guest: QEMU has no GPU \u2014 open(/dev/dri/card0) -> ENOENT, no drm/radeon modules, hw.drm absent (run.log). Not a memory-corruption primitive (display-engine VRAM read only), so no uid=0 chain exists.",
  "exploit_chain": "",
  "evidence": "run.log (ENOENT + module/sysctl absence on guest); VERDICT.md (full static trace with path:line and killed candidates); cursor_oob.c (trigger: tiny VRAM GEM bo + CURSOR2 64x64)",
  "kernel_refs": [
    "sys/dev/drm/radeon/radeon_cursor.c:295",
    "sys/dev/drm/radeon/radeon_cursor.c:301",
    "sys/dev/drm/radeon/radeon_cursor.c:314",
    "sys/dev/drm/radeon/radeon_cursor.c:218",
    "sys/dev/drm/radeon/radeon_cursor.c:224",
    "sys/dev/drm/radeon/radeon_cursor.c:241",
    "sys/dev/drm/radeon/radeon_display.c:661",
    "sys/dev/drm/radeon/radeon_display.c:679",
    "sys/dev/drm/drm_plane.c:935",
    "sys/dev/drm/drm_ioctl.c:659"
  ],
  "poc_changes": "Seed sketch rewrote as a self-contained probe with locally-defined drm_mode_cursor2 struct and ioctl number taken from sys/dev/drm/include/uapi/drm/drm.h (0xBB, d); added empirical device-absence documentation (open ENOENT, kldstat, sysctl hw.drm).",
  "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": "./build.sh (cc -Wall -Wextra -o cursor_oob cursor_oob.c)",
  "run_cmd": "./run.sh (also run on guest via vm.sh run_user)",
  "code_hash": "3d89a8a5eb8fbcb530e057587c57acd074675d15c383d5a99abd8fd92f76c17e",
  "notes": "Impact ceiling on real HW is info-exposure of adjacent VRAM via the physical screen (multiseat/second-window scenario); not kernel memory corruption, no escalation chain. Guest has no GPU at all -> device-absent untestability documented empirically. Upstream-shaped code shares the missing check.",
  "recommended_fix": "Reject cursor BOs smaller than max_cursor_width*max_cursor_height*4 in radeon_crtc_cursor_set2 before pinning (see fix.diff).",
  "fix_status": "not_testable",
  "fix_baseline_reproduced": 0,
  "fix_patched_reproduced": 0,
  "fix_verdict": "fix.diff adds the minimum-size rejection after the single GEM lookup (refcount-safe); cannot be kernel-built/validated on this guest because the radeon device does not exist here and the bug cannot be triggered at all (baseline not reproducible).",
  "fix_evidence": "findings/poc/DF-4974/fix.diff (authored post-verification, never applied to sys/)"
}