# DF-3697 — VERDICT

**status: not_reproduced (hardware absent) · source-level: certain**

## What the code does

1. `gfx_v9_0_init_microcode` (`gfx_v9_0.c:663-668`) requests the RLC firmware
   and reads the header. Header version 2.1 sets `adev->gfx.rlc.is_rlc_v2_1`
   (`:670-673`).
2. For v2_1 images, `gfx_v9_0_init_rlc_ext_microcode` (`:516-535`, called at
   `:715-716`) computes three pointers straight from unvalidated u32 header
   fields:

   ```c
   adev->gfx.rlc.save_restore_list_cntl =
       (u8 *)rlc_hdr + le32_to_cpu(rlc_hdr->save_restore_list_cntl_offset_bytes);
   adev->gfx.rlc.save_restore_list_gpm  = (u8 *)rlc_hdr + ..._gpm_offset_bytes;
   adev->gfx.rlc.save_restore_list_srm  = (u8 *)rlc_hdr + ..._srm_offset_bytes;
   ```

   with `*_size_bytes` likewise unchecked. Nothing in this tree constrains
   them: `amdgpu_ucode_validate` (`amdgpu_ucode.c:251-259`) only checks
   `fw->datasize == hdr->size_bytes` — a self-consistency check that a crafted
   image trivially satisfies (the generator sets `size_bytes = 256 = filesize`).
3. On PSP-load devices the three sub-images are registered
   (`gfx_v9_0.c:776-797`) and later copied by
   `amdgpu_ucode_init_single_fw` (`amdgpu_ucode.c:381-394`):

   ```c
   ucode->ucode_size = adev->gfx.rlc.save_restore_list_cntl_size_bytes;
   memcpy(ucode->kaddr, adev->gfx.rlc.save_restore_list_cntl, ucode->ucode_size);
   ```

   Destination BO is allocated using the same attacker fields
   (`amdgpu_ucode_init_bo`, `amdgpu_psp.c:428`), so dst is sized to match:
   the bug is an **out-of-bounds read** of `size_bytes` from
   `fw->data + offset` (offset/size up to 4 GiB each) — wild-pointer read →
   page-fault panic, or a copy of unrelated kernel heap into a GPU-mapped BO.

This is **distinct from DF-1176**, which covers the *v2_0* reg-list fields
(`reg_list_format_*` kmalloc + copy loops at `gfx_v9_0.c:695-713`); this
finding is the *v2_1 extension block* (different fields, different function,
different sink — the ucode-loader memcpy).

## Verification run (guest, virtio-only)

* Guest: DragonFly 6.5-DEVELOPMENT `#0 Thu Jul 2 06:02:54 UTC 2026`, x86_64.
* `amdgpu.ko` **is** present in `/boot/kernel` and `kldload amdgpu` succeeds
  (`[drm] amdgpu kernel modesetting enabled.`), proving the vulnerable code
  ships compiled in this kernel build.
* PCI scan shows only virtio-vga (`vgapci0@pci0:0:2.0 chip=0x11111234`) —
  **no vendor 0x1002 device**, so `amdgpu_pci_probe` never matches,
  `gfx_v9_0_sw_init`/`init_microcode` never execute → the parse path is
  unreachable at runtime on this guest.
* The trigger generator builds cleanly with the guest toolchain (`cc 8.3`)
  and produces `amdgpufw_raven_rlc.evil` (256 bytes) whose bytes drive the
  bug on real hardware: header 2.1, `srlc_offset = 0x10000`, `srlc_size = 0x1000`.
  Build+run logs: `build.log`, `run.log`, `env.txt`.

## Honest classification

`not_reproduced` — not because the path was traced dead (it is live for every
v2_1 RLC image on PSP-load Vega10/12/Raven hardware), but because the audit
guest has no AMD GPU to bind. Source-level certainty: every link in the chain
is cited above and re-checked line-by-line in this tree.

Impact ceiling on hardware: kernel memory disclosure into GPU-accessible
memory (contents of whatever follows the firmware allocation) or DoS panic.
Not a write primitive.

## Fix validation

`fix.diff` adds `offset + size <= fw->datasize` checks for all three
save-restore-list blobs and makes `gfx_v9_0_init_rlc_ext_microcode` fail
`init_microcode` on violation. Not runtime-testable here (no hardware);
`fix_status: not_tested`, baseline not reproduced for the same reason.

## 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.
