# DF-4238 — sdma_v3_0: SDMA firmware sub-header fields read after only
datasize==size_bytes validation — heap OOB read on a short (root-installed)
firmware blob (DF-1451/1553/1598 family, v3 variant)

## What

`sdma_v3_0_init_microcode()` (sys/dev/drm/amd/amdgpu/sdma_v3_0.c) validates
each SDMA firmware image only through `amdgpu_ucode_validate()`, which checks
a single equality — `fw->datasize == hdr->size_bytes`
(amdgpu_ucode.c:251-260) — and then immediately reads SDMA sub-header fields
that live *beyond* the common header:

* sdma_v3_0.c:316 `hdr->header.ucode_version` (common header +16)
* sdma_v3_0.c:317 `hdr->ucode_feature_version` (sdma v1_0 header +32;
  `struct sdma_firmware_header_v1_0` is 48 bytes — amdgpu_ucode.h:120-126)
* sdma_v3_0.c:324-326 `header->ucode_size_bytes` (common header +24) folded
  into `adev->firmware.fw_size`, which sizes the firmware BO later memcpy'd
  from `fw->data + ucode_array_offset_bytes` (amdgpu_ucode.c:347-351, the
  DF-1947 sink).

A blob as small as 4 bytes (declaring `size_bytes = 4`) passes validation;
every field read above is then a heap out-of-bounds read on the
request_firmware buffer.

## Threat model

Firmware files (`amdgpufw_tonga_sdma` etc., sdma_v3_0.c:54-68) are root-
installed module firmware — same gate as DF-1451 (sdma_v4_0), DF-1553
(cik_sdma), DF-1598 (sdma_v2_4). Low severity: root-triggerable kernel heap
OOB read (leak into fw_version/feature_version printed to dmesg, and
`fw_size` inflation feeding the DF-1947 memcpy sink).

## Evidence in this pack

* `fwcheck.c` — harness replicating the exact validate-then-read sequence on
  a 4-byte blob placed against a guard page. Built and run **on the audit
  guest** (DragonFly cc 8.3):
  1. `amdgpu_ucode_validate(4-byte blob) = 0 (accepted)`
  2. the first sub-header read (`ucode_version` at +16, the exact first
     field sdma_v3_0.c:316 reads) **faults into the guard page → SIGSEGV,
     exit 139** — the out-of-bounds access proven without ASan.
* `run.log` — full untrimmed guest output.
* `fix.diff` — minimum-size guard in `sdma_v3_0_init_microcode` (the shared
  root fix — offset/size cross-validation in `amdgpu_ucode_validate` — is
  DF-1946 and out of scope for this file).

## Reproduce

```
# on the DragonFly guest (no AMD hardware needed for the harness)
cc -O2 -Wall -o fwcheck fwcheck.c
./fwcheck; echo $?     # prints validate=0, then dies SIGSEGV (139) at the +16 read
```

The kernel path itself is hardware-gated (Tonga/Polaris/Fiji/Carrizo/Stoney
SDMA engine — none on the guest, see `env.txt`), so the finding is
**not_reproduced / hardware-gated**; the parsing primitive is proven by the
harness.
