# DF-3860 — gpu_info firmware payload read at unvalidated `ucode_array_offset_bytes` (kernel heap OOB read)

File: `sys/dev/drm/amd/amdgpu/amdgpu_device.c` — `amdgpu_device_parse_gpu_info_fw()` (lines 1301–1407).

## Root cause

1. `amdgpu_device_parse_gpu_info_fw()` loads `amdgpufw_<asic>_gpu_info` via
   `request_firmware()` (amdgpu_device.c:1354) and calls `amdgpu_ucode_validate()`
   (amdgpu_device.c:1361).
2. `amdgpu_ucode_validate()` (sys/dev/drm/amd/amdgpu/amdgpu_ucode.c:251–260)
   ONLY checks `fw->datasize == le32_to_cpu(hdr->size_bytes)`. It performs **no
   validation of `hdr->ucode_array_offset_bytes`** and no check that the payload
   fits in the buffer.
3. The parse then does (amdgpu_device.c:1375–1377):

```c
        const struct gpu_info_firmware_v1_0 *gpu_info_fw =
                (const struct gpu_info_firmware_v1_0 *)(adev->firmware.gpu_info_fw->data +
                                                        le32_to_cpu(hdr->header.ucode_array_offset_bytes));
```

   `ucode_array_offset_bytes` is a raw, attacker-chosen u32 inside the firmware
   file. Lines 1379–1396 then read 14 u32 members (`gc_num_se` … `gc_lds_size`,
   a 60-byte `struct gpu_info_firmware_v1_0`, amdgpu_ucode.h:135–151) through
   that pointer — up to ~4 GiB past the end of the firmware buffer.

4. The read values are stored into `adev->gfx.config.*` and `adev->gfx.cu_info.*`
   and are (a) returned to unprivileged userspace via the
   `AMDGPU_INFO_CU_COUNT`-family paths of AMDGPU_INFO ioctl
   (amdgpu_kms.c copies `adev->gfx.cu_info`), i.e. **heap memory disclosure**;
   and (b) fed into the cu_info[4][4] walkers analysed in DF-3698
   (`max_shader_engines`, `max_sh_per_se` — no clamp on this parse side).

The firmware-parse itself happens at driver probe for VEGA10/VEGA12/RAVEN
(amdgpu_device.c:1337–1350).

## Threat model

Requires control of the gpu_info firmware file (root-planted file in the
firmware search path /usr/local/lib/firmware, a compromised firmware channel,
or an image-distribution attack). Same precondition class as the already-filed
DF-1256 (same loader) and DF-3698 (same consumer).

## Trigger

`make_gpuinfo_fw.py` generates a minimal file whose `common_firmware_header`
satisfies `amdgpu_ucode_validate()` (size_bytes == file size) but whose
`ucode_array_offset_bytes` points far past the buffer (default 0x7fff0000).
On hardware, placing this file and loading amdgpu makes the kernel read
`data + 0x7fff0000 … +60`.

## Build / run

- `./build.sh` — no compilation needed; runs the generator and hexdumps the file.
- `./run.sh` — on a host with a VEGA10-class AMD GPU: install the file, load
  amdgpu; on this audit guest: reports the hardware gate.

## Expected result (on hardware)

Kernel reads 60 bytes at `fw->data + attacker_offset`. With a
groomed firmware slab this is a kernel heap OOB read whose contents are
reflected to userspace via AMDGPU_INFO (cu_info fields) — plus unclamped
values flowing into the DF-3698 walker family (heap OOB write there).
No crash is expected from the read itself; a KASAN/DEBUG kernel would log
the OOB. On DragonFly the observable artifact is bogus `cu_info` values
in AMDGPU_INFO output that differ from the real hardware census.
