{
  "finding_id": "DF-2214",
  "status": "reproduced",
  "reproduced": 1,
  "impact": "corruption",
  "confidence": "certain",
  "verdict": "REPRODUCED via verbatim-code KLD rig on the QEMU guest. The rig compiles the UNMODIFIED production function rv770_copy_dma (sys/dev/drm/radeon/rv770_dma.c lines 42-94, sha256 c1b2d82ab166f9e04f7158a8d6fe0506d22ee1c6f05aca121ca0ac9b5a6b547c, extracted from the guest tree file which is byte-identical to the audited tree b33101a3...) kernel-resident, walks it with fabricated boundary structures carrying the real constants (RADEON_GPU_PAGE_SHIFT 12, DMA_PACKET per rv770d.h:663-669, R600_RING_TYPE_DMA_INDEX 3, ptr_mask/ring_write semantics per radeon.h:2708-2717, ring reservation per radeon_ring.c:107-127, real 64 KiB DMA ring per rv770.c:1968) and drives the exact input domain. Line 57 computes size_in_dw in 32-bit unsigned arithmetic: at num_gpu_pages=0x100000 (a 4 GiB TTM move) it wraps to 0, so num_loops=0, ZERO DMA_PACKET_COPY packets are emitted, and radeon_fence_emit still fires - the function returns a success fence having scheduled 0 of 4294967296 bytes (run_extract.log: scheduled_bytes=0 fence_emitted=1, VERDICT SILENT_TRUNCATION). At 0x100001 exactly one packet with n=1024 dwords (first_pkt=0x30000400) is emitted of a 4 GiB+4K request. Below the wrap (0xFFFFF) the same verbatim code schedules 100.0000 percent via 16385 packets chunked at 0xFFFF. On the real 64 KiB ring geometry the correct 4 GiB-4K copy is refused loudly (radeon_ring_lock -ENOMEM, production DRM_ERROR radeon: moving bo (-12)) while the wrapped 4 GiB copy locks 8 dwords, emits nothing and fences success - silent integrity corruption that ttm_bo_move_accel_cleanup publishes as a completed move. Three identical deterministic runs. Impact ceiling is GPU-side data integrity (video/GTT buffer contents); host kernel memory is untouched - canary zones around the ring buffer stayed INTACT in all 16 runs and the wrap can only shrink the reservation, never grow it - so there is no overwrite primitive and no route to uid=0 (privesc directive assessed and closed: not host memcorrupt).",
  "exploit_chain": "unprivileged GPU client on rv770-class (r7xx/evergreen DMA engine) hardware with large GTT -> TTM buffer-object move of >= 4 GiB -> radeon_move_blit computes num_pages = new_mem->num_pages (PAGE_SIZE==RADEON_GPU_PAGE_SIZE) and calls the asic copy callback rv770_copy_dma with num_gpu_pages >= 2^20 (radeon_ttm.c) -> (num_gpu_pages << 12) wraps u32 at rv770_dma.c:57 -> size_in_dw tiny or zero -> zero/few DMA packets at rv770_dma.c:69-81 but success fence at rv770_dma.c:83-93 -> ttm_bo_move_accel_cleanup marks the move complete -> destination buffer object keeps stale/uninitialized contents while userspace believes the copy succeeded: silent video-memory integrity corruption, potential info exposure of prior GPU memory contents in the destination, no host memory effect.",
  "evidence": "run_extract.log (identical in run2/run3_extract.log): BASELINE pages=0x00100000 -> num_loops=0 copy_packets=0 scheduled_bytes=0 (0.0000% of request) fence_emitted=1 VERDICT SILENT_TRUNCATION; BASELINE pages=0x000fffff -> 16385 packets 100.0000% FULL_COPY; BASELINE real64K 0x000fffff -> ERR_PTR(-12) loud vs 0x00100000 -> silent no-op with fence; FIXED pages=0x00100000 -> sum_chunk_dw=1073741824 scheduled=4294967296 100.0000% FULL_COPY; FIXED real64K >=2^20 -> ERR_PTR(-12) loud; all 16 cases canaries=INTACT count_dw_final=8 underruns=0. build.log shows the pinned provenance hashes and the clean gcc 8.3 kernel-context build of both variants into one KLD. kldload_panic_iteration.txt documents the toolchain-only iteration (loader panic before the module ever ran, kern.mk flags).",
  "kernel_refs": [
    "sys/dev/drm/radeon/rv770_dma.c:57",
    "sys/dev/drm/radeon/rv770_dma.c:58",
    "sys/dev/drm/radeon/rv770_dma.c:69-81",
    "sys/dev/drm/radeon/rv770_dma.c:83-93",
    "sys/dev/drm/radeon/radeon.h:645",
    "sys/dev/drm/radeon/radeon.h:2708-2717",
    "sys/dev/drm/radeon/radeon_ring.c:107-127",
    "sys/dev/drm/radeon/rv770d.h:663-669",
    "sys/dev/drm/radeon/rv770.c:1968",
    "sys/dev/drm/radeon/radeon_ttm.c:radeon_move_blit"
  ],
  "poc_changes": "No seed pack existed; rig authored from scratch in verify mode. Iterations: (1) guest kernel include environment replicated from sys/conf/kmod.mk semantics (merged machine namespace, machine_base/cpu_base/dragonfly symlinks, -D_KERNEL, -I/usr/src/include); (2) <string.h> include dropped because the fortified memset macro from systm.h collides with its prototype; (3) first kldload panicked the DFly link_elf_obj loader (lost base for relatab) because the object lacked sys/platform/pc64/conf/kern.mk flags - .eh_frame relocations the loader cannot base; fixed with -fno-asynchronous-unwind-tables -mcmodel=kernel -mno-red-zone -msoft-float -mno-mmx -mno-sse -fno-stack-protector; guest reset with-src (module had never executed; unrelated to the finding); (4) trigger values match the finding exactly (2^20-1 vs 2^20 plus 2^20+1, 2^20+2048, 0xC0000000 and a 128 MiB real-ring control).",
  "attempts": 3,
  "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": 90,
  "guest_dirty": 0,
  "build_cmd": "cd findings/poc/DF-2214 && ./build.sh  # provenance-checked extraction + cc -D_KERNEL -nostdinc kernel-context KLD build of baseline+fixed variants",
  "run_cmd": "cd findings/poc/DF-2214 && ./run.sh  # kldload df2214_rig.ko once; 16-case matrix to dmesg; kldunload",
  "code_hash": "d3feae34e18fd1a7968d550deecfe9a787e9cdfdcf8d06257fa76741a5937d6e",
  "notes": "GPU-gated integrity bug (CWE-190 wrap -> silent DMA under-copy): Medium as filed is right. Negative claims verified: no host kernel memory corruption (canaries INTACT across all runs; wrap is monotonically shrinking so no over-reservation/over-write primitive) and no uid=0 chain - the TEAM DIRECTIVE privesc assessment is closed explicitly. Consistency: fix.diff mirrors the verified evergreen fix shape (u64 size_in_dw + (u64) cast on the shift). Residual nit for a follow-up: with u64 size_in_dw the loop still narrows cur_size_in_dw before the 0xFFFF cap; harmless here because ring_lock refuses absurd reservations (observed lock_rc=-12 at attempted_ndw=251662093). Guest left up and clean: module unloaded after each run (kldstat shows kernel+usb only), /root/df2214 removed after artifact pull.",
  "recommended_fix": "Widen the size computation at rv770_dma.c:57 - declare u64 size_in_dw (u32 cur_size_in_dw) and compute ((u64)num_gpu_pages << RADEON_GPU_PAGE_SHIFT) / 4 - see fix.diff; same shape as the verified evergreen_dma.c fix.",
  "fix_status": "fixed",
  "fix_kernel_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",
  "fix_baseline_reproduced": 1,
  "fix_patched_reproduced": 0,
  "fix_validated": 1,
  "fix_diff": "findings/poc/DF-2214/fix.diff",
  "fix_verdict": "A/B validated in the same verbatim KLD: the FIXED variant (fix.diff applied to the same tree bytes, renamed) schedules exactly 100.0000 percent of the request for every num_gpu_pages >= 2^20 on the oversized test ring (e.g. 0x100000: sum_chunk_dw=1073741824 = full 4 GiB in 16385 packets) and fails LOUDLY with ERR_PTR(-12) on the real 64 KiB ring exactly like a genuine 4 GiB-4K copy, while every sub-2^20 case is bit-identical to the unpatched production code (same packet count, same first/last chunk sizes, same fence behavior, 128 MiB real-ring control FULL_COPY). Full kernel rebuild not meaningful for this target: the guest has no radeon hardware so the production path cannot execute there, and the rig compiles the identical production bytes kernel-resident (precedent DF-2290).",
  "fix_evidence": "run_extract.log FIXED section: pages=0x00100000 -> scheduled_bytes=4294967296 (100.0000% of request) VERDICT FULL_COPY; real64K FIXED 0x00100000 -> ERR_PTR(-12) loud failure; sub-2^20 FIXED cells byte-for-byte equal to BASELINE cells. build.log: Hunk #1 succeeded at 48 (fix.diff applied to pristine tree bytes). fix_build.log/fix_run.log carry the full transcripts."
}
