amdgpu_atombios_lookup_voltage_object_v3: zero usSize strides forever; walk bounded only by an unvalidated usStructureSize (hang / OOB read at DPM init)
Summary
The voltage-object walk advances offset by the BIOS u16 usSize; usSize==0 on the first non-matching object makes offset never advance - infinite loop. The bound itself (usStructureSize) is an unvalidated BIOS u16, so a large claimed size walks up to ~64KB past the VoltageObjectInfo table inside the kmalloced BIOS image. Callers run at DPM init/driver bind. The radeon twin is in DF-3741; the adjacent get_voltage_table count clamp is correct - only this walk is unguarded. Floor-the-stride fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4155 Β· 4 files| File | Type | Description | Size | |
|---|---|---|---|---|
| vbios_mutator.py | trigger | Illustrative trigger (hardware-gated) | 1.4 KB | view raw |
| VERDICT.md | verdict | Full source-trace analysis | 2.5 KB | β raw |
| fix.diff | suggested-fix | Break the lookup walk when usSize < sizeof(ATOM_VOLTAGE_OBJECT_V3) (zero-stride) instead of adding it to offset. | 960 B | view raw |
| env.txt | environment | Guest uname + pciconf hardware-gate proof | 980 B | view raw |
DF-4155 β amdgpu_atombios_lookup_voltage_object_v3: zero usSize strides forever; walk bounded only by an unvalidated usStructureSize
File: sys/dev/drm/amd/amdgpu/amdgpu_atombios.c (pass 2)
Reproduced?
No β hardware-gated (no AMD GPU on the audit guest; see env.txt).
Established at source level.
Source trace (amdgpu_atombios_lookup_voltage_object_v3, lines 1415-1430)
u32 size = le16_to_cpu(v3->sHeader.usStructureSize); /* BIOS u16, unvalidated */
u32 offset = offsetof(ATOM_VOLTAGE_OBJECT_INFO_V3_1, asVoltageObj[0]);
while (offset < size) {
ATOM_VOLTAGE_OBJECT_V3 *vo = (ATOM_VOLTAGE_OBJECT_V3 *)(start + offset);
if (type/mode match) return vo;
offset += le16_to_cpu(vo->asGpioVoltageObj.sHeader.usSize); /* may be 0 */
}
Two defects:
-
Zero-stride infinite loop:
usSize == 0on the first (non-matching) voltage object βoffsetnever advances β the loop re-reads the same header forever β hard hang. Callers:amdgpu_atombios_get_svi2_info(:1452),amdgpu_atombios_is_voltage_gpio(:1494), andamdgpu_atombios_get_voltage_table(:1533) β all invoked during amdgpu DPM init at driver bind with a crafted VBIOS. The radeon twin (atom_lookup_voltage_object_v3) is explicitly listed in DF-3741. -
Unvalidated bound: the loop runs to
usStructureSize(up to 65535), never against the actual BIOS allocation length (atom_context has no bios_size β DF-4104 enabler). A large claimed size makes the walk read up to ~64 KB past theVoltageObjectInfotable inside the kmalloced image β OOB kernel heap reads (headers,ucVoltageType/Modecomparisons) until a lucky match, the offset limit, or a fault.
Note the sibling amdgpu_atombios_get_voltage_table (:1539-1540) does
correctly clamp ucGpioEntryNum > MAX_VOLTAGE_ENTRIES (32) β the DF-1574/
DF-4003 count family is bounded at this site; only the lookup walk above is
defective.
Verdict
INCONCLUSIVE (hardware-gated / not reachable on the audit guest).
Source-certain: deterministic hang via usSize == 0; OOB reads via
unvalidated usStructureSize walk bound. Severity Medium (crafted-VBIOS
DoS + OOB read at DPM init, hardware-gated).
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.
Fix verification
not_testableFix not testable: single-fix kernel build cannot exercise adv(4)/amdgpu paths without the respective hardware on the guest.
fix.diff authored post-verification from line-accurate source trace (VERDICT.md); env.txt documents absent hardware.
Confirmed kernel references
Detail
Evidence (decisive lines)
VERDICT.md (zero-stride analysis + callers), fix.diff (obj_size floor), env.txt (guest has no AMD GPU).
PoC changes
Seed replaced with an honest hardware-gated trigger sketch/mutator; no runnable PoC exists for this defect class on the audit guest.
Verified recommended fix
Break the lookup walk when usSize < sizeof(ATOM_VOLTAGE_OBJECT_V3) (zero-stride) instead of adding it to offset.
Verdict
INCONCLUSIVE (hardware-gated). amdgpu_atombios_lookup_voltage_object_v3 (amdgpu_atombios.c:1415-1430): usSize==0 on a non-matching voltage object makes offset never advance -> infinite loop at DPM init (callers get_svi2_info :1452, is_voltage_gpio :1494, get_voltage_table :1533); the walk bound usStructureSize is itself an unvalidated BIOS u16, so a large claimed size walks OOB past the BIOS heap allocation. radeon twin named in DF-3741. Note get_voltage_table's ucGpioEntryNum IS correctly clamped (:1539) -- this lookup walk is the unguarded sibling.
No comments yet.