vega10_setup_default_pcie_table reads bios entries[1] past the allocation when the VBIOS PCIE table has a single state (NUM_LINK_LEVELS=2)
Summary
The function asserts only bios_pcie_table->count != 0, then loops i < NUM_LINK_LEVELS (=2) reading bios_pcie_table->entries[i] - the parser allocates exactly ucNumEntries records, so count==1 makes entries[1] an OOB heap read; the garbage gen/lane/lclk becomes link level 1 (count forced to 2) and is uploaded to the SMC. Crafted/malformed VBIOS (1-entry PCIE table): OOB heap read at driver load plus wrong PCIe link programming pushed to the SMC - DoS-adjacent misbehavior and info exposure from adjacent heap into SMC uploads.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-3706 Β· 10 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 1.8 KB | β raw | |
| VERDICT.md | β | 1.7 KB | β raw | |
| trigger_notes.c | β | 621 B | view raw | |
| build.sh | β | 81 B | view raw | |
| run.sh | β | 145 B | view raw | |
| run.log | β | 399 B | view raw | |
| env.txt | β | 848 B | view raw | |
| fix.diff | β | 771 B | view raw | |
| manifest.json | β | 910 B | view raw | |
| verdict.json | β | 2.1 KB | view raw |
DF-3706 β vega10_setup_default_pcie_table reads bios entries[1] when the VBIOS PCIE table has a single state (NUM_LINK_LEVELS=2)
File: sys/dev/drm/amd/powerplay/hwmgr/vega10_hwmgr.c (pass 2).
Bug (:1228-1252)
PP_ASSERT_WITH_CODE(bios_pcie_table->count, // :1228 β only checks != 0
"Incorrect number of PCIE States from VBIOS!", return -1);
for (i = 0; i < NUM_LINK_LEVELS; i++) { // :1232 β NUM_LINK_LEVELS == 2
... pcie_table->pcie_gen[i] = bios_pcie_table->entries[i].gen_speed; // :1238
... pcie_table->pcie_lane[i] = encode_pcie_lane_width(
bios_pcie_table->entries[i].lane_width); // :1244-1245
... pcie_table->lclk[i] = bios_pcie_table->entries[i].pcie_sclk; // :1251
The parser (get_pcie_table, vega10_processpptables.c:757-790) allocates exactly
ucNumEntries records and the assert above accepts any nonzero count β a VBIOS PCIE table
with ucNumEntries == 1 leaves entries[1] beyond the allocation, and :1238/:1244/:1251
read it (heap OOB read). The garbage gen/lane/lclk values are then installed as
pcie_table->[1] and uploaded to the SMC (vega10_populate_smc_link_levels :1480-1504, count
forced to NUM_LINK_LEVELS at :1254).
Trigger (requires AMD Vega10 hardware)
VBIOS powerplay table with ATOM_Vega10_PCIE_Table.ucNumEntries == 1 (but nonzero). Driver load β vega10_enable_dpm_tasks β vega10_init_smc_table :2472 β vega10_setup_default_dpm_tables :1415 β this function β OOB read of one phm_ppt_v1_pcie_record.
Impact
Heap OOB read feeding wrong PCIe gen/lane/clock to the SMC link state (hardware misprogram / info exposure into SMC uploads); adjacent-allocation read is not attacker-directed content, so this is a Medium/Low correctness+OOB-read defect rather than a corruption primitive.
PoC status
Guest virtio-only β inconclusive, source-certain.
DF-3706 VERDICT β pcie bios entries[1] OOB read
status: inconclusive (hardware-gated) β source-certain. reproduced=0 on audit guest.
Proof chain (lines verified in guest /usr/src, see run.log)
- smu9_driver_if.h:44
#define NUM_LINK_LEVELS 2. - vega10_processpptables.c:772-783 get_pcie_table kzallocs
ucNumEntriesrecords; the count is the raw ATOM value (1..255), no minimum of NUM_LINK_LEVELS enforced. - vega10_hwmgr.c:1228 asserts only
bios_pcie_table->count != 0; :1232-1252 then readsbios_pcie_table->entries[i]for i in [0, NUM_LINK_LEVELS) β with count == 1 the read of entries[1] is past the kzalloc'd table (heap OOB read), and its garbage is committed to pcie_table->pcie_gen/lane/lclk[1] (:1254 count = NUM_LINK_LEVELS) and uploaded to the SMC by vega10_populate_smc_link_levels (:1480-1504).
Impact: OOB heap read + wrong PCIe link programming on crafted/malformed VBIOS. DoS-adjacent and correctness; Medium/Low. (Upstream Linux later clamped this loop to bios count and let populate_smc_link_levels pad.)
Guest result
0 AMD PCI devices; amdgpu.ko never binds. Lines confirmed in guest /usr/src (run.log). Verdict: inconclusive, source-certain.
Fix
fix.diff clamps the loop to min(NUM_LINK_LEVELS, bios count) and lets the established padding path in vega10_populate_smc_link_levels (:1492-1504) replicate the last valid level.
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_testablefix.diff authored against read-only sys/ tree; no AMD hw on guest so no patched-kernel validation possible
fix.diff; run.log hardware gate
Confirmed kernel references
Detail
Exploit chain
crafted VBIOS (1-entry PCIE table) -> OOB read -> wrong PCIe link state programmed to SMC (misbehavior/DoS-adjacent); no write primitive
Evidence (decisive lines)
VERDICT.md; run.log (guest-src excerpts :1228-1232, NUM_LINK_LEVELS def); trigger_notes.c; fix.diff
PoC changes
no runnable PoC possible (hardware-gated); documented VBIOS recipe
Verified recommended fix
Clamp loop to min(NUM_LINK_LEVELS, bios count) and let populate_smc_link_levels pad (fix.diff)
Verdict
Source-certain heap OOB read: vega10_setup_default_pcie_table asserts only bios_pcie_table->count != 0 (:1228) then loops i < NUM_LINK_LEVELS(=2, smu9_driver_if.h:44) reading bios_pcie_table->entries[i] (:1238/:1244/:1251); the parser allocates exactly ucNumEntries records (vega10_processpptables.c:772-783), so a 1-entry VBIOS PCIE table makes entries[1] an OOB read whose garbage gen/lane/lclk is uploaded to the SMC (populate_smc_link_levels :1480-1504, count forced to 2 at :1254). Not reproducible on the virtio-only audit guest (0 AMD PCI devices); lines verified in guest /usr/src.
No comments yet.