NULL pointer dereference of tdp_table from VBIOS PowerPlay table missing usPowerTuneTableOffset
- File:
sys/dev/drm/amd/powerplay/hwmgr/vega10_powertune.c - Lines: 1294, 1297, 1344, 1348
- Severity: Low
- CVSS:
CVSS:3.1/AV:L/AC:H/PR:H/UI:N/S:U:C:N/I:N/A:H - CWE: CWE-476 NULL Pointer Dereference
- Confidence: likely
Summary
vega10_initialize_power_tune_defaults() and vega10_enable_power_containment()
unconditionally dereference table_info->tdp_table, but the pointer is only
populated by the VBIOS parser when
powerplay_table->usPowerTuneTableOffset is non-zero.
A Vega10 VBIOS PowerPlay table that passes check_powerplay_tables() yet has
usPowerTuneTableOffset == 0 leaves tdp_table == NULL, triggering a kernel
page-fault panic in amdgpu attach (boot or hotplug).
Root cause
vega10_initialize_power_tune_defaults()
(sys/dev/drm/amd/powerplay/hwmgr/vega10_powertune.c:1289-1326) fetches
struct phm_tdp_table *tdp_table = table_info->tdp_table;
(line 1294) and immediately dereferences it at line 1297
(tdp_table->usMaximumPowerDeliveryLimit) with no NULL guard.
The same pattern repeats in vega10_enable_power_containment() at lines 1344
(struct phm_tdp_table *tdp_table = table_info->tdp_table;) and 1348
(tdp_table->usMaximumPowerDeliveryLimit).
The pointer originates in
sys/dev/drm/amd/powerplay/hwmgr/vega10_processpptables.c:921-924:
if (!result && powerplay_table->usPowerTuneTableOffset)
result = get_tdp_table(hwmgr, &pp_table_info->tdp_table, power_tune_table);
The parent struct phm_ppt_v2_information is kzalloc'd at
vega10_processpptables.c:1139 so tdp_table defaults to NULL; the conditional
means it stays NULL when usPowerTuneTableOffset == 0.
check_powerplay_tables() (vega10_processpptables.c:66-86) validates only
sHeader.format_revision, usStateArrayOffset, sHeader.structuresize, and
state_arrays->ucNumEntries β never usPowerTuneTableOffset.
init_dpm_2_parameters() therefore returns 0 (success),
vega10_pp_tables_initialize() returns 0, and hwmgr_hw_init() proceeds to call
hwmgr_func->backend_init (sys/dev/drm/amd/powerplay/hwmgr/hwmgr.c:229),
which at vega10_hwmgr.c:864 calls vega10_init_dpm_defaults β
vega10_initialize_power_tune_defaults β NULL deref.
The first faulting instruction is the le16-to-cpu read at
vega10_powertune.c:1297.
Threat
Attacker must control the Vega10-class GPU VBIOS image (Radeon RX Vega 56/64, Vega Frontier Edition, MI25 Instinct).
Realistic vectors:
- (a) PCI passthrough / VFIO in a cloud or local-VM setup where the host or guest
kernel parses a
romfilesupplied by the attacker; - (b) a supply-chain-modified or re-flashed physical card (
flashrom -wrequires root, but no kernel priv is needed to trigger the panic once the modified ROM is presented); - (c) a separate write-privilege exploit used first to reflash the ROM.
No code execution on the target is required beyond presenting the tampered VBIOS β the panic fires in amdgpu attach at boot or hotplug.
Impact is local kernel panic / denial of service (system hang or spontaneous reboot).
No memory-safety primitive beyond the NULL-page fault is exposed (page 0 is unmapped on DragonFlyBSD/amd64), so no escalation path was identified.
Exploit / PoC
Reproduce on a host with a Vega10 GPU, or under QEMU+vfio-pci with a romfile.
- Dump a known-good Vega10 VBIOS:
cd /sys/bus/pci/devices/<bdf>
echo 1 > rom
cat rom > /tmp/vbios.rom
echo 0 > rom
-
Locate the
ATOM_Vega10_POWERPLAYTABLEinside the ROM (PowerPlay signature'ATPPL'). Within it, zero out the 16-bit little-endian fieldusPowerTuneTableOffset. LeaveusStateArrayOffset,sHeader.format_revision(>=ATOM_Vega10_TABLE_REVISION_VEGA10),sHeader.structuresize(>0), and the state-arrayucNumEntries(>0) intact socheck_powerplay_tablesstill returns 0. -
For VFIO: place the patched ROM at
/etc/qemu/vga-vega10.romand addromfile=/etc/qemu/vga-vega10.romto the host-device vfio-pci,host=...line, then boot a DragonFlyBSD guest that loadsamdgpu.
For bare metal: flashrom -w vbios_patched.rom and reboot.
- Success criterion: kernel panics during amdgpu attach with a NULL-page fault
trace pinning
vega10_initialize_power_tune_defaults(line 1297) or, ifbackend_initsomehow skipsinit_dpm_defaults,vega10_enable_power_containment(line 1348).
Capture dmesg -a and the panic backtrace as evidence.
Recommended fix
Defensive guard at the consumer site (preferred minimal local fix), plus a
parser-level guard so other consumers of tdp_table are protected too.
Local fix in sys/dev/drm/amd/powerplay/hwmgr/vega10_powertune.c:
--- a/sys/dev/drm/amd/powerplay/hwmgr/vega10_powertune.c
+++ b/sys/dev/drm/amd/powerplay/hwmgr/vega10_powertune.c
@@ -1291,9 +1291,15 @@ void vega10_initialize_power_tune_defaults(struct pp_hwmgr *hwmgr)
struct phm_ppt_v2_information *table_info =
(struct phm_ppt_v2_information *)(hwmgr->pptable);
struct phm_tdp_table *tdp_table = table_info->tdp_table;
PPTable_t *table = &(data->smc_state_table.pp_table);
+ if (tdp_table == NULL) {
+ pr_err("amdgpu: %s: missing PowerTune table "
+ "(usPowerTuneTableOffset == 0 in VBIOS)\n", __func__);
+ return;
+ }
+
table->SocketPowerLimit = cpu_to_le16(
tdp_table->usMaximumPowerDeliveryLimit);
@@ -1342,6 +1348,12 @@ int vega10_enable_power_containment(struct pp_hwmgr *hwmgr)
struct phm_tdp_table *tdp_table = table_info->tdp_table;
int result = 0;
+ if (tdp_table == NULL) {
+ pr_err("amdgpu: %s: missing PowerTune table "
+ "(usPowerTuneTableOffset == 0 in VBIOS)\n", __func__);
+ return -EINVAL;
+ }
+
hwmgr->default_power_limit = hwmgr->power_limit =
(uint32_t)(tdp_table->usMaximumPowerDeliveryLimit);
Root-cause fix in sys/dev/drm/amd/powerplay/hwmgr/vega10_processpptables.c β
reject VBIOS images that omit the mandatory PowerTune table up front so every
consumer is protected:
--- a/sys/dev/drm/amd/powerplay/hwmgr/vega10_processpptables.c
+++ b/sys/dev/drm/amd/powerplay/hwmgr/vega10_processpptables.c
@@ -78,6 +78,9 @@ static int check_powerplay_tables(
PP_ASSERT_WITH_CODE(powerplay_table->usStateArrayOffset,
"State table is not set!", return -1);
+ PP_ASSERT_WITH_CODE(powerplay_table->usPowerTuneTableOffset,
+ "PowerTune table offset is not set!", return -1);
PP_ASSERT_WITH_CODE(powerplay_table->sHeader.structuresize > 0,
"Invalid PowerPlay Table!", return -1);
Related findings
- DF-1471 (sibling, processpptables.c):
NULLderef DoS ininit_overdrive_limitsandcac_dtp_tableallocation path β same VBIOS-omits-mandatory-table family. - DF-1468/DF-1469/DF-1470 (siblings, processpptables.c): unbounded indices / counts in the same VBIOS PowerPlay parser family.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-1550 Β· 4 files| File | Type | Description | Size | |
|---|---|---|---|---|
| fix.diff | suggested-fix | git-apply-able fix for the cited bug | 548 B | view raw |
| VERDICT.md | verdict | source-confirmation analysis | 732 B | β raw |
| build.sh | build-script | N/A (source-only) | 61 B | view raw |
| run.sh | run-script | N/A (source-only) | 87 B | view raw |
DF-1550 VERDICT
Verdict: REPRODUCED (source-confirmed)
Impact: Low (driver-level NULL deref / OOB / leak / DoS β hardware-gated)
Mechanism: vega10_powertune.c:1294 vega10_initialize_power_tune_defaults fetches tdp_table=table_info->tdp_table then derefs at 1297 (tdp_table->usMaximumPowerDeliveryLimit). Same at 1344 vega10_enable_power_con
Citation: sys/dev/drm/amd/powerplay/hwmgr/vega10_powertune.c:1294-1348
Fix: Applied fix.diff β compiles in batch kernel build (rc=0, -Werror).
Verification method: Source-only line-by-line trace of cited path:line. Low-severity driver bug; PoC trigger requires specific hardware or root context. Confirmed the cited vulnerable pattern exists in source.
Fix verification
fixedfix.diff compiled in batch kernel build rc=0 -Werror
fix.diff compiled in batch kernel build rc=0 -Werror
Confirmed kernel references
β
Detail
Exploit chain
none (Low severity)
Evidence (decisive lines)
Source-confirmed: tdp_table NULL deref when VBIOS omits PowerTuneTableOffset (vega10_powertune.c:1294-1297)
Verified recommended fix
Source-confirmed: tdp_table NULL deref when VBIOS omits PowerTuneTableOffset (vega10_powertune.c:1294-1297)
Verdict
Source-confirmed: tdp_table NULL deref when VBIOS omits PowerTuneTableOffset (vega10_powertune.c:1294-1297)
No comments yet.