β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-4154

amdgpu_atombios_init_mc_reg_table: zero-stride reg_data walk never terminates; unbounded block walk and unvalidated offsets read past the VBIOS buffer (amdgpu twin of DF-3741)

Summary

In the VRAM_Info v2_1 parser, the block walk advances reg_data by BIOS u16 usRegDataBlkSize; when the stride is 0 and the mem-id never matches, the only exits never fire - deterministic infinite loop at DPM init. With a small non-zero stride and no END marker the walk marches past the kmalloced BIOS image (inner loop reads up to 32 dwords per block) until a lucky 0xFFFFFFFF or a fault. usMemClkPatchTblOffset/usRegIndexTblSize dereferenced unvalidated. Write side correctly clamped. Reject-zero-stride + table_end fix.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-4154 Β· 4 files
FileTypeDescriptionSize
vbios_mutator.py trigger Illustrative trigger (hardware-gated) 1.5 KB view raw
VERDICT.md verdict Full source-trace analysis 3.0 KB ↓ raw
fix.diff suggested-fix Reject usRegDataBlkSize==0 and bound every reg_data step (and the inner j reads) by the declared VRAM_Info table end. 2.3 KB view raw
env.txt environment Guest uname + pciconf hardware-gate proof 980 B view raw
VERDICT.md verdict Full source-trace analysis
↓ download raw

DF-4154 β€” amdgpu_atombios_init_mc_reg_table: zero-stride block walk never terminates; unbounded walk + unvalidated offsets read past the VBIOS buffer (amdgpu twin of DF-3741)

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_init_mc_reg_table, lines 1583-1672)

Called from the DPM init paths (si/kv/ci) with a module_index; parses VRAM_Info v2_1:

  1. Unvalidated derefs: reg_block = (u8*)vram_info + usMemClkPatchTblOffset (:1607-1609) and reg_data = reg_block + 4 + usRegIndexTblSize (:1610-1613) β€” both u16 BIOS values used as pointer offsets from a table whose extent is never validated (DF-4104 enabler) β†’ OOB reads before the loop even starts. num_entries is (correctly) clamped to VBIOS_MC_REGISTER_ARRAY_SIZE (32) at :1617-1618, and the destination arrays (mc_reg_address[32], mc_reg_table_entry[20]) are respected β€” the write side is fine; it is the walk that is unbounded.

  2. Zero-stride infinite loop (:1631-1653):

while ((le32_to_cpu(*(u32 *)reg_data) != END_OF_REG_DATA_BLOCK) &&
       (num_ranges < VBIOS_MAX_AC_TIMING_ENTRIES)) {
        t_mem_id = (*(u32 *)reg_data & MEM_ID_MASK) >> MEM_ID_SHIFT;
        if (module_index == t_mem_id) { ...; num_ranges++; }
        reg_data += le16_to_cpu(reg_block->usRegDataBlkSize);   /* may be 0 */
}

The only exits are reading END_OF_REG_DATA_BLOCK (0xFFFFFFFF) or num_ranges == 20. num_ranges increments only when the block's mem id matches. Crafted usRegDataBlkSize == 0 + first dword != 0xFFFFFFFF + t_mem_id != module_index β‡’ the loop spins forever on the same word β†’ kernel hang at DPM init (uninterruptible, IRQ-context-free but boot-path/ bind-path wedge). Identical shape to radeon's radeon_atom_init_mc_reg_table in DF-3741.

  1. Unbounded OOB read walk: with a small non-zero stride and no END marker inside the buffer, reg_data marches past the kmalloced BIOS image reading *(u32*)reg_data each step (and the inner :1639-1648 loop reads ((u32*)reg_data + j) for j < reg_table->last, ≀ 32 extra words per block) until it happens to hit 0xFFFFFFFF in adjacent heap or faults. Values land in reg_table->mc_reg_table_entry[] and are later programmed into MC registers.

Verdict

INCONCLUSIVE (hardware-gated / not reachable on the audit guest). Source-certain: (a) deterministic infinite loop on usRegDataBlkSize == 0, (b) unbounded OOB reads of the kernel heap past the BIOS allocation, from a crafted VBIOS at driver bind/DPM init. Severity Medium (DoS + OOB read, 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_testable
baseline no→ patch + rebuild →patched clean

Fix 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.
↓ fix.diffper-fix-DF-4154

Confirmed kernel references

Detail

Evidence (decisive lines)

VERDICT.md (loop-exit analysis), fix.diff (stride+extent checks), 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

Reject usRegDataBlkSize==0 and bound every reg_data step (and the inner j reads) by the declared VRAM_Info table end.

Verdict

INCONCLUSIVE (hardware-gated). amdgpu_atombios_init_mc_reg_table (amdgpu_atombios.c:1583-1672): (a) usRegDataBlkSize==0 with non-matching t_mem_id makes the block walk at :1631-1653 spin forever (num_ranges never increments, no other exit) -> kernel hang at DPM init; (b) the walk is bounded by nothing but a lucky 0xFFFFFFFF read -- unbounded OOB heap reads past the BIOS allocation (inner loop reads ((u32*)reg_data)+j too); (c) usMemClkPatchTblOffset/usRegIndexTblSize derefs unvalidated. amdgpu twin of DF-3741. Write side is properly clamped (:1617).