# DF-1246 — radeon evergreen_dma_cs_parse IB body OOB read+write

## Verdict (one line)
**CONFIRMED REAL (source trace + harness primitive), NOT reproduced on audit guest (no AMD/ATI radeon GPU).**

## Finding
`sys/dev/drm/radeon/evergreen_cs.c:evergreen_dma_cs_parse()` bounds-checks
only the **header** word of each DMA packet (`evergreen_cs.c:2806`,
`if (p->idx >= ib_chunk->length_dw)`) but then reads and **writes** body
words `ib[idx+N]` for N up to 8 (and reads via `radeon_get_ib_value(p, idx+N)`)
with no check that the body fits in the IB. A crafted indirect buffer whose
packet header is near the end causes `idx+1..idx+8` to index past the `ib`
allocation: adjacent kernel memory is **read** (leak) and **written back**
(corruption).

## Mechanism (path:line)
1. `evergreen_cs.c:2806` — loop entry guards only the header:
   `if (p->idx >= ib_chunk->length_dw) return -EINVAL;`
2. `evergreen_cs.c:2811-2812` — `idx = p->idx; header = ib[idx];`
3. Body accesses with NO bounds check, e.g.:
   - `evergreen_cs.c:2830` `ib[idx+1] += (u32)(dst_reloc->gpu_offset >> 8);`
     (DMA_PACKET_WRITE tiled)
   - `evergreen_cs.c:2897` `radeon_get_ib_value(p, idx+8)` (COPY L2T read)
   - `evergreen_cs.c:2899` `ib[idx+8] += upper_32_bits(dst_reloc->gpu_offset);`
     (COPY L2T write)
4. `radeon_get_ib_value(p, idx)` is `p->ib.ptr[idx]` with no check
   (`sys/dev/drm/radeon/radeon.h:1098-1105`).
5. Contrast the CP path, which strictly checks `count+1+idx >= length_dw`
   via `radeon_cs_packet_parse` — the DMA path omits this.

## Why not reproduced on the audit guest
`radeon` is a loadable DRM module (`/boot/kernel/radeon.ko`), **not in
`X86_64_GENERIC`**, and the QEMU/KVM guest has **no AMD/ATI GPU** (no PCI
GPU, no `/dev/dri/renderD128`). The CS-parse path is reached only from the
radeon DRM ioctl after a GPU attach. There is no GPU to attach, so the path
is dead at runtime on this guest. The realistic trigger is an unprivileged
local user on a machine with an affected radeon GPU submitting a crafted
DMA command stream — a legitimate local-privilege-escalation / kernel-memory-
corruption threat on such hardware, but not exercisable here.

## Primitive proof (harness)
`harness.c` replicates the parse loop with a 4-word IB and a header at the
last word, then emulates the COPY L2T detile access (`ib[idx+8]`). Result:
```
Header bounds check (evergreen_cs.c:2806) PASSED: idx=3 < length_dw=4
idx+8 = 11, length_dw = 4
ib[idx+8] read = 0x00000000  (OUT OF BOUNDS: idx+8=11 >= length_dw=4)
ib[idx+8] written back = 0x11111111  (heap word corrupted)
PRIMITIVE CONFIRMED: idx+8 (11) >= length_dw (4); OOB read+write past IB.
```

## Fix
`fix.diff` adds a per-packet body-size bounds check after the header is
parsed (`evergreen_cs.c`): it computes the maximum body word count `need`
for each `cmd`/`sub_cmd` (2..10 words) and returns `-EINVAL` if
`idx + need > ib_chunk->length_dw` before any `ib[idx+N]` access. This
mirrors the strict check the CP path already has. **Validated: builds
cleanly into `radeon.ko` with `-Werror`.**

## Reproduce
```
ssh dfbsd-maxx; cd poc/DF-1246 && cc -O2 -Wall -o harness harness.c && ./harness
```
