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

VRAM_Info MC-register-table path: unvalidated VBIOS NULL/offset/stride β€” NULL-deref panic, OOB read walk, and stride-0 infinite loop at attach

Summary

atomctrl_initialize_mc_reg_table dereferences the VRAM_Info table pointer without a NULL check, then feeds two walkers whose geometry is entirely VBIOS-controlled and never validated: an out-of-range offset starts an OOB read walk whose stride is attacker-chosen, and a zero stride with a non-matching tmem_id is a non-preemptible infinite loop in the kldload/attach path. Crafted VBIOS on CI/Iceland/Tonga: panic, hard hang, or OOB-read values programmed into memory-controller registers. NULL-check + validate-offset/stride fix.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-4288 Β· 6 files
FileTypeDescriptionSize
README.md β€” 3.9 KB ↓ raw
VERDICT.md β€” 4.1 KB ↓ raw
fix.diff β€” 2.0 KB view raw
manifest.json β€” 1014 B view raw
verdict.json β€” 3.3 KB view raw
env.txt β€” 962 B view raw

DF-4288 β€” VRAM_Info/MC-register-table path: missing NULL check + fully unvalidated VBIOS offsets/strides (OOB read walk, stride-0 infinite loop)

Pass-2 finding for sys/dev/drm/amd/powerplay/hwmgr/ppatomctrl.c. Not the known DF-1496/1497/1498 (those cover atomctrl_get_smc_sclk_range_table count overflow, atomctrl_get_svi2_info lookup NULL, and the ASIC_ProfilingInfo leakage arrays). This pack covers the VRAM_Info β†’ MC-register-table path that was not audited in pass 1.

The bug (three defects, one path)

atomctrl_initialize_mc_reg_table() (ppatomctrl.c:133-168) fetches the VBIOS VRAM_Info data table and feeds it to two walkers:

  1. Missing NULL check β€” vram_info is dereferenced at ppatomctrl.c:148 (vram_info->ucNumOfVRAMModule) with no NULL check. smu_atom_get_data_table() (smu_helper.c:660-673) returns NULL when the master data table has no VRAM_Info entry (or the master table itself is malformed such that amdgpu_atom_parse_data_header fails, amdgpu/atom.c:1384-1403 β€” which performs no validation at all). Callers ci_smumgr.c:2708, iceland_smumgr.c:2638, tonga_smumgr.c:3120 run this during SMU backend init at driver attach. β†’ NULL-deref kernel panic at boot/module-load with a VBIOS that omits VRAM_Info.

  2. Unvalidated usMemClkPatchTblOffset β€” ppatomctrl.c:157-158 computes reg_block = vram_info + le16(usMemClkPatchTblOffset) with a raw VBIOS USHORT, never checked against the table size returned by smu_atom_get_data_table. atomctrl_set_mc_reg_address_table (ppatomctrl.c:102-131) then walks ATOM_INIT_REG_INDEX_FORMAT entries from that unchecked base (format walk at :117-127 reads ucPreRegDataLength each step), and atomctrl_retrieve_ac_timing computes reg_data = reg_block + 4 + usRegIndexTblSize (ppatomctrl.c:55-56) β€” usRegIndexTblSize likewise unvalidated. β†’ arbitrary-offset OOB reads of kernel memory past the BIOS image (ctx->bios buffer, amdgpu/atom.c), values copied into table->mc_reg_table_entry[].mc_data which the caller later programs into memory-controller registers.

  3. Unbounded reg_data walk, attacker stride, stride 0 = infinite loop β€” atomctrl_retrieve_ac_timing (ppatomctrl.c:60-86) loops while (*(uint32_t *)reg_data != END_OF_REG_DATA_BLOCK && num_ranges < 20), advancing reg_data += le16(usRegDataBlkSize) (:84-85). The loop bound num_ranges only increments when the module index matches tmem_id (:64). A VBIOS with usRegDataBlkSize == 0 and a non-matching tmem_id in the first word loops forever (same address, no progress, no lock drop) β€” hard hang of the kldload/attach thread. A nonzero stride with no END marker (0xFFFFFFFF word) walks past the BIOS image indefinitely until it happens to find one (OOB read of arbitrary length). The final PP_ASSERT at :88 also dereferences the walked-off pointer.

Threat model

Crafted VBIOS on an AMD dGPU (malicious/exploited GPU firmware or EEPROM reflash) β€” the standard powerplay threat model of this audit (same as DF-1496/1497/1498). Boot-time/module-load kernel DoS: NULL-deref panic, unbounded OOB read, or non-preemptible infinite loop in the DRM attach path.

Reproduce

Not runnable on this audit guest: it has no AMD dGPU (QEMU exposes no AMD GPU model; no drm modules load β€” see env.txt), so the powerplay attach path cannot be entered. Verification is by exhaustive code trace (VERDICT.md + trace.md). On hardware (or with a VBIOSimage fuzz harness driving amdgpu_atom_parse_data_header directly) the trigger is: VRAM_Info table absent (defect 1), or usMemClkPatchTblOffset/usRegIndexTblSize beyond table end (defect 2), or usRegDataBlkSize=0 with mismatched ucMemClkPatchTbl tmem_id (defect 3).

Expected: Fatal trap 12 NULL deref (defect 1), garbage MC register programming / eventual fault (defect 2), or attach thread spinning at 100% forever (defect 3).

VERDICT.md
↓ download raw

DF-4288 VERDICT β€” VRAM_Info path unvalidated geometry

Status: inconclusive (code-trace certain; not runnable on guest β€” no AMD dGPU)

What was verified

Full read of sys/dev/drm/amd/powerplay/hwmgr/ppatomctrl.c (1563 lines) and every supporting definition/caller:

  • smu_atom_get_data_table() β€” sys/dev/drm/amd/powerplay/hwmgr/smu_helper.c:660-673: returns NULL whenever amdgpu_atom_parse_data_header() fails; the atom parser (sys/dev/drm/amd/amdgpu/atom.c:1384-1403) performs zero bounds validation β€” it trusts the VBIOS master-table offset (idx = CU16(...)) and returns bios + data_start directly. Both *size and any inner usStructureSize are raw VBIOS values.
  • atomctrl_initialize_mc_reg_table() β€” ppatomctrl.c:144-168:
  • :146 smu_atom_get_data_table(...VRAM_Info...) β†’ result stored in vram_info
  • :148 vram_info->ucNumOfVRAMModule dereferenced with no NULL check (contrast the guarded pattern used elsewhere in the same file, e.g. :494 if (fw_info == NULL) and :614 PP_ASSERT_WITH_CODE(NULL != table_address...)).
  • :157-158 reg_block = (uint8_t *)vram_info + le16_to_cpu(vram_info->usMemClkPatchTblOffset) β€” USHORT offset, never validated against the table size.
  • atomctrl_set_mc_reg_address_table() β€” ppatomctrl.c:102-131: walks ATOM_INIT_REG_INDEX_FORMAT entries from the unchecked reg_block base; loop bound num_entries = usRegIndexTblSize/sizeof(entry) (:107-108) is VBIOS-controlled; only guarded against exceeding the destination array (:113 num_entries <= VBIOS_MC_REGISTER_ARRAY_SIZE(32)), not against reading past the source table. (Note :111 num_entries-- wraps 0β†’255 for usRegIndexTblSize < sizeof(entry), but 255 > 32 so the assert catches it β€” no bug there.)
  • atomctrl_retrieve_ac_timing() β€” ppatomctrl.c:48-93:
  • :55-56 reg_data = reg_block + 4 + le16(usRegIndexTblSize) β€” second unvalidated offset.
  • :60-86 walk: exit conditions are only the 0xFFFFFFFF sentinel word and num_ranges < VBIOS_MAX_AC_TIMING_ENTRIES(20); num_ranges increments only on tmem_id match (:64-81). usRegDataBlkSize == 0 + non-matching index β‡’ infinite loop (same address re-read forever, non-preemptible kldload context). Non-matching index + any stride β‡’ walk continues past the BIOS image until a 0xFFFFFFFF word happens to appear (unbounded OOB read); :88 dereferences the final pointer again.
  • callers: ci_smumgr.c:2708, iceland_smumgr.c:2638, tonga_smumgr.c:3120 (*_initialize_mc_reg_table during SMU backend init at attach).
  • Destination-array bounds verified safe (so the bug is strictly an OOB read/hang, not a write): mc_reg_address[32] written for i < num_entries ≀ 32 (ppatomctrl.h:251); mc_reg_table_entry[20] indexed by num_ranges < 20 (ppatomctrl.h:250); mc_data[i-1] at i==0 (ppatomctrl.c:77-78) aliases mclk_max within the same struct (ppatomctrl.h:219-222) β€” in-bounds.

Why inconclusive (not not_reproduced)

The DragonFly QEMU guest used by this audit has no AMD dGPU (QEMU ships no AMD GPU model; kldstat shows no drm modules β€” env.txt). The powerplay attach path β€” the only reachable route into atomctrl_initialize_mc_reg_table β€” cannot be entered, so no live PoC is possible in this environment. The defects themselves are proven by trace; impact is boot/module-load kernel panic (CWE-476), unbounded OOB read (CWE-125), or infinite-loop hang (CWE-835) under a crafted VBIOS, i.e. the same threat model and severity band as DF-1498 (Medium).

Exploit chain

None β€” this is a read-only/hang primitive in the DRM attach path, gated on attacker-controlled GPU firmware (physical supply-chain or malicious-reflash threat). No unprivileged-local or remote path exists to this code.

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

Baseline behavior not reproducible on this guest (no AMD dGPU) -> no fixed-kernel build possible. fix.diff authored from trace.

findings/poc/DF-4288/fix.diff (not applied to sys/; authored post-verification from line-accurate trace)
↓ fix.diffper-fix-DF-4288

Confirmed kernel references

Detail

Evidence (decisive lines)

VERDICT.md (line-by-line trace); README.md (defect summary + trigger shapes); fix.diff (NULL check + offset bound + stride/table bound); env.txt (guest lacks AMD GPU)

PoC changes

n/a β€” no PoC possible on this guest (no AMD dGPU); code-trace evidence pack instead

Verified recommended fix

NULL-check vram_info; validate usMemClkPatchTblOffset/usRegIndexTblSize/usRegDataBlkSize against the data-table size; reject usRegDataBlkSize < 4

Verdict

Three defects proven by code trace in the VRAM_Info -> MC-reg-table path of ppatomctrl.c: (1) vram_info from smu_atom_get_data_table is dereferenced at ppatomctrl.c:148 without a NULL check (helper returns NULL when the VBIOS master data table lacks VRAM_Info; the atom parser at amdgpu/atom.c:1384-1403 does no validation); (2) usMemClkPatchTblOffset (ppatomctrl.c:157) and usRegIndexTblSize (:55) are raw VBIOS USHORTs used as read offsets with no bound against the table size -> OOB read walk whose results are programmed into memory-controller registers; (3) atomctrl_retrieve_ac_timing's walk (:60-86) advances by VBIOS-controlled usRegDataBlkSize with num_ranges only incrementing on tmem_id match, so usRegDataBlkSize==0 with a non-matching index is a non-preemptible infinite loop in the kldload/attach path (CWE-835), and a missing 0xFFFFFFFF sentinel walks past the BIOS image unbounded (CWE-125). Impact is boot/module-load kernel panic or hang under a crafted VBIOS β€” same threat model and Medium band as the known DF-1498. NOT runnable on this audit guest: it has no AMD dGPU (QEMU has no AMD GPU model; no drm module loads β€” env.txt), so the powerplay attach path cannot be entered; verification is by exhaustive trace (VERDICT.md). Destination arrays verified in-bounds, so no write primitive: mc_reg_address[32] indexed < 32 (ppatomctrl.h:251), mc_reg_table_entry[20] indexed < 20 (:250), mc_data[i-1] at i==0 aliases mclk_max in-struct (ppatomctrl.h:219-222).