# DF-4871 — r600_copy_dma ignores radeon_sync_resv()/radeon_sync_rings() failures

File: `sys/dev/drm/radeon/r600_dma.c` (pass 2, GLM 5.3)
Severity: Low · CWE-754 (Improper Check for Unusual or Exceptional Conditions)

## What

`r600_copy_dma()` — the ttm/GEM buffer-object migration copy engine used by
unprivileged GEM clients via `radeon_bo_move` → `radeon_move_blit` →
`radeon_copy(rdev, ..., bo->resv)` (radeon_ttm.c:302) — discards both
synchronization return values:

```c
466:    radeon_sync_resv(rdev, &sync, resv, false);   /* int return ignored */
467:    radeon_sync_rings(rdev, &sync, ring->idx);    /* int return ignored */
```

`radeon_sync_rings()` (radeon_sync.c:137-201) can fail with `-EINVAL`
("Syncing to a disabled ring!", radeon_sync.c:153-156), fence-wait
interruption (radeon_sync.c:160-163, 180-183, 190-193), or semaphore/alloc
failure (radeon_sync.c:165-174). `radeon_sync_resv()` can fail on
`dma_fence_wait()` errors (radeon_sync.c:106, 119). On any of these the
function returns early — possibly after committing semaphore signals on
other rings — and the pending cross-ring dependencies are **not** waited on;
`r600_copy_dma` then emits the DMA copy packets and commits anyway.

Consequence: the copy can execute before a pending write to the *same
buffer object* on another ring completes → silent GPU-side data corruption
of the migrated bo (stale/partial source contents land in the destination).
This is data corruption of the buffer's own contents, not kernel memory
corruption.

## Contrast with sibling code (why this is a defect, not a style choice)

* `radeon_ib_schedule()` — the other user of `radeon_sync_rings()` — checks
  the return and unwinds: radeon_ib.c:149-154 (`failed to sync rings` →
  `radeon_ring_unlock_undo` + return).
* Upstream Linux moved `radeon_sync_resv()` **before** the ring lock with
  full error handling (`if (r) goto error_free;`) in this exact function
  and reserves `num_loops * 4 + 16` — this tree both ignores the errors and
  keeps the smaller `+ 8` reservation (the reservation half of that is the
  known DF-1943; the ignored-return half is this finding).

## Siblings carrying the identical ignored-return pattern (cross-ref)

* `sys/dev/drm/radeon/evergreen_dma.c:130-131`
* `sys/dev/drm/radeon/rv770_dma.c:66-67`
* `sys/dev/drm/radeon/si_dma.c:255-256`
* `sys/dev/drm/radeon/r600.c:2997-2998` (blit copy path)

## Trigger / preconditions

Unprivileged GEM client causes bo migrations (VRAM↔GTT moves, eviction)
whose resv carries fences on another ring; combined with a GPU state where
`radeon_sync_rings()` fails (e.g. a ring disabled by a previous failed ib
test/reset: `ring->ready = false` in radeon_ib.c:271 — then any pending
fence on it makes sync_rings return `-EINVAL`). Realistically requires a
r6xx+ GPU in a degraded state; deterministic construction is not possible
from userland alone.

## Verdict

Not runtime-verified: requires real r6xx-class Radeon hardware; the audit
QEMU guest has none (see `env.txt`). Code-evident divergence from the
checked sibling path. Status: `untested` (hardware-gated), confidence
`likely`.

## Fix

See `fix.diff` — check both returns, unwind ring lock and sync state on
failure; `radeon_sync_resv()` is additionally moved before
`radeon_ring_lock()` so its potentially-sleeping fence waits do not happen
under the global `rdev->ring_lock` mutex (mirrors upstream).
