vega20_processpptables: VBIOS powerplay table size not validated against structure footprint -> OOB read
| Field | Value |
|---|---|
| ID | DF-1689 |
| File | sys/dev/drm/amd/powerplay/hwmgr/vega20_processpptables.c |
| Lines | 636, 643, 644, 804, 815, 857, 880, 884, 886 |
| 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 (VBIOS PowerPlay parser count/offset family β DF-1468/1574/1586) |
| Created | 2026-07-18 |
Summary
check_powerplay_tables() only asserts sHeader.structuresize > 0 and
never validates that the VBIOS-reported size covers the full
ATOM_Vega20_POWERPLAYTABLE layout (including the multi-KB inline
PPTable_t smcPPTable).
The parser then unconditionally dereferences fields far past the reported
size β most importantly memcpy-ing sizeof(PPTable_t) bytes from
&powerplay_table->smcPPTable at line 884. A malicious/corrupted VBIOS
that positions the powerplayinfo data table near the end of the BIOS heap
allocation (or simply reports a small structuresize) causes the parser to
read past the end of adev->bios (allocated via kmalloc in
amdgpu_bios.c).
This is the same defect class as the sibling parsers DF-1468
(processpptables.c), DF-1574 (process_pptables_v1_0.c), and DF-1586
(vega10_processpptables.c); vega20 confirms the pattern persists.
Root cause
get_powerplay_table() (lines 44-62) calls smu_atom_get_data_table()
which returns (uint8_t *)atom_context->bios + data_start
(smu_helper.c:669-670) where data_start is a 16-bit offset pulled
verbatim from the VBIOS data_table by amdgpu_atom_parse_data_header
(atom.c:1384-1403) with NO validation against bios_size. The
reported size is stored into hwmgr->soft_pp_table_size but never
compared to sizeof(ATOM_Vega20_POWERPLAYTABLE).
check_powerplay_tables() (lines 636-657) only checks:
format_revision >= 11structuresize > 0smcPPTable.Version == PPTABLE_V20_SMU_VERSION
β all of which can be satisfied by a 16-byte table.
init_powerplay_table_information() then reads fields at fixed offsets
throughout the entire ATOM_Vega20_POWERPLAYTABLE layout:
hwmgr->thermal_controller.fanInfo.ulMaxRPM = powerplay_table->smcPPTable.FanMaximumRpm
(line 815), the OverDrive8Table.ODSettingsMax[] access (line 857), and
crucially the memcpy(pptable_information->smc_pptable,
&(powerplay_table->smcPPTable), sizeof(PPTable_t)) at lines 884-886 which
copies a multi-KB inline structure whose offset+size routinely exceeds any
small structuresize.
Since ATOM_Vega20_POWERPLAYTABLE embeds PPTable_t smcPPTable inline
(vega20_pptable.h:136) rather than via an offset, the parser cannot
avoid reading the full structure footprint regardless of the VBIOS-reported
size.
Threat model
Attacker position: anyone who controls the GPU's VBIOS image β a malicious card in the supply chain, a re-flashed EEPROM (some cards expose VBIOS flash via userspace tools / MMIO), or a virtualization setup passing through a crafted PCI option ROM.
Precondition: amdgpu driver loads and calls vega20_pp_tables_initialize()
(line 894) via vega20_hwmgr_init path.
Impact:
- kernel heap OOB read past the
adev->bioskmallocallocation whendata_start + sizeof(ATOM_Vega20_POWERPLAYTABLE)exceedsbios_size; thememcpyat line 884 is the cleanest OOB primitive since it copies a known large size - The OOB-read bytes (adjacent kernel heap contents) are then exposed to
userspace indirectly via sysfs attributes derived from
pptable_information(clock limits, OD limits,TDPODLimitat line 857) and are also sent to the SMU microcontroller as configuration, which can cause GPU hardware misbehavior (over-volt/over-clock)
No direct path to kernel code execution was demonstrated from this file alone β impact is bounded info leak of adjacent heap + hardware-config corruption.
PoC
PoC is a VBIOS image patch, not a userspace program, because the attack surface is firmware-supplied data consumed at driver init. Steps:
- Obtain a Vega20 VBIOS image (e.g. dump from a Radeon VII / MI50 via
amdgpu.gpu_recovery=0+ MMIO strap, or extract the PCI option ROM withpci-tools). - Locate the ATOM master
data_table; find the index forpowerplayinfo(viaGetIndexIntoMasterDataTable(powerplayinfo)β same lookup the driver uses at line 46). - Patch that data table entry's
data_startto a 16-bit value nearbios_size(e.g.bios_size - 32), OR set thestructuresizefield at thedata_startoffset to 1, while keepingformat_revision=11(ATOM_VEGA20_TABLE_REVISION_VEGA20),content_revisionto pass, and the byte atsmcPPTable.Versionoffset =PPTABLE_V20_SMU_VERSION(socheck_powerplay_tableslines 640-652 still pass). - Reprogram the SPI EEPROM with the modified image, OR feed it to a VM
via
pci-passthroughof an emulated option ROM. - Trigger driver (re-)bind:
echo > /sys/bus/pci/drivers/amdgpu/bindor boot.
Expected outcome: amdgpu calls vega20_pp_tables_initialize β
init_powerplay_table_information β the memcpy at line 884 reads
sizeof(PPTable_t) (~2 KB) bytes starting near the end of the BIOS
allocation, going past bios_size into adjacent kernel heap.
A KASAN-enabled kernel reports a slab-out-of-bounds read; on a production
kernel the read silently returns adjacent heap bytes that then appear in
sysfs (e.g. /sys/class/drm/card0/device/... pp_dpm_sclk / OD settings)
and are pushed to the SMU.
findings/poc/DF-1689/: drop the patched VBIOS as
vega20_poisoned_vbios.bin plus a Python script patch_vbios.py that
takes a stock Vega20 VBIOS dump and writes the patched image with the
data_start/structuresize fields set as described; include the binding
trigger script trigger.sh.
Recommended fix
Validate that the VBIOS-reported size actually covers the full
ATOM_Vega20_POWERPLAYTABLE structure before any field is read, and
additionally guard against data_start running past bios_size.
Minimal in-file fix:
--- a/sys/dev/drm/amd/powerplay/hwmgr/vega20_processpptables.c
+++ b/sys/dev/drm/amd/powerplay/hwmgr/vega20_processpptables.c
@@ -636,11 +636,18 @@ static int check_powerplay_tables(
struct pp_hwmgr *hwmgr,
const ATOM_Vega20_POWERPLAYTABLE *powerplay_table)
{
+ u16 struct_size = le16_to_cpu(powerplay_table->sHeader.structuresize);
+
PP_ASSERT_WITH_CODE((powerplay_table->sHeader.format_revision >=
ATOM_VEGA20_TABLE_REVISION_VEGA20),
"Unsupported PPTable format!", return -1);
PP_ASSERT_WITH_CODE(powerplay_table->sHeader.structuresize > 0,
"Invalid PowerPlay Table!", return -1);
+ PP_ASSERT_WITH_CODE(struct_size >= sizeof(ATOM_Vega20_POWERPLAYTABLE),
+ "PowerPlay Table size too small for vega20 layout!",
+ return -EINVAL);
+ PP_ASSERT_WITH_CODE(hwmgr->soft_pp_table_size == 0 ||
+ hwmgr->soft_pp_table_size >= sizeof(ATOM_Vega20_POWERPLAYTABLE),
+ "PowerPlay Table allocation too small!", return -EINVAL);
if (powerplay_table->smcPPTable.Version != PPTABLE_V20_SMU_VERSION) {
pr_info("Unmatch PPTable version: "
Defense-in-depth: amdgpu_atom_parse_data_header() in
sys/dev/drm/amd/amdgpu/atom.c:1384 should also reject data_start
values where data_start + size > ctx->bios_size, but that fix belongs to
atom.c, not this file.
Related findings
VBIOS PowerPlay parser count/offset family:
- DF-1468 (processpptables.c)
- DF-1574 (process_pptables_v1_0.c)
- DF-1586 (vega10_processpptables.c)
- DF-1560 (amd_powerplay.c)
- DF-1674 (amdgpu_dpm.c)
Related sibling
- DF-1690 (
smc_dpm_infotable β same file, separate OOB)
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-1689 Β· 4 files| File | Type | Description | Size | |
|---|---|---|---|---|
| VERDICT.md | verdict | source-only confirmation + mechanism + fix | 1.7 KB | β raw |
| fix.diff | suggested-fix | Add 'structuresize >= sizeof(ATOM_Vega20_POWERPLAYTABLE)' assertion. | 714 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-1689 β PoC Verification Verdict
Category: amdgpu powerplay (module, HW-gated)
Source: sys/dev/drm/amd/powerplay/hwmgr/vega20_processpptables.c:636-657
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
check_powerplay_tables only asserts structuresize>0 (643) and smcPPTable.Version==PPTABLE_V20_SMU_VERSION (646). NO check that structuresize covers the full ATOM_Vega20_POWERPLAYTABLE layout (which includes multi-KB inline PPTable_t smcPPTable). A VBIOS with a tiny structuresize but matching version passes validation; subsequent field accesses read OOB.
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
Add 'structuresize >= sizeof(ATOM_Vega20_POWERPLAYTABLE)' assertion.
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): check_powerplay_tables only asserts structuresize>0 and version match; NO check that structuresize covers full ATOM_Vega20_POWERPLAYTABLE layout; multi-KB images used unbound
Verified recommended fix
REPRODUCED (source-only): check_powerplay_tables only asserts structuresize>0 and version match; NO check that structuresize covers full ATOM_Vega20_POWERPLAYTABLE layout; multi-KB images used unbounded.
Verdict
REPRODUCED (source-only): check_powerplay_tables only asserts structuresize>0 and version match; NO check that structuresize covers full ATOM_Vega20_POWERPLAYTABLE layout; multi-KB images used unbounded.
No comments yet.