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

Heap OOB read in vega10_get_soc_index_for_max_uclk: hardcoded entries[3] without count check

Summary

vega10_get_soc_index_for_max_uclk at :3444-3453: indexes vdd_dep_on_mclk->entries[NUM_UCLK_DPM_LEVELS-1=3] unconditionally. Table allocated for ucNumEntries slots (from VBIOS u8). If ucNumEntries<4: OOB read. Reached when mem_boot_level==3 in vega10_upload_dpm_bootup_level. Fix: validate count>=NUM_UCLK_DPM_LEVELS before indexing.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-1183 Β· 12 files
FileTypeDescriptionSize
harness.c trigger-source userspace transcription of vega10_get_soc_index_for_max_uclk showing buggy and fixed behavior 2.8 KB view raw
build.sh build-script cc -O2 -o harness harness.c 101 B view raw
run.sh run-script ./harness 67 B view raw
README.md readme human-readable summary + reproduce 2.2 KB ↓ raw
VERDICT.md verdict detailed mechanism, source trace, why-not-reproduced; upgrades confidence from likely to certain 2.1 KB ↓ raw
fix.diff suggested-fix add count >= NUM_UCLK_DPM_LEVELS + NULL check before indexing entries[NUM_UCLK_DPM_LEVELS-1] 571 B view raw
run.log run-log harness stdout showing buggy returns garbage (1), fixed returns 0 502 B view raw
fix_build.log fix-build-log nativekernel build log showing vega10_hwmgr.c compiles cleanly with fix applied (rc=0, -Werror) 5.6 MB ↓ download
fix_run.log fix-run-log harness on patched kernel (no regression) 121 B view raw
env.txt environment uname, cc version, pciconf 947 B view raw
../fix_build_combined.log build-log Combined 41-finding kernel build (rc=0, -Werror clean) 5.6 MB ↓ download
../fix_build_summary.txt build-summary Summary of the combined 41-finding kernel build 826 B view raw
README.md readme human-readable summary + reproduce
↓ download raw

DF-1183 β€” vega10_get_soc_index_for_max_uclk hardcoded [3] OOB read

Bug (confirmed in source)

sys/dev/drm/amd/powerplay/hwmgr/vega10_hwmgr.c:3444-3453:

static int vega10_get_soc_index_for_max_uclk(struct pp_hwmgr *hwmgr)
{
    struct phm_ppt_v1_clock_voltage_dependency_table *vdd_dep_table_on_mclk;
    struct phm_ppt_v2_information *table_info =
            (struct phm_ppt_v2_information *)(hwmgr->pptable);

    vdd_dep_table_on_mclk  = table_info->vdd_dep_on_mclk;

    return vdd_dep_table_on_mclk->entries[NUM_UCLK_DPM_LEVELS - 1].vddInd + 1;
}

NUM_UCLK_DPM_LEVELS == 4 (defined at sys/dev/drm/amd/powerplay/inc/smu9_driver_if.h:41). The function unconditionally indexes entries[3] regardless of how many entries the table actually holds.

Allocation of vdd_dep_on_mclk is sized by VBIOS ucNumEntries:

// vega10_processpptables.c:565-569
table_size = sizeof(uint32_t) +
    sizeof(phm_ppt_v1_clock_voltage_dependency_record) * mclk_dep_table->ucNumEntries;
mclk_table = kzalloc(table_size, GFP_KERNEL);

If ucNumEntries < 4, the hardcoded entries[3] reads past the allocation into adjacent kernel heap.

Caller chain

vega10_hwmgr.c:3476-3477 inside vega10_upload_dpm_bootup_level:

if (data->smc_state_table.mem_boot_level == NUM_UCLK_DPM_LEVELS - 1) {
    socclk_idx = vega10_get_soc_index_for_max_uclk(hwmgr);

Called from vega10_enable_dpm_tasks (line 3548) and from various power-state-switch helpers (lines 3899, 3921, 3946) β€” only on a real Vega10 GPU.

Trigger model

  • Requires amdgpu powerplay attached to a real Vega10 GPU.
  • Requires a malicious VBIOS with ucNumEntries < 4.
  • CVSS AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H β€” realistic bar is PR:H.

On this guest

Unreachable β€” no AMD GPU; amdgpu/powerplay never attaches. harness.c transcribes the function into userspace, runs both buggy and fixed versions, and prints the result; no kernel effect.

Reproduce

ssh dfbsd-maxx "cd poc/DF-1183 && ./build.sh && ./run.sh"

Validate vdd_dep_on_mclk->count >= NUM_UCLK_DPM_LEVELS before indexing entries[NUM_UCLK_DPM_LEVELS - 1]. Full patch in fix.diff. Matches the finding proposal.

VERDICT.md verdict detailed mechanism, source trace, why-not-reproduced; upgrades confidence from likely to certain
↓ download raw

DF-1183 β€” VERDICT

Status: NOT REPRODUCED (HW-gated; bug confirmed in source) Impact: none (cannot trigger on this guest) Confidence: certain (line-by-line source trace; finding markdown said likely, I confirm certain β€” the indexing is unconditional, no guard upstream) Class: heap OOB read (CWE-125) β€” latent

Mechanism (cited)

vega10_hwmgr.c:3444-3453:

static int vega10_get_soc_index_for_max_uclk(struct pp_hwmgr *hwmgr)
{
    struct phm_ppt_v1_clock_voltage_dependency_table *vdd_dep_table_on_mclk;
    struct phm_ppt_v2_information *table_info =
            (struct phm_ppt_v2_information *)(hwmgr->pptable);

    vdd_dep_table_on_mclk  = table_info->vdd_dep_on_mclk;

    return vdd_dep_table_on_mclk->entries[NUM_UCLK_DPM_LEVELS - 1].vddInd + 1;
}

NUM_UCLK_DPM_LEVELS == 4 (smu9_driver_if.h:41). The function unconditionally dereferences entries[3] regardless of how many entries the table actually holds. The table is sized from the VBIOS-supplied ucNumEntries (vega10_processpptables.c:565-569):

table_size = sizeof(uint32_t) +
    sizeof(phm_ppt_v1_clock_voltage_dependency_record) * mclk_dep_table->ucNumEntries;
mclk_table = kzalloc(table_size, GFP_KERNEL);

If ucNumEntries < 4, the hardcoded entries[3] reads past the allocation. The returned value is then sent to the SMC firmware as a PPSMC_MSG_SetSoftMinSocclkByIndex parameter at vega10_hwmgr.c:3478, so corrupted heap bytes also reach the GPU's microcontroller.

Why it cannot be reproduced on this guest

  • No AMD GPU on the PCI bus; amdgpu/powerplay not in GENERIC.
  • The MCLK table is parsed from device firmware at attach time, so even with hardware the trigger requires a malicious VBIOS (PR:H).

Latent / hardware-gated bug; confirmed real by source trace, unreachable on the audit guest. harness.c transcribes the function into userspace, runs both buggy and fixed versions, and prints the result; no kernel effect.

Fix

fix.diff validates vdd_dep_on_mclk->count >= NUM_UCLK_DPM_LEVELS (and != NULL) before indexing entries[3]. Validated as applies + compiles only β€” fix_status: not_testable.

Fix verification

not_testable

compile+harness validated

kernel build rc=0 + harness

Confirmed kernel references

β€”

Detail

Exploit chain

none

Evidence (decisive lines)

β€”

Verdict

Source+harness. vega10_get_soc_index hardcoded [3] vs ucNumEntries<4 -> heap OOB read. amdgpu not in GENERIC.