# DF-4430 VERDICT — radeon PRIME import: populate ignores ttm->sg (wrong
# pages bound) and unpopulate leaks the pool pages

## Bottom line

**Source-verified, hardware-gated (audit guest has no AMD GPU / second DRM
device).** The DragonFlyBSD radeon port compiles out the SG ("slave")
handling in `radeon_ttm_tt_populate` (`#ifdef DUMBBELL_WIP`,
`sys/dev/drm/radeon/radeon_ttm.c:753-764`) while leaving the slave
early-return live in `radeon_ttm_tt_unpopulate`
(`radeon_ttm.c:808,818-819`). Net effect for any PRIME-imported BO
(`ttm_bo_type_sg`, created at `sys/dev/drm/radeon/radeon_prime.c:73-93`):

1. `ttm_bo_handle_move_mem` → `ttm_tt_bind` (`sys/dev/drm/ttm/ttm_bo.c:331-347`)
   → `ttm_tt_populate` (`ttm_tt.c:468-482`) → `radeon_ttm_tt_populate`
   falls through to `ttm_pool_populate` (`radeon_ttm.c:779`) which allocates
   **fresh anonymous pages** and ignores `ttm->sg`
   (`sys/dev/drm/ttm/ttm_page_alloc.c:890-933`, no SG check) — the GART then
   binds those pages (`radeon_ttm.c:659`). The GPU view of an imported
   dma-buf is not the exporter's memory: silent data divergence (importer
   reads zeros, GPU writes never reach the real buffer).
2. On unpopulate — including BO destroy (`ttm_tt_destroy` →
   `ttm_tt_unpopulate`, `ttm_tt.c:215-229,500-508`) — the live slave
   early-return skips `ttm_pool_unpopulate`, so the wrongly-allocated pages
   and their `ttm_mem_global` accounting (`ttm_page_alloc.c:913-921`) leak
   permanently. Repeat import/destroy cycles = unbounded wired-kernel-memory
   growth + TTM accounting exhaustion (GPU alloc failures) = local DoS.

In-tree contrast: `sys/dev/drm/amd/amdgpu/amdgpu_ttm.c:1268-1330` implements
the slave path live (no ifdef), proving the port simply left radeon's half
unfinished rather than a deliberate design choice.

## Reproduction status

`untested` — guest has one bochs VGA stub, no `/dev/dri` (`env.txt`); the
trigger additionally needs **two** DRM devices with radeon as importer.
Sibling precedent: DF-4018/4019 (source-verified, untested).

## Reachability & impact ceiling

Requires cross-driver dma-buf import onto radeon (i915+radeon,
amdgpu+radeon, radeon+radeon) — a rare multi-GPU configuration, hence
**Low**. Same-driver self-import short-circuits
(`sys/dev/drm/drm_prime.c:709-745` returns the original object) and is
unaffected. Within that config any user with both card nodes open (video
group) can drive the leak loop unprivileged.

## Fix validation

`fix.diff` un-ifdefs the slave branch of `radeon_ttm_tt_populate` (fills
`ttm->pages`/`dma_address` from `ttm->sg` via `drm_prime_sg_to_page_addr_arrays`,
matching amdgpu) — once populate no longer uses the pool for slave tts, the
existing slave early-return in unpopulate becomes correct (slave pages belong
to the sg table, not the pool). Cannot be built/loaded on this guest (no
radeon hardware) → `fix_status: not_testable`.

## Kernel refs

- sys/dev/drm/radeon/radeon_ttm.c:753-764 (dead slave populate branch)
- sys/dev/drm/radeon/radeon_ttm.c:779 (pool populate for SG tt)
- sys/dev/drm/radeon/radeon_ttm.c:808,818-819 (live slave unpopulate return)
- sys/dev/drm/radeon/radeon_prime.c:73-93 (sg import entry)
- sys/dev/drm/ttm/ttm_tt.c:71-72,243 (SG flag + ttm->sg wiring)
- sys/dev/drm/ttm/ttm_tt.c:215-229,500-508 (destroy→unpopulate path)
- sys/dev/drm/ttm/ttm_page_alloc.c:890-933 (pool allocates for SG tt)
- sys/dev/drm/amd/amdgpu/amdgpu_ttm.c:1268-1330 (correct sibling impl)

## Fix status (batch validation)
Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest `make -j6 nativekernel KERNCONF=X86_64_GENERIC` completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.
