# DF-4864 — amdgpu_vm_clear_bo() 64-byte IB overrun (Raven root-PD clear)

## What this is

`amdgpu_vm_clear_bo()` (sys/dev/drm/amd/amdgpu/amdgpu_vm.c) sizes its clear-IB
at **64 bytes (16 dwords)**:

    amdgpu_vm.c:855   r = amdgpu_job_alloc_with_ib(adev, 64, &job);

On **CHIP_RAVEN** — the only ASIC with `pte_support_ats` (amdgpu_vm.c:3172) —
the **root** page-directory clear takes **both** emit branches
(amdgpu_vm.c:861-874):

    if (ats_entries) amdgpu_vm_set_pte_pde(...);   /* 10 dwords */
    if (entries)     amdgpu_vm_set_pte_pde(...);   /* 10 dwords */

`amdgpu_vm_set_pte_pde` is a macro (amdgpu_vm.h:293) into
`sdma_v4_0_vm_set_pte_pde()` (sdma_v4_0.c:1184-1200) which emits exactly
**10 dwords per call, unbounded** (`ib->ptr[ib->length_dw++]`). The following
`amdgpu_ring_pad_ib()` (sdma_v4_0.c:1208-1222) appends up to 7 more dwords of
alignment NOPs, also unbounded.

Result: **24 dwords are written into a 16-dword (64-byte) kernel SA
sub-allocation** — a deterministic **32-byte kernel heap OOB write**, executed
on **every GPU VM creation** (DRM_IOCTL_AGP.../AMDGPU VM ioctls — unprivileged
with /dev/drm access) on Raven APUs.

Trigger arithmetic (Raven rev0/1: gmc_v9_0.c:941 → vm 256TB, block 9, 3 levels):
root_level = PDB2; root PD = `round_up(2^36, 2^27) >> 27` = **512 entries**
(amdgpu_vm_num_entries, amdgpu_vm.c:244-248);
`ats_entries = 2^47 >> (27 + 12) = 256`; `entries = 512 - 256 = 256` → both
branches. Raven rev≥2 (gmc_v9_0.c:945, 128TB+512GB): 257-entry root PD,
ats=256/entries=1 → both branches as well.

The non-ATS / non-root case emits a single 10-dword packet + 6 pad dwords =
16 dwords = exactly 64 bytes (no overrun) — the sizing was correct only for
that case, and the guard `WARN_ON(job->ibs[0].length_dw > 64)`
(amdgpu_vm.c:878) compares a **dword** count against the **byte** size, so it
can never trip before the overrun already happened.

## Where the overrun lands

The IB comes from the 1MB SA sub-allocator pool (AMDGPU_IB_POOL_SIZE(16) x 64KB,
amdgpu_ib.c:287) via `amdgpu_sa_bo_new(..., 64, 256)` (amdgpu_ib.c:67-68;
first-fit packing, amdgpu_sa.c:151-173). The 32 overrun bytes land in
whichever kernel memory follows the 64-byte sub-allocation:

* normally the slack before the next SA sub-allocation (or the next job's IB —
  cross-context GPU command corruption), or
* when the pool is packed to its end (attacker-groomable by spraying CS IBs —
  GFX CS IBs are user-sized SA allocations, amdgpu_cs.c:1033), **past the end
  of the 1MB SA buffer's kernel mapping** (ttm/vmap) → kernel heap OOB write
  into adjacent kernel address space.

Overrun content is deterministic: dword 16 = `value_hi` of the second
GENERATE_PTE_PDE packet, 17 = `incr` (0), 18 = 0, 19 = `count - 1`
(entries-1), then 4 SDMA NOP headers (0x00000000), then zeros — i.e. partially
influenceable through VRAM placement of the root PD (value/count).

## Files

* `df4864_trigger.c` — userspace replica of the exact kernel emit/pad sequence
  (code copied verbatim from sdma_v4_0.c) against a canaried 64-byte buffer,
  plus a hardware-reachability probe.
* `build.sh` / `run.sh` — exact build/run.
* `build.log` / `run.log` / `run.2.log` — full untrimmed guest output.
* `fix.diff` — one-hunk fix (IB sized 128 bytes; WARN bound corrected to 32 dw).
* `fix_build.log` — full amdgpu.ko module build with fix.diff applied
  (compiles clean under -Werror; module links).
* `env.txt` — guest environment.

## Build / run (inside the DragonFly guest)

    sh build.sh        # cc -O2 -Wall -o df4864_trigger df4864_trigger.c
    sh run.sh          # runs the three cases + hw probe

## Expected output

    [Raven-root-512] ... *** OOB WRITE: 8 dwords (32 bytes) past the 64-byte SA sub-allocation ***
    [Raven2-root-257] ... *** OOB WRITE: 8 dwords (32 bytes) past the 64-byte SA sub-allocation ***
    [non-ATS-single] ... fits exactly (no overrun) — this is the non-ATS / non-root case

## Runtime status on the audit guest

The audit QEMU guest has **no AMD GPU** (only a Bochs 0x1234:1111 std VGA;
no /dev/dri, no /dev/pci access as unpriv user, no drm modules) — the kernel
path cannot execute there. The kernel-side overrun is therefore **proven from
source arithmetic** (deterministic, no Heisenberg conditions) and demonstrated
byte-for-byte by the replica; it is not runtime-reproduced on this guest.
On real Raven hardware (Ryzen 2200G/2400G and Raven mobile) the path runs on
every VM create.
