# DF-3573 — legacy-firmware size-check bypass in cik_init_microcode() → heap OOB reads in legacy ucode loaders

File: `sys/dev/drm/radeon/cik.c` (pass 2, GLM 5.3)

## What

`cik_init_microcode()` (cik.c:1961-2299) validates legacy (old-format) firmware
sizes, but only the **pfp** block follows a "Bogus length" with `goto out`
(cik.c:2060). The **me / ce / mec / rlc / sdma / mc / smc** blocks set
`err = -EINVAL` and **fall through** (cik.c:2080-2084, 2103-2107, 2126-2130,
2164-2168, 2187-2191, 2216-2221, 2248-2252). Every subsequent
`err = request_firmware(...)` assignment **clobbers the error on success**, so
when any later firmware in the sequence loads cleanly the function returns 0
with the wrong-size firmware still installed and `rdev->new_fw = false`.

The legacy loaders then bound their read loops by **compile-time constants**,
not by `fw->datasize`:

| loader | loop bound | site |
|---|---|---|
| `cik_cp_gfx_load_microcode` PFP/CE/ME | `CIK_{PFP,CE,ME}_UCODE_SIZE` (2144 dw) | cik.c:3966, 3973, **3980** |
| `cik_cp_compute_load_microcode` MEC1/2 | `CIK_MEC_UCODE_SIZE` (4192 dw) | cik.c:4318, 4326 |
| `cik_rlc_resume` RLC | `BONAIRE/KV/KB/ML_RLC_UCODE_SIZE` | cik.c:6001-6002 |
| `cik_sdma_load_microcode` SDMA0/1 | `CIK_SDMA_UCODE_SIZE` (1050 dw) | cik_sdma.c:501, 508 |

A legacy firmware file shorter than `CONSTANT*4` therefore produces a kernel
**heap OOB read** of up to `CONSTANT*4 - datasize` bytes (e.g. 7552 bytes for
a 1 KiB ME file), streamed into CP/RLC register FIFOs. (The MC legacy loader
is self-bounded by `datasize/4` at cik.c:1880 — harmless.)

Not a DFly port bug: verified byte-identical to Linux v4.7
`drivers/gpu/drm/radeon/cik.c` (upstream carries the same asymmetric `goto
out`). Distinct from DF-1119 (new-format header fields unchecked): this is the
*legacy* path and the *size gate itself* being bypassable.

## Build / Run (guest)

```
scp fwsize_bypass.c dfbsd-maxx:/tmp/
cc -O -Wall -o /tmp/fwsize_bypass /tmp/fwsize_bypass.c
cc -O -Wall -DFIXED -o /tmp/fwsize_bypass_fixed /tmp/fwsize_bypass.c
/tmp/fwsize_bypass          # baseline acceptance state machine
/tmp/fwsize_bypass_fixed    # with fix.diff applied
```

The harness is a statement-faithful transcription of cik.c:2049-2297
(BONAIRE, all-legacy) plus a simulation of the constant-bounded legacy loops.
It runs on any Unix — no radeon hardware needed — because the kernel path
itself requires a CIK GPU to attach, which this (virtio-only) guest does not
have.

## Expected output (baseline)

```
== S1: truncated legacy ME firmware ==
  cik_init_microcode -> err=0 (ACCEPTED), installed fw: ..., me_fw(1024B), ...
    OOB READ: me_fw holds 1024 bytes, cik.c:3980 loop reads 8576 -> 7552 bytes past allocation
```
and, under `-DFIXED` (fix.diff):
```
== S1: truncated legacy ME firmware ==
  cik_init_microcode -> err=-22 (rejected), installed fw: (none)
```
Controls: stock sizes accepted in both modes; a truncated **pfp** is rejected
in both modes (pfp's block already has `goto out`).

## Fix

`fix.diff` — add `goto out;` after every "Bogus length" `err = -EINVAL`
(7 sites), matching the pfp precedent. Applies with `patch -p0` from the
tree root; verified against a scratch copy.
