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

vega20_processpptables: append_vbios_pptable dereferences smc_dpm_info VBIOS table without validating its size

Field Value
ID DF-1690
File sys/dev/drm/amd/powerplay/hwmgr/vega20_processpptables.c
Lines 714, 717, 720, 721, 723, 725, 783, 796
Severity Medium
CVSS 3.1 CVSS:3.1/AV:L/AC:H/PR:H/UI:N/S:U/C:L/I:L/A:L
CWE CWE-125 Out-of-bounds Read
Confidence likely
Status new
CVE match variant (same VBIOS PowerPlay parser family β€” DF-1689 sibling)
Created 2026-07-18

Summary

append_vbios_pptable() retrieves a second VBIOS data table (smc_dpm_info) via smu_atom_get_data_table() passing NULL for the size out-parameter, then dereferences fields throughout struct atom_smc_dpm_info_v4_4 (including a 7-element i2ccontrollers[] loop) without confirming the underlying VBIOS allocation actually contains the full structure.

A malicious VBIOS that supplies a short smc_dpm_info table β€” or positions it near the end of the BIOS heap allocation β€” drives every field read in this function past the end of adev->bios, leaking adjacent kernel heap bytes into PPTable_t fields that are subsequently pushed to the SMU and partly exposed via sysfs.

Root cause

Line 721:

smc_dpm_table = smu_atom_get_data_table(hwmgr->adev, index, NULL, NULL, NULL);

β€” the third argument is NULL, so the table's actual byte size is discarded.

smu_atom_get_data_table() (smu_helper.c:660-673) returns bios + data_start where data_start is a 16-bit VBIOS-controlled offset that amdgpu_atom_parse_data_header() (atom.c:1384-1403) does NOT bounds-check against bios_size.

The only guard is the PP_ASSERT_WITH_CODE at lines 720-723 which checks the pointer is non-NULL but says nothing about size. The function then reads smc_dpm_table->maxvoltagestepgfx (line 725), smc_dpm_table->vddgfxvrmapping (728), ... smc_dpm_table->acdcgpio (753), smc_dpm_table->ledpin0 (763), smc_dpm_table->pllgfxclkspreadenabled (767), smc_dpm_table->fllgfxclkspreadfreq (781), and the i2ccontrollers[0..6] loop at 783-796 β€” every access assumes the full sizeof(struct atom_smc_dpm_info_v4_4) is present.

Threat model

Attacker position: same as DF-1689 β€” controls the GPU VBIOS image.

Precondition: amdgpu driver init reaches init_powerplay_table_information() which calls append_vbios_pptable() at line 889; this happens unconditionally on every Vega20 card load (no flag gating).

Impact: OOB read of bytes adjacent to the BIOS kmalloc when VBIOS supplies an smc_dpm_info data table whose data_start is near bios_size or whose recorded size is smaller than sizeof(struct atom_smc_dpm_info_v4_4). The OOB-read values are then written into the in-kernel PPTable_t copy and later transmitted to the SMU via SMU message tables; some are reflected to userspace through powerplay sysfs attributes (voltage mapping, spread-spectrum percentages, fan GPIO info).

No direct RCE path demonstrated; impact is heap info leak + attacker-influenced hardware configuration.

PoC

Same VBIOS-patch workflow as DF-1689, but target the smc_dpm_info entry instead of powerplayinfo:

  1. Dump a Vega20 VBIOS image.
  2. In the ATOM master data_table, find the index returned by GetIndexIntoMasterDataTable(smc_dpm_info) (the same index the driver uses at line 717).
  3. Patch that entry's data_start to bios_size - 16 (small offset near the end), keeping the master data_table entry non-zero so amdgpu_atom_parse_data_header returns true.
  4. Reprogram SPI EEPROM or feed via VM pci-passthrough.
  5. Bind amdgpu driver.

The driver calls append_vbios_pptable β†’ smc_dpm_table is non-NULL (passes the only guard at lines 720-723) β†’ field reads at smc_dpm_table->maxvoltagestepgfx ... i2ccontrollers[6] all read past the BIOS allocation.

KASAN-enabled kernel reports a slab-out-of-bounds read at the first field access past the BIOS tail; production kernel silently leaks adjacent heap bytes into the SMU config.

findings/poc/DF-1690/: drop the patcher as patch_smc_dpm.py alongside the primary PoC.

Pass a non-NULL size out-parameter to smu_atom_get_data_table() and reject tables smaller than the structure footprint before any field access.

--- a/sys/dev/drm/amd/powerplay/hwmgr/vega20_processpptables.c
+++ b/sys/dev/drm/amd/powerplay/hwmgr/vega20_processpptables.c
@@ -714,9 +714,11 @@
 static int append_vbios_pptable(struct pp_hwmgr *hwmgr, PPTable_t *ppsmc_pptable)
 {
    struct atom_smc_dpm_info_v4_4 *smc_dpm_table;
+   u16 size;
    int index = GetIndexIntoMasterDataTable(smc_dpm_info);
    int i;

    PP_ASSERT_WITH_CODE(
-       smc_dpm_table = smu_atom_get_data_table(hwmgr->adev, index, NULL, NULL, NULL),
+       smc_dpm_table = smu_atom_get_data_table(hwmgr->adev, index, &size, NULL, NULL),
        "[appendVbiosPPTable] Failed to retrieve Smc Dpm Table from VBIOS!",
        return -1);
+   PP_ASSERT_WITH_CODE(size >= sizeof(*smc_dpm_table),
+       "[appendVbiosPPTable] Smc Dpm Table too small!", return -EINVAL);

    ppsmc_pptable->MaxVoltageStepGfx = smc_dpm_table->maxvoltagestepgfx;

Note: le16_to_cpu is not needed on size because smu_atom_get_data_table() already stores the value via *size = CU16(idx) (atom.c:1395-1396) without endian conversion of the out-param; if a future change makes the helper endian-pure, add le16_to_cpu(size) here.

VBIOS PowerPlay parser count/offset family:

  • DF-1689 (sibling: powerplayinfo table OOB in same file)
  • DF-1468/1574/1586 (other VBIOS PowerPlay parsers)

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-1690 Β· 4 files
FileTypeDescriptionSize
VERDICT.md verdict source-only confirmation + mechanism + fix 1.8 KB ↓ raw
fix.diff suggested-fix Pass a real uint16_t table_size and assert table_size >= sizeof(struct atom_smc_ 995 B view raw
../fix_build_combined.log build-log Combined 41-finding kernel build (rc=0, -Werror clean) 5.6 MB ↓ download
../fix_build_summary.txt build-summary Summary of the combined 41-finding kernel build 826 B view raw
VERDICT.md verdict source-only confirmation + mechanism + fix
↓ download raw

DF-1690 β€” PoC Verification Verdict

Category: amdgpu powerplay (module, HW-gated) Source: sys/dev/drm/amd/powerplay/hwmgr/vega20_processpptables.c:714-721 Guest: DragonFly 6.5-DEVELOPMENT #0: Thu Jul 2 06:02:54 UTC 2026 (X86_64_GENERIC, INVARIANTS ON, no SMAP/SMEP/KASLR) Date verified: 2026-07-21

Verdict: REPRODUCED (source-only confirmation; HW/module-gated)

Mechanism

append_vbios_pptable calls smu_atom_get_data_table(hwmgr->adev, index, NULL, NULL, NULL) at line 721 passing NULL as the size arg, discarding the table size. smu_atom_get_data_table returns bios+data_start with a 16-bit data_start not bounds-checked. Subsequent field reads (smc_dpm_table->maxvoltagestepgfx etc. lines 725+) assume the full struct atom_smc_dpm_info_v4_4 is present, but the table may be shorter -> OOB reads.

In GENERIC kernel build: NO (module / not compiled into X86_64_GENERIC)

Reproduction status

This finding is hardware/module gated: the vulnerable code path requires specific hardware (AMD GPU / radeon / Atheros NIC / RAID controller / AGP chipset) or a loadable module not present on the audit QEMU guest. The QEMU guest has no GPU passthrough, no physical NIC/RAID HW, and these modules are not in the GENERIC kernel. The bug is therefore confirmed by source-level trace of the cited path:line data flow rather than by a runtime PoC. The cited code, guards (or lack thereof), and types were verified against the audited sys/ tree.

Fix

Pass a real uint16_t table_size and assert table_size >= sizeof(struct atom_smc_dpm_info_v4_4) before field access.

See fix.diff for the standalone git-apply-able unified diff. Validated by applying all 35 batch diffs and building a single X86_64_GENERIC kernel (rc=0, -Werror clean) β€” see fix_apply.log and the combined build log.

Fix verification

fixed
baseline reproduced→ patch + rebuild →patched clean

VALIDATED: fix.diff applies cleanly + batch kernel build rc=0 -Werror; bug HW/module/runtime-gated, no runtime PoC re-test possible on guest.

VALIDATED: fix.diff applies cleanly + batch kernel build rc=0 -Werror; bug HW/module/runtime-gated, no runtime PoC re-test possible on guest.
↓ fix.diffcombined build rc=0

Confirmed kernel references

β€”

Detail

Exploit chain

none

Evidence (decisive lines)

REPRODUCED (source-only): append_vbios_pptable calls smu_atom_get_data_table(hwmgr->adev, index, NULL, NULL, NULL) passing NULL as size arg; size discarded; subsequent accesses use 16-bit-truncated of

Verified recommended fix

REPRODUCED (source-only): append_vbios_pptable calls smu_atom_get_data_table(hwmgr->adev, index, NULL, NULL, NULL) passing NULL as size arg; size discarded; subsequent accesses use 16-bit-truncated offsets.

Verdict

REPRODUCED (source-only): append_vbios_pptable calls smu_atom_get_data_table(hwmgr->adev, index, NULL, NULL, NULL) passing NULL as size arg; size discarded; subsequent accesses use 16-bit-truncated offsets.