# DF-4165 — VBIOS retimer HdmiRegNum/Hdmi6GRegNum drive OOB register-index copy loops past fixed `[9]`/`[3]` arrays in `struct integrated_info`

- File: `sys/dev/drm/amd/display/dc/bios/bios_parser2.c` — `get_integrated_info_v11()` (:1367-1578), reached from `bios_parser2_construct` (:1989) → `bios_parser_create_integrated_info` (:1645, `kzalloc(sizeof(struct integrated_info))` at :1651) → `construct_integrated_info` (:1595, dispatches on ATOM table `content_revision == 11`)
- Class: kernel heap buffer overflow (write + read), CWE-787 / CWE-125
- Verified via the **verbatim-code rig** (`rig/`): the production parser functions
  and every struct they touch were sed-extracted byte-identical from `sys/`
  (`gen_rig.sh`), compiled on the DragonFly guest, and driven with a crafted
  ATOM `integratedsysteminfo v1_11` image whose retimer count bytes exceed the
  array bounds. `check_verbatim.sh` re-proved block identity **against the
  guest's own `/usr/src/sys`** (the tree its running kernel was built from).
- Hardware gating: the guest has only the Bochs SVGA (0x1234:1111), no
  `dev.drm`, no amdgpu (see `env.txt`) — the parser never executes in this
  guest's kernel. In-kernel execution requires an AMD DCE12-class ASIC
  (bios_parser2 path, e.g. Vega10/Raven) presenting the crafted table, i.e. a
  crafted/corrupted VBIOS (flashed ROM, vfio-pci `romfile=`, malicious
  device). That is exactly the threat model of the finding.

## Layout (printed by the rig from the verbatim structs; matches the kernel ABI)

```
sizeof(struct integrated_info)                      = 692   (kzalloc'd at bios_parser2.c:1651)
  dp0_ext_hdmi_reg_settings[9]     @ 582   dp0_ext_hdmi_6g_reg_settings[3] @ 601
  dp1_ext_hdmi_reg_settings[9]     @ 609   dp1_ext_hdmi_6g_reg_settings[3] @ 628
  dp2_ext_hdmi_reg_settings[9]     @ 636   dp2_ext_hdmi_6g_reg_settings[3] @ 655
  dp3_ext_hdmi_reg_settings[9]     @ 663   dp3_ext_hdmi_6g_reg_settings[3] @ 682
  dp_ss_control (last member)      @ 688
sizeof(atom_integrated_system_info_v1_11)           = 1140
sizeof(atom_hdmi_retimer_redriver_set)              = 27    (HdmiRegSetting@3 [9x2B], Hdmi6GhzRegSetting@21 [3x2B])
```

With `Hdmi6GRegNum = 255` (u8) on dp3, the loop writes bytes 682..1191 of the
692-byte kzalloc'd object: **500 bytes past its end** (in-kernel: past the
kmalloc-1024 bucket slot at offset 1024 → ~168 bytes into the adjacent slab
object). Count fields are raw u8 image bytes; there is no clamp anywhere on
the path.

## Build (on the DragonFly guest)

```
cd rig && sh ./build.sh /usr/src     # verbatim check + cc both binaries
```

Builds `rig_base` (production code, byte-identical) and `rig_fixed`
(same slice + the 16 clamp lines of `../fix.diff` under
`-DAPPLY_DF4165_FIX`; the checker verifies the guarded lines are exactly
fix.diff's additions).

## Run

```
cd rig && sh ./run.sh
```

Five modes per build: `layout`, `dest-guard` (kzalloc object ends at a
PROT_NONE page), `src-guard` (v1_11 table ends at the mapped BIOS-image end),
`canary` (only dp0.HdmiRegNum=255, adjacent victim pre-filled 0x41),
`canary6g` (only dp3.Hdmi6GRegNum=255).

## Expected output (see run.log / run.2.log)

Baseline (`rig_base`):
- `dest-guard`: SIGSEGV, `fault offset ... 692 (sizeof=692) -> 0 bytes PAST
  the allocation end => OUT-OF-BOUNDS WRITE` (rc=88)
- `src-guard`: SIGSEGV, `fault offset in BIOS image: 4096 (bios_size=4096)
  => READ BEYOND BIOS IMAGE (OOB READ)` (rc=88)
- `canary`: `VICTIM bytes changed: 400/512 ... 400 match / 0 mismatch`
  — every byte written past the 692-byte object is byte-for-byte the crafted
  image byte from the corresponding source position
- `canary6g`: `500/512 ... 500 match / 0 mismatch`, `dp_ss_control` (last
  member) clobbered with crafted bytes

Fixed (`rig_fixed`): both guard modes complete rc=0 with stored counts
`9/3`, canary modes show `VICTIM bytes changed: 0/512`, `victim intact`.

## Verdict

Reproduced (verbatim-rig evidence class): the exact production parser code,
byte-identical to the running kernel's source, performs a fully
attacker-controlled linear heap overflow of up to 500 bytes past the
destination object plus OOB reads past the source ATOM table, on a crafted
VBIOS. `fix.diff` (clamp all eight counts to 9/3) eliminates it — A/B
validated in the same rig on the identical crafted image. See `VERDICT.md`,
`verdict.json`, `fix_validation.log`.
