⬢ DragonFlyBSD Kernel Audit
DF-5223 / verdict.json
← back to finding ↓ download raw
{
  "finding_id": "DF-5223",
  "status": "not_reproduced",
  "reproduced": false,
  "impact": "none",
  "confidence": "likely",
  "verdict": "rv770_copy_dma reserves num_loops*5+8 dwords on the R600 DMA ring (rv770_dma.c:59) but radeon_sync_rings() emits up to two 3-dword semaphore waits on that same ring (GFX+UVD cross-ring fences; radeon_sync.c:186-194 documents the caller-side allocation contract) plus a 5-dword fence emit (r600_dma.c:292-297) - with two pending cross-ring fences every num_loops==8,11,14 (mod 16) overruns the reserved window by exactly 16 dwords after commit padding, and radeon_ring_write() (radeon.h:2710-2716) writes past it anyway (masked in-ring, no host heap OOB), overwriting un-executed GPU commands when the ring is tight. Runtime reproduction is impossible on the audit guest: pciconf shows only QEMU std-VGA (chip 0x11111234) and kldload radeondrm fails (module absent), so the bug is hardware-gated to real HD-4000 GPUs; the deficit arithmetic was proven instead by a guest-run 1:1 simulator of radeon_ring_alloc/ring_write/ring_commit semantics (12 overflowing combinations, each exactly 16 dwords). User-reachable on real hardware: rv770_copy_dma is the primary TTM .copy callback (radeon_asic.c:1253-1256), and S=2 is constructible by an unprivileged render-node client because read-only relocations add shared per-ring fences (radeon_cs.c:182, ttm_execbuf_util.c:203-206).",
  "exploit_chain": "On real RV770 hardware: (1) unprivileged client makes a bo read by a GFX CS and a UVD decode job (two unsignaled per-ring fences on the resv); (2) forces VRAM eviction so TTM moves the bo via rv770_copy_dma with num_loops==8/11/14 mod 16 while keeping the DMA ring nearly full; (3) radeon_sync_rings emits the 2nd semaphore wait past the reservation, wptr overtakes rptr by up to 15 dwords, overwriting un-executed committed commands with fence/nop dwords; (4) GPU re-parses mid-packet garbage (attacker-influenced GPU-address dwords as DMA packet headers) -> cross-context GPU memory corruption or GPU lockup (DoS). No host-memory corruption; no kernel privilege escalation path - impact ceiling is GPU-visible memory corruption/DoS.",
  "evidence": "run.log: 12 DEFICIT: S=2 ... overflow=16 dwords rows from the guest-run simulator; env.txt: pciconf std-VGA-only + kldload: cant load radeondrm; fix.diff passes git apply --check; VERDICT.md kill list separates this from DF-2214/DF-5131/DF-1943.",
  "kernel_refs": [
    "sys/dev/drm/radeon/rv770_dma.c:59",
    "sys/dev/drm/radeon/rv770_dma.c:66-67",
    "sys/dev/drm/radeon/radeon_sync.c:186-194",
    "sys/dev/drm/radeon/r600_dma.c:319-321",
    "sys/dev/drm/radeon/r600_dma.c:292-297",
    "sys/dev/drm/radeon/radeon.h:2708-2716",
    "sys/dev/drm/radeon/radeon_ring.c:106-131",
    "sys/dev/drm/radeon/radeon_ring.c:173",
    "sys/dev/drm/radeon/r600.c:2798",
    "sys/dev/drm/radeon/radeon_ib.c:135",
    "sys/dev/drm/radeon/radeon_asic.c:1253-1256",
    "sys/dev/drm/radeon/radeon_cs.c:182",
    "sys/dev/drm/ttm/ttm_execbuf_util.c:203-206"
  ],
  "poc_changes": "No seed existed; authored sim_accounting.c from scratch as a 1:1 arithmetic model of the kernel ring-accounting code (every mirrored line cited in the header).",
  "attempts": 3,
  "guest_uname": "DragonFly dfbsd 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": 12,
  "guest_dirty": 0,
  "build_cmd": "sh build.sh  (cc -O2 -Wall -o /tmp/sim_accounting sim_accounting.c)",
  "run_cmd": "sh run.sh",
  "code_hash": "acd8b0c1bf4adcbf6e843dc461e3ce354366180117882aa23fefb6675c81fcef",
  "notes": "attempts=3: (1) guest hardware probe + kldload -> path absent (env.txt); (2) simulator build+run on guest -> deficit arithmetic reproduced deterministically; (3) git apply --check of fix.diff against pristine sys/ tree -> OK. not_reproduced refers to the kernel runtime behavior (hardware-gated; same classification basis as DF-5170 QEMU std-VGA proven). Family context: r600 twin filed as DF-1943 (Low, admin-gated there); evergreen (+11) and si (+11) share the latent shortfall for S>=2 (flagged unfiled in DF-5131 poc notes); this is the user-reachable rv770 instance. Guest left clean (only /root/poc/DF-5223 scratch + /tmp binary).",
  "recommended_fix": "Reserve the semaphore-wait space radeon_sync_rings may emit on the waiter ring: radeon_ring_lock(rdev, ring, num_loops * 5 + 8 + RADEON_NUM_SYNCS * 3);",
  "fix_status": "not_testable",
  "fix_baseline_reproduced": false,
  "fix_patched_reproduced": false,
  "fix_verdict": "fix.diff (git apply --check clean) mirrors the IB-path reservation accounting (radeon_ib.c:135 reserves 64 + RADEON_NUM_SYNCS*8 for the same documented contract) and the DF-1943 family fix. Baseline/patched kernel behavior cannot be validated on this guest: the radeon driver is absent from the guest kernel and no R700 GPU exists (env.txt), so no observable behavior exists to compare. The simulator run.log doubles as the arithmetic baseline: under the fixs reservation (num_loops*5+20 >= worst-case consumers num_loops*5+11) no (S,L) combination can exceed the reserved window.",
  "fix_evidence": "fix.diff; git apply --check output (GIT_APPLY_CHECK_OK) in session log; run.log arithmetic (max consumers = num_loops*5+6+5 = num_loops*5+11 <= num_loops*5+8+12 under the fix)."
}