# DF-4265 — `amdgpu_cs_wait_all_fences` reads `fence->error` after the final `dma_fence_put` (UAF read)

**File:** `sys/dev/drm/amd/amdgpu/amdgpu_cs.c` (pass 2)
**Severity:** Low (use-after-free read; value returned as ioctl errno) — CWE-416
**Confidence:** likely (defect certain; runtime manifestation narrowed by
RCU-deferred freeing on this tree)

## Build

```
cc -O2 -Wall -o wait_all_fences_uaf wait_all_fences_uaf.c
```

## Run (as any user on amdgpu hardware)

```
./wait_all_fences_uaf
```
Submits 33 minimal GFX CS on one context (so the context fence slot for
seq 1 — replaced only after `amdgpu_sched_jobs`=32 further submissions,
amdgpu_ctx.c:449-461 — is recycled while the wait ioctl holds its own
reference), then calls `DRM_IOCTL_AMDGPU_WAIT_FENCES` with
`wait_all=1` on seq 1.

## Root cause (path:line)

`amdgpu_cs_wait_all_fences()` (amdgpu_cs.c:1481-1516):

```c
r = dma_fence_wait_timeout(fence, true, timeout);
dma_fence_put(fence);              /* :1501  may drop the LAST ref   */
if (r < 0) return r;
if (r == 0) break;
if (fence->error)                  /* :1508  READ AFTER PUT          */
        return fence->error;
```

If the reference obtained from `amdgpu_cs_get_fence()` (amdgpu_ctx.c:489)
is the last one (context slot replaced, scheduler/job refs dropped),
:1501 frees the fence and :1508 reads freed memory. Compare the correct
ordering in the sibling `amdgpu_cs_wait_ioctl()` (:1367-1370: read
`fence->error` **before** the put).

On this tree the free is deferred: `drm_sched_fence_release_finished` →
`dma_fence_put(scheduled)` → `drm_sched_fence_release_scheduled` →
`call_rcu(&fence->finished.rcu, drm_sched_fence_free)`
(sys/dev/drm/scheduler/sched_fence.c:107-113), so a plain
single-threaded run reads pre-free memory; manifestation needs the RCU
grace period (or preemption) between :1501 and :1508, and the stale
value is returned to userspace as the ioctl errno — a 4-byte heap
oracle (whether the slot got reallocated by a concurrently-submitted
job, and the new fence's error code). Impact ceiling: info oracle +
theoretical crash; kept Low.

## Guest verification (this audit)

Hardware-gated like DF-4262..64 (no AMD GPU): trigger builds
(`build.log`), run exits at open (`run.log`); baseline + patched
in-tree compile rc=0 with `-Werror` (`kmod_baseline_build.log`,
`fix_build.log` — reorder so the put happens after the error read, all
exit paths covered).

## Fix

`fix.diff`: restructure the loop body so `fence->error` is read before
`dma_fence_put()` and every exit path (r<0 return, r==0 break, error
return, loop continue) puts the reference exactly once.
