pptable init reports SUCCESS with NULL OD arrays: revision gate (ucODTableRevision != 1) skips all OD copies and five copy-helper -ENOMEM returns are ignored β unconditional NULL-deref in vega20_od8_set_feature_capabilities, kernel panic at amdgpu driver load
Summary
Two independent gaps leave the OD arrays NULL while init returns 0: the revision gate silently skips all copies for any other legal revision, and the five copy-helper -ENOMEM returns are dropped. Ordering guarantees the crash: pptable_init runs before backend_init, whose first array access dereferences NULL. Harness-reproduced on guest: crafted rev=2 passes every header check, init prints SUCCESS with NULL arrays, consumer SIGSEGVs at 0x0; the ENOMEM variant faults identically; the PATCHED init refuses (-EINVAL) with no fault. Deterministic load-time panic (DoS), also reachable under plain memory pressure.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4545 Β· 14 files| File | Type | Description | Size | |
|---|---|---|---|---|
| od_null_deref.c | β | 9.9 KB | view raw | |
| build.sh | β | 116 B | view raw | |
| run.sh | β | 96 B | view raw | |
| build.log | β | 17 B | view raw | |
| run.log | β | 1003 B | view raw | |
| run.2.log | β | 861 B | view raw | |
| fix_run.log | β | 1003 B | view raw | |
| fix.diff | β | 2.9 KB | view raw | |
| fix_build.log | β | 8.6 KB | view raw | |
| env.txt | β | 335 B | view raw | |
| README.md | β | 2.9 KB | β raw | |
| VERDICT.md | β | 3.1 KB | β raw | |
| manifest.json | β | 1.3 KB | view raw | |
| verdict.json | β | 4.3 KB | view raw |
DF-4545 β vega20 pptable init reports SUCCESS with NULL OD arrays β NULL-deref panic at driver load
Severity: Medium (crafted-VBIOS deterministic kernel panic) | Confidence: certain
File: sys/dev/drm/amd/powerplay/hwmgr/vega20_processpptables.c:823,835-846,870-877,880-891
What
Two independent gaps leave hwmgr->pptable->od_feature_capabilities /
od_settings_max / od_settings_min NULL while
vega20_pp_tables_initialize() returns 0 (success):
- The revision gate (:823): the three OD copies run only when
OverDrive8Table.ucODTableRevision == 1. Any other legal value (0, 2, 3, ...) skips them entirely β no error, arrays stay NULL. The PowerSaving clock copies (:864-878) have the same shape. - Ignored return codes (CWE-252): the five copy calls
(:835-846, :870-877) drop
-ENOMEMon the floor, so an allocation failure also yields NULL arrays with a "successful" init.
vega20_pp_tables_initialize (hwmgr.c:222) runs before backend_init
(hwmgr.c:229); vega20_hwmgr_backend_init β vega20_enable_dpm_tasks β
vega20_od8_initialize_default_settings (vega20_hwmgr.c:1598) β
vega20_od8_set_feature_capabilities, whose first array access
(vega20_hwmgr.c:906, od_feature_capabilities[0] with
GNLD_DPM_GFXCLK enabled on real silicon) dereferences NULL β kernel
panic during amdgpu driver load. The fill loop (:1219) would deref NULL
likewise.
Same untrusted-firmware threat model as knowns DF-1689/DF-1690 but a
different defect class (NULL deref / skipped-init-reported-as-success vs
OOB read of the blob). Note also :857 reads
ODSettingsMax[ATOM_VEGA20_ODSETTING_POWERPERCENTAGE] unconditionally even
when the revision gate skipped parsing β garbage TDPODLimit can set the
PowerControl cap.
Reproduce (lifted-copy harness)
./build.sh && ./run.sh
Expected (run.log):
- CONTROL rev=1 healthy alloc β init SUCCESS, consumer completes, no fault
- VARIANT A crafted ucODTableRevision=2 β check_powerplay_tables PASSES
(format rev 11, structuresize>0, smcPPTable.Version=3), init returns 0
with od_feature_capabilities=0x0, consumer faults:
SIGSEGV at address 0x0 β panic-class
- VARIANT B rev=1 + injected kzalloc failure (the ignored -ENOMEM) β same
fault at 0x0
- PATCHED (fix.diff: checked returns + refuse-to-succeed-with-NULL) β init
returns -EINVAL, consumer never runs, no fault
Fix validation
fix.diffβgit apply --checkOK; return codes checked + NULL guard that fails init with -EINVAL- full
amdgpu.korebuild with the patch under the real kernel toolchain (-Werror): RC=0 (fix_build.log); guest /usr/src restored - harness PATCHED control: crafted rev=2 refused at init, no fault
Files
od_null_deref.c(sha256 97242d7d577fb00d2cc1ea71b6a54a0758c8caa2a5df42f3b659601a1fac0739)build.sh/run.sh,build.log/run.log/run.2.log/fix_run.logfix.diff,fix_build.log,env.txt,VERDICT.md,manifest.json,verdict.json
VERDICT β DF-4545
Reproduced: YES (lifted-copy harness on the audit guest; in-kernel path hardware-gated)
- Root cause proven (vega20_processpptables.c):
- :823 β the
ucODTableRevision == 1gate around ALL three OD copies; revision != 1 skips them silently. - :835-846 / :870-877 β the five copy-helper return codes are dropped (CWE-252); a -ENOMEM leaves the pointers NULL.
- :889-891 β init returns
resultof append_vbios_pptable (0), so vega20_pp_tables_initialize reports SUCCESS with NULL od arrays. - Consumer proven (vega20_hwmgr.c): backend_init (hwmgr.c:229, after
pptable_init at :222) β vega20_enable_dpm_tasks β
vega20_od8_initialize_default_settings (:1598) β
vega20_od8_set_feature_capabilities β first deref
od_feature_capabilities[ ATOM_VEGA20_ODFEATURE_GFXCLK_LIMITS]at :906, guarded only bysmu_features[GNLD_DPM_GFXCLK].enabled(true on functioning silicon). No NULL check anywhere on the path (checked :906-:976 and :1216-1221). - Harness result (run.log): VARIANT A (crafted ucODTableRevision=2,
all header checks pass, Version=3) β init SUCCESS with
od_feature_capabilities=0x0β consumer SIGSEGV at 0x0 β the kernel-equivalent panic. VARIANT B (rev=1, injected kzalloc failure) β identical fault, proving the ignored -ENOMEM is equally fatal. CONTROL (rev=1 healthy) runs clean; PATCHED (fix.diff) refuses init (-EINVAL) and the consumer never runs.
Impact
Deterministic kernel panic (NULL-deref) at amdgpu driver load on a machine whose VBIOS carries an OverDrive8 revision other than 1 β a value the driver parses but never accepts, reporting success instead of rejecting the table. Same crafted-firmware precondition as DF-1689/DF-1690 (Medium per family convention); also a memory-pressure-induced variant. DoS only β NULL write at a fixed offset 0, no controllable data.
Fix validation
git apply --checkOK- Full amdgpu.ko rebuild with the fix under the real kernel toolchain with -Werror: RC=0 (fix_build.log); /usr/src restored + verified
- Harness PATCHED control: crafted table rejected at init, no fault
Kernel refs
sys/dev/drm/amd/powerplay/hwmgr/vega20_processpptables.c:823, sys/dev/drm/amd/powerplay/hwmgr/vega20_processpptables.c:835, sys/dev/drm/amd/powerplay/hwmgr/vega20_processpptables.c:839, sys/dev/drm/amd/powerplay/hwmgr/vega20_processpptables.c:843, sys/dev/drm/amd/powerplay/hwmgr/vega20_processpptables.c:870, sys/dev/drm/amd/powerplay/hwmgr/vega20_processpptables.c:874, sys/dev/drm/amd/powerplay/hwmgr/vega20_processpptables.c:889, sys/dev/drm/amd/powerplay/hwmgr/vega20_hwmgr.c:906, sys/dev/drm/amd/powerplay/hwmgr/vega20_hwmgr.c:1598, sys/dev/drm/amd/powerplay/hwmgr/hwmgr.c:222, sys/dev/drm/amd/powerplay/hwmgr/hwmgr.c:229
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
not_testablePatched kernel not runnable on the guest (no AMD ASIC). fix.diff is git-apply-able, compiles clean in a full amdgpu.ko module build under the real kernel toolchain with -Werror (fix_build.log, RC=0), and the harness PATCHED control rejects the crafted table at init with no consumer fault.
fix.diff; fix_build.log (module rebuild RC=0, 0 errors); run.log PATCHED case (init refuses -EINVAL, no fault)
Confirmed kernel references
- sys/dev/drm/amd/powerplay/hwmgr/vega20_processpptables.c:823
- sys/dev/drm/amd/powerplay/hwmgr/vega20_processpptables.c:835
- sys/dev/drm/amd/powerplay/hwmgr/vega20_processpptables.c:839
- sys/dev/drm/amd/powerplay/hwmgr/vega20_processpptables.c:843
- sys/dev/drm/amd/powerplay/hwmgr/vega20_processpptables.c:870
- sys/dev/drm/amd/powerplay/hwmgr/vega20_processpptables.c:874
- sys/dev/drm/amd/powerplay/hwmgr/vega20_processpptables.c:889
- sys/dev/drm/amd/powerplay/hwmgr/vega20_hwmgr.c:906
- sys/dev/drm/amd/powerplay/hwmgr/vega20_hwmgr.c:1598
- sys/dev/drm/amd/powerplay/hwmgr/hwmgr.c:222
- sys/dev/drm/amd/powerplay/hwmgr/hwmgr.c:229
Detail
Exploit chain
crafted VBIOS/vGPU ROM with OverDrive8Table.ucODTableRevision != 1 (or memory pressure during pptable init) -> pptable_init succeeds with NULL od arrays -> vega20_od8_set_feature_capabilities NULL-deref at driver load -> kernel panic (DoS); no data control, fixed NULL offset
Evidence (decisive lines)
run.log (VARIANT A/B: '>>> SIGSEGV: fault address 0x0 (od_feature_capabilities + 0)' + 'PANIC-CLASS FAULT REPRODUCED'; CONTROL clean; PATCHED refuses init, no fault); run.2.log (repeat); fix_build.log (full amdgpu.ko rebuild with fix.diff, -Werror, RC=0); fix.diff (git apply --check OK)
PoC changes
authored fresh this run (pass 2); no seed PoC existed
Verified recommended fix
check the five copy-helper return codes and fail init (-EINVAL) when the OD revision is unsupported instead of reporting success with NULL arrays (fix.diff)
Verdict
vega20 pptable init reports SUCCESS with NULL overdrive arrays: the ucODTableRevision==1 gate (vega20_processpptables.c:823) silently skips all three OD copies for any other legal revision value, and the five copy-helper return codes are ignored (:835-846, :870-877) so a kzalloc failure leaves the pointers NULL too; init then returns append_vbios_pptable's 0 (:889-891). backend_init runs right after (hwmgr.c:229 after :222) and vega20_od8_initialize_default_settings (vega20_hwmgr.c:1598) calls vega20_od8_set_feature_capabilities whose first array access (vega20_hwmgr.c:906, od_feature_capabilities[0]) dereferences NULL -> kernel panic at amdgpu driver load. Reproduced on the audit guest with a faithful lifted-copy harness: VARIANT A (crafted ucODTableRevision=2 that passes every header check) -> init SUCCESS with od_feature_capabilities=0x0 -> consumer SIGSEGV at address 0x0; VARIANT B (rev=1 with injected kzalloc failure, the dropped -ENOMEM) -> identical fault; CONTROL (rev=1 healthy) clean; PATCHED (fix.diff: checked returns + refuse-to-succeed-with-NULL) rejects the table at init (-EINVAL) and no fault occurs. In-kernel path hardware-gated (no AMD GPU on the guest).
No comments yet.