# DF-4372 — r600_parse_extended_power_table dereferences two VBIOS offsets
without any validation (fan table offset, extended-header offset)

* File: `sys/dev/drm/radeon/r600_dpm.c` (:891-918 fan table, :1068-1073 ext
  header + every downstream ext-table condition)
* Severity: Low (sibling of DF-1566 — distinct missing checks: *offset
  provenance* rather than count/index bounds; same hostile-VBIOS threat)
* Class: CWE-125 / CWE-20 (unvalidated firmware offset → OOB heap reads and
  pointer confusion of the entire extended-table parse).

## The bug

Every sub-table *inside* the extended header checks its own offset field
(`usVCETableOffset`, `usUVDTableOffset`, …) for non-zero, but the two
**container** offsets are used with no check at all:

1. Fan table — `r600_dpm.c:893-895`: `fan_info = bios + data_offset +
   usFanTableOffset` and immediately dereferenced (`ucTHyst`, `usTMin`,
   …, and for `ucFanTableFormat >= 2` the `fan2`/`fan3` unions at :903-915).
   `usFanTableOffset` is a raw u16 from the ROM: 0 is pre-checked, but any
   value up to 65535 is accepted even when `data_offset + offset` (+
   record size) lies beyond the kmalloc'd BIOS image
   (`radeon_bios.c:68/98/153/269/717`, ≤ 256 KiB) → OOB heap read; with
   `ucFanTableFormat` itself read out of bounds the walk can chain further.

2. Extended header — `r600_dpm.c:1070-1073`: `ext_hdr = bios + data_offset +
   usExtendendedHeaderOffset` is dereferenced (`ext_hdr->usSize`) with **no
   zero check and no bound**. A crafted table with
   `usExtendendedHeaderOffset == 0` aliases the *PowerPlayInfo table header
   itself* as the extended header: `usSize` and all six sub-table offsets
   then come from unrelated header fields, and each subsequent
   `ext_hdr->usXTableOffset` (used at :1074-1293 for VCE/UVD/SAMU/PPM/ACP/
   PowerTune) can point anywhere within a 64 KiB window of `bios` — far
   outside the actual allocation on small ROMs. The downstream
   count/index walks are DF-1566's known territory; the *pointer
   provenance* itself is unchecked here.

## Trigger

Crafted VBIOS on a radeon DPM-family card (modified ROM dump, hostile
option ROM, or ACPI VFCT image — `radeon_bios.c:717` copies the VFCT image
into the same kmalloc buffer). Boot → radeon attach → `radeon_pm_init` →
`<asic>_dpm_init` → `r600_parse_extended_power_table`.

## Fix

`fix.diff` plumbs the real ROM size (`rdev->bios_size`, set at every
allocation site in `radeon_bios.c`) into the parser and (a) rejects a fan
table offset that runs past the image, (b) computes `ext_hdr` only when
`usExtendendedHeaderOffset != 0` and `data_offset + ext_off +
sizeof(ATOM_PPLIB_EXTENDEDHEADER) <= bios_size`, gating all six downstream
conditions on the result (`ext_hdr_ok`). Verified `patch -p1 --dry-run` →
APPLIES_CLEAN on scratch copies (radeon.h, radeon_bios.c, r600_dpm.c).
