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:
- Dump a Vega20 VBIOS image.
- In the ATOM master
data_table, find the index returned byGetIndexIntoMasterDataTable(smc_dpm_info)(the same index the driver uses at line 717). - Patch that entry's
data_starttobios_size - 16(small offset near the end), keeping the master data_table entry non-zero soamdgpu_atom_parse_data_headerreturns true. - Reprogram SPI EEPROM or feed via VM
pci-passthrough. - 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.
Recommended fix
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.
Related findings
VBIOS PowerPlay parser count/offset family:
- DF-1689 (sibling:
powerplayinfotable OOB in same file) - DF-1468/1574/1586 (other VBIOS PowerPlay parsers)
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-1690 Β· 4 files| File | Type | Description | Size | |
|---|---|---|---|---|
| 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 |
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
fixedVALIDATED: 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.
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.
No comments yet.