β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-3706

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)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-3706 Β· 10 files
FileTypeDescriptionSize
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.

VERDICT.md
↓ download raw

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)

  1. smu9_driver_if.h:44 #define NUM_LINK_LEVELS 2.
  2. vega10_processpptables.c:772-783 get_pcie_table kzallocs ucNumEntries records; the count is the raw ATOM value (1..255), no minimum of NUM_LINK_LEVELS enforced.
  3. vega10_hwmgr.c:1228 asserts only bios_pcie_table->count != 0; :1232-1252 then reads bios_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_testable
baseline no→ patch + rebuild →patched clean

fix.diff authored against read-only sys/ tree; no AMD hw on guest so no patched-kernel validation possible

fix.diff; run.log hardware gate
↓ fix.diffper-fix-DF-3706

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.