# DF-4152 — Object-table walks dereference unvalidated VBIOS offsets and stride unbounded sizes (amdgpu twin of DF-3745)

File: `sys/dev/drm/amd/amdgpu/amdgpu_atombios.c` (pass 2)

## Reproduced?

**No — hardware-gated.** No AMD GPU on the audit guest (`pciconf -l`: only
virtio VGA 0x1111:1234; zero `amdgpu`/`radeon` matches in dmesg — `env.txt`).
Crafted-VBIOS delivery additionally requires `romfile=` VFIO assignment or a
malicious FPGA device. Established at source level.

## Source trace (all inside `amdgpu_atombios_get_connector_info_from_object_table`, lines 291-563)

Enabler: `amdgpu_atom_parse_data_header()` (`atom.c:1384-1403`) returns
`data_start = idx` (a BIOS-supplied u16) with **no extent validation**, and
`struct atom_context` carries **no bios_size** — identical to the radeon
situation in DF-4104/DF-3745. The BIOS image itself is `kmalloc(size)` with
`size` from the ROM (`amdgpu_bios.c:106/135/206`). Every pointer of the form
`bios + data_offset + <u16 from BIOS>` is therefore an unvalidated heap
pointer.

1. **Display-path walk** (`:333-338`): `path_size += le16_to_cpu(path->usSize)`
   accumulates per-entry u16 sizes with no cumulative bound; `addr` walks
   `path_obj->asDispPath + path_size`. 255 entries * 65535 = up to ~16 MB of
   pointer travel past `path_obj` (itself at an unvalidated
   `usDisplayPathTableOffset`, `:318-320`). Each iteration dereferences
   `path->usSize/usDeviceTag/usConnObjectId/usGraphicObjIds[]` → OOB heap
   reads of the parsed fields and of the `usGraphicObjIds[j]` inner loop
   (`:375-386`, count = `(usSize-8)/2`, unbounded).

2. **Encoder record deref + walk** (`:392-410`):
   `record = bios + data_offset + enc_obj->asObjects[k].usRecordOffset`
   — u16 offset, unvalidated → immediate OOB read of `record->ucRecordSize`;
   then `while (ucRecordSize > 0 && ucRecordType > 0 && <= MAX)` advances by
   the u8 `ucRecordSize`. The walk terminates only when it reads a
   non-conforming byte — with a crafted BIOS the walk runs arbitrarily far
   past the allocation (min stride 1; each step needs size>0 and
   1<=type<=ATOM_MAX_OBJECT_RECORD_NUMBER). Values read (e.g.
   `ATOM_ENCODER_CAP_RECORD.usEncoderCap`) feed `amdgpu_display_add_encoder`.

3. **Router record walk + src/dst table** (`:416-482`): same record-walk
   pattern (`:420-422`, `:444-480`), plus
   `router_src_dst_table = bios + data_offset + usSrcDstTableOffset`
   (`:427-430`) and the `dst_objs[enum_id]` walk bounded by the u8
   `*num_dst_objs` (`:431-442`) — both deref/stride unvalidated data. (The
   `ucMuxState[enum_id]` read within this block is the already-filed
   DF-1409; the offsets/walks feeding it are not covered by any known.)

4. **Connector record walk** (`:491-541`): identical deref of
   `con_obj->asObjects[j].usRecordOffset` (`:495-502`) and `ucRecordSize`
   walk (`:507-538`).

Related (same no-extent-check enabler, noted not separately filed): fixed
`union firmware_info` reads at `bios+data_offset` regardless of actual table
length (`:588-713`), and `has_dce_engine_info`'s `usDisplayPathTableOffset`
deref (`:280-288`).

## Distinction from knowns

* DF-1408 covers the `(size - sizeof(HEADER)) / sizeof(ENTRY)` underflow
  sites (`:106, :177, :926/945/967, :1326-1331`) and the u8-count flex-array
  walks at `:375/:389` (`enc_obj->ucNumberOfObjects`,
  `con_obj->ucNumberOfObjects`).
* DF-1409 covers `ucMuxState[enum_id]` OOB.
* **Not covered** (this finding): the unvalidated **u16 offset dereferences**
  (`usRecordOffset`, `usSrcDstTableOffset`, `usDisplayPathTableOffset`), the
  **`ucRecordSize`-stride record walks** with no termination bound, and the
  **`usSize` accumulation** in the display-path walk.

## Verdict

INCONCLUSIVE (hardware-gated / not reachable on the audit guest).
Source-certain: crafted VBIOS ⇒ unbounded OOB kernel heap reads at driver
attach / connector init (kheap disclosure ceiling is limited — parsed values
steer encoder/connector/ddc setup — plus fault/DoS). Severity Medium,
aligned with radeon twin DF-3745.

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