# DF-4104 VERDICT

**Status: untested (hardware-gated — no AMD GPU on the audit guest; source-level certainty certain)**
**Impact: OOB heap read (≤ ~44 bytes + follow-on record-walk reads which are DF-1380's scope) of the kmalloc'd VBIOS image buffer; parsed values become panel mode / SS id state. Panic if the read crosses into an unmapped page.**

## Root cause chain

1. The VBIOS image is copied into a heap buffer sized from the image itself:
   - `amdgpu_read_bios_from_rom` amdgpu_bios.c:172-179:
     `len = AMD_VBIOS_LENGTH(header); adev->bios = kmalloc(len); adev->bios_size = len;`
     (also amdgpu_read_bios:135-141 with `pci_map_rom` size, and the
     ATOM-flash/VRAM paths — the buffer is *exactly* image-sized, never padded
     to 64KB/256KB).
2. `struct atom_context` (atom.h:126-144) has **no bios_size member** — no
   atom consumer can even express a bound check.
3. `amdgpu_atom_parse_data_header` (atom.c:1384-1403) does:
   `int idx = CU16(ctx->data_table + offset); ... *data_start = idx; return true;`
   — it validates only that the master-table entry is non-zero. The u16
   `data_offset` is handed to the caller with **no check that
   data_offset + table-size ≤ bios_size**.
4. `amdgpu_atombios_encoder_get_lcd_info` (atombios_encoders.c:2069) passes
   `NULL` for the size out-param (2081-2082) and immediately casts
   `lvds_info = (union lvds_info *)(mode_info->atom_context->bios + data_offset)`
   (2083-2084), then performs ~22 fixed-offset reads through
   2091-2131 (`sLCDTiming` fields, `usOffDelayInMs`, `ucLVDS_Misc`,
   mode-misc polarity bits, `usModePatchTableOffset` at 2141, `ucSS_Id` at
   2131) — spanning sizeof(_ATOM_LVDS_INFO) = 0x2C bytes
   (atombios.h:3898-3917: 4-byte header + 28-byte ATOM_DTD_FORMAT + 12 tail).

Because `data_offset` is an unvalidated u16 (0..0xFFFF) and the buffer is
image-sized (frequently 32KB/64KB, can be as small as the ROM declares), a
crafted VBIOS places the table near the image end and every field read runs
past the allocation. This is the amdgpu twin of radeon's DF-3745 family.

Distinction from known DF-1380 (same function): DF-1380 covers the
`while (*record != ATOM_RECORD_END_TYPE)` walk from `usModePatchTableOffset`
(atombios_encoders.c:2156). The reads here (2091-2131) execute *before* and
*independently* of the record walk (they run even when
`usModePatchTableOffset == 0`), and their fix (bound `data_offset` against
bios_size) is different from DF-1380's fix (bound the record pointer).

## Reachability

Boot-time: `amdgpu_modeset` → encoder init (dce_v10_0.c:3520) →
get_lcd_info. Attacker model: crafted VBIOS (root-flashed SPI, malicious
replacement eGPU/PCIe option ROM, or a supplied .rom loaded via the
Linux-vendor `rom` sysfs mechanism — DFly has no such loader, so physical/
root flash is the gate). Read-only primitive; values feed panel timing and
`lcd_ss_id` (consumed in atombios_crtc.c:789 — SS id family).

## Why untested

Guest has no AMD GPU; amdgpu never attaches (pciconf shows bochs 1234:1111
only). Line-by-line trace performed instead.

## Fix

Plumb `bios_size` into `struct atom_context` (set at
amdgpu_atombios.c:2037 `amdgpu_atom_parse(atom_card_info, adev->bios)`),
bound-check `data_offset` + minimum struct size in
`amdgpu_atom_parse_data_header`, and in get_lcd_info verify
`data_offset + sizeof(union lvds_info) <= bios_size` before dereferencing.
See fix.diff.

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