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 | 1.1 KB | β raw |
| build.sh | build-script | N/A (source-only) | 65 B | view raw |
| run.sh | run-script | N/A (source-only) | 89 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 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
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.