Heap OOB writes into kzalloc'd struct ci_smumgr via unclamped VBIOS MCLK DPM count vs SMU7_MAX_LEVELS_MEMORY=6: MemoryLevel[6..7], unguarded MemoryLevel[count-1], mc_regs->data[6..7], mclk_levels[MemoryDpmLevelCount] (DF-3909 twin)
Summary
The VBIOS-controlled mclk DPM count (up to 8, unclamped - same provenance) indexes arrays dimensioned 6 for CI: full ~40B MemoryLevel structs written past MemoryLevel[6] clobbering LinkLevel; the unguarded count-1 DisplayWatermark store; 64B MCRegisterSet structs written past mc_regs->data[6] into the trailing ci_mc_reg_table of the kzalloc'd ci_smumgr; and the runtime profile/OD path reads/writes mclk_levels[i] up to the unclamped count. In-object heap corruption with VBIOS-chosen contents, then byte-copied into SMC - kernel heap corruption primitive + firmware confusion. Clamp-at-sinks fix; root fix (trim MCLK in smu7_setup_dpm_tables) flagged for smu7_hwmgr.c.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-3940 Β· 6 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 3.5 KB | β raw | |
| VERDICT.md | β | 4.3 KB | β raw | |
| fix.diff | β | 2.3 KB | view raw | |
| env.txt | β | 1004 B | view raw | |
| manifest.json | β | 1.2 KB | view raw | |
| verdict.json | β | 3.7 KB | view raw |
DF-3940 β Heap OOB writes into kzalloc'd struct ci_smumgr via unclamped VBIOS MCLK DPM count (SMU7_MAX_LEVELS_MEMORY=6 vs counts up to 8)
Finding (tonga DF-3909 twin, CI)
Unclamped data->dpm_table.mclk_table.count (VBIOS-controlled, up to
MAX_REGULAR_DPM_NUMBER=8) is used as the write index into arrays dimensioned
SMU7_MAX_LEVELS_MEMORY = SMU__NUM_MCLK_DPM_LEVELS = 6 for CI
(sys/dev/drm/amd/powerplay/smumgr/ci_smumgr.h:27):
sys/dev/drm/amd/powerplay/smumgr/ci_smumgr.c:1314-1321βci_populate_all_memory_levels:ci_populate_single_memory_level(..., &MemoryLevel[i])writes fullSMU7_Discrete_MemoryLevel(~40B each,smu7_discrete.h:168-207) structs atMemoryLevel[6]/MemoryLevel[7]when count β {7,8} β pastMemoryLevel[SMU7_MAX_LEVELS_MEMORY](smu7_discrete.h:325), clobbering the adjacentLinkLevel[0..2]insidesmc_state_tableof the kzalloc'dstruct ci_smumgr(ci_smumgr.h:69-78, alloc atci_smumgr.c:2731-2743).ci_smumgr.c:1339βMemoryLevel[dpm_table->mclk_table.count-1].DisplayWatermark = ...β the unguarded[count-1]store (tonga DF-3909 called this out too): for count 7/8 writesMemoryLevel[6]/[7]OOB (count==0 is impossible:smu7_setup_dpm_tables_v0/v1assert dep count >= 1,smu7_hwmgr.c:683-685/779-785).ci_smumgr.c:1794-1798βci_convert_mc_reg_table_to_smc:ci_convert_mc_reg_table_entry_to_smc(..., &mc_regs->data[i])for i < mclk count, butSMU7_Discrete_MCRegisters.data[]is sizedSMU7_DISCRETE_MC_REGISTER_ARRAY_SET_COUNT = SMU7_MAX_LEVELS_MEMORY = 6(smu7_discrete.h:408) βdata[6]/[7]are 64-byteSMU7_Discrete_MCRegisterSetwrites (value[16] u32s, VBIOS MC register data) past the array into the trailingstruct ci_mc_reg_tablemember of the kzalloc'd ci_smumgr (in-object, tail of allocation). Alsoci_update_and_upload_mc_reg_table(:1828-1831) then DMAssizeof(Set)*countbytes to the SMC.ci_smumgr.c:2819-2846βci_update_dpm_settings(runtime profile/OD path):for (i = 0; i < smc_state_table.MemoryDpmLevelCount; i++)mclk_levels[i].ActivityLevel/UpH/DownHread/write βMemoryDpmLevelCountwas set to the unclamped count at:1337, so counts 7/8 give further OOB 2-byte/1-byte writes pastMemoryLevel[6].
Count provenance (why 7-8 is legal input)
processpptables.c:391: dep table count = VBIOSucNumEntries(u8), table entries dynamically allocated (hwmgr.h:120-123).smu7_hwmgr.c:704-710(smu7_setup_dpm_tables_v0, selected for CI viahwmgr.c:92-99PP_TABLE_V0): dedups intodpm_levels[8]β counts 7-8 are fully in-bounds there. Nomin(count, SMU7_MAX_LEVELS_MEMORY)anywhere.- The MCLK family is the only level family without an upstream trim:
VDDC/VDDCI/MVDD voltage tables are trimmed at
smu7_hwmgr.c:324-346, PCIE capped at:557-569. SCLK needs no trim by luck (8 == GraphicsLevel[8]).
Trigger
Crafted VBIOS with vdd_dep_on_mclk = 7-8 distinct MCLK entries on a
Bonaire/Hawaii board; amdgpu DPM enable β ci_init_smc_table β
ci_populate_all_memory_levels (ci_smumgr.c:1980), ci_populate_initial_mc_reg_table
(:2110), and later OD/profile updates β ci_update_dpm_settings.
Why inconclusive on this guest
No AMD GPU present (env.txt β zero vendor-0x1002 PCI functions on the audit guest). Source math is exact; this is the CI twin of verified tonga finding DF-3909 (heap OOB into GraphicsLevel[8]/MemoryLevel[4] via unclamped counts).
DF-3940 β VERDICT
Status: inconclusive (hardware-gated) β source-level certainty: certain. Impact class: kernel heap out-of-bounds write (CWE-787), crafted-VBIOS vector.
What overflows, exactly
struct ci_smumgr is kzalloc'd at ci_smumgr.c:2731-2743; it embeds
SMU7_Discrete_DpmTable smc_state_table (with MemoryLevel[6] at
smu7_discrete.h:325) and, near its tail, SMU7_Discrete_MCRegisters mc_regs
(with data[6] since SMU7_DISCRETE_MC_REGISTER_ARRAY_SET_COUNT ==
SMU7_MAX_LEVELS_MEMORY, smu7_discrete.h:408). For CI,
SMU7_MAX_LEVELS_MEMORY == SMU__NUM_MCLK_DPM_LEVELS == 6
(ci_smumgr.h:27) while the hwmgr-side DPM machinery holds up to
MAX_REGULAR_DPM_NUMBER == 8 levels (smu7_hwmgr.h:95,100).
Sinks (all keyed on dpm_table->mclk_table.count, never clamped):
| # | site | write | OOB when count |
|---|---|---|---|
| 1 | ci_smumgr.c:1314-1318 |
full SMU7_Discrete_MemoryLevel (~40B, ~30 VBIOS-derived fields) per level via ci_populate_single_memory_level |
7 or 8 |
| 2 | ci_smumgr.c:1339 |
[count-1].DisplayWatermark u8 |
7 or 8 |
| 3 | ci_smumgr.c:1794-1798 |
SMU7_Discrete_MCRegisterSet (16 x u32 VBIOS MC data) per level into mc_regs->data[i] |
7 or 8 |
| 4 | ci_smumgr.c:2819-2846 |
ActivityLevel (u16), UpH/DownH (u8) via MemoryDpmLevelCount (set at :1337) |
7 or 8 |
Writes stay inside the ci_smumgr allocation for sinks 1-2 (they land on
LinkLevel[0..2]) and reach the trailing ci_mc_reg_table for sink 3 β
in-object corruption of the heap object that is then byte-copied wholesale
into SMC RAM (ci_copy_bytes_to_smc, bounded to SMC_RAM_END, so no kernel
side-effect there). This is precisely the DF-3909 (tonga) result shape: heap
object corruption with attacker-influenced contents, gateway to
confused SMC firmware state and, with the right heap layout, past-object
corruption is not reachable from these sinks alone (struct is large and the
sinks are mid/tail members; sink 3's data[7] ends 64B into ci_mc_reg_table).
Count provenance (path:line)
processpptables.c:391β count = VBIOSucNumEntries(u8, β€255).hwmgr.h:120-123β dep table entries[1] dynamic alloc β any count fits the source table.smu7_hwmgr.c:704-710β v0 setup (CI selects PP_TABLE_V0 athwmgr.c:92-99) dedups intodpm_levels[8]; count 7-8 in-bounds; no trim to 6.- Contrast (proves the omission is the bug, not policy):
- voltage tables trimmed:
smu7_hwmgr.c:324-346(phm_trim_voltage_table_to_fit_state_table+smum_get_mac_definition(SMU_MAX_LEVELS_{VDDC,VDDGFX,VDDCI,MVDD})) β henceVddcLevel[8]/VddciLevel[4]/MvddLevel[4]writes atci_smumgr.c:839-922are SAFE (negative result). - PCIE table capped:
smu7_hwmgr.c:557-569(max_entry = min(SMU7_MAX_LEVELS_LINK, count),count = max_entry - 1β€ 7) β hence thei <= countloop atci_smumgr.c:1003writingLinkLevel[count]stays β€LinkLevel[7], in-bounds (negative result β looks like an off-by-one, is not reachable for CI).
Why not reproduced
No AMD GPU on the audit guest (env.txt). Requires Bonaire/Hawaii hardware
plus a modified VBIOS image. Classified inconclusive per batch convention
(DF-3934/DF-3935, tonga DF-3908..3910). No uid=0 chain possible without the
device; the write contents are largely VBIOS-chosen (clocks, voltages, MC
register values) making this a strong corruption primitive on-target.
Fix validation
fix.diff clamps the MCLK count at the populate sinks (sinks 1, 2, 4 via the
clamped count, sink 3 with its own clamp). fix_status: not_testable
(no hardware); bounds-only diff. Root fix belongs one layer up β trim MCLK
(and SCLK for symmetry) in smu7_setup_dpm_tables_v0/v1 exactly like the
voltage/PCIE tables; that also kills DF-3939's trigger at the source.
Out-of-file note (for the orchestrator, not re-reported here)
Dep-table counts > 8 overflow dpm_levels[8] inside struct smu7_hwmgr
itself (smu7_hwmgr.c:704-710, :783-799) β a separate file/finding.
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 not validated on the guest: requires an AMD Sea Islands GPU + crafted VBIOS (no 0x1002 function on the guest, env.txt). fix.diff clamps the MCLK count to SMU7_MAX_LEVELS_MEMORY at the ci_smumgr.c sinks (populate loop, MemoryDpmLevelCount, [count-1] store, mc_regs->data[] loop) and is authored post-verification against the cited lines; min_t is already used in this TU (ci_smumgr.c:391).
findings/poc/DF-3940/fix.diff + VERDICT.md
Confirmed kernel references
- sys/dev/drm/amd/powerplay/smumgr/ci_smumgr.c:1314
- sys/dev/drm/amd/powerplay/smumgr/ci_smumgr.c:1339
- sys/dev/drm/amd/powerplay/smumgr/ci_smumgr.c:1794
- sys/dev/drm/amd/powerplay/smumgr/ci_smumgr.c:2819
- sys/dev/drm/amd/powerplay/smumgr/ci_smumgr.c:2731
- sys/dev/drm/amd/powerplay/smumgr/ci_smumgr.h:27
- sys/dev/drm/amd/powerplay/inc/smu7_discrete.h:325
- sys/dev/drm/amd/powerplay/inc/smu7_discrete.h:408
- sys/dev/drm/amd/powerplay/hwmgr/smu7_hwmgr.c:704
- sys/dev/drm/amd/powerplay/hwmgr/processpptables.c:391
Detail
Exploit chain
craft VBIOS with vdd_dep_on_mclk = 7-8 distinct MCLK entries -> amdgpu DPM enable -> ci_init_smc_table -> ci_populate_all_memory_levels/ci_populate_initial_mc_reg_table write past MemoryLevel[6]/mc_regs.data[6] -> corrupt LinkLevel + ci_mc_reg_table in the ci_smumgr heap object -> confused SMC state / OOD writes on every profile update (ci_update_dpm_settings)
Evidence (decisive lines)
findings/poc/DF-3940/VERDICT.md (sink table + provenance), fix.diff, env.txt
PoC changes
No seed PoC; hardware-gated (AMD CI GPU + crafted VBIOS).
Verified recommended fix
Clamp mclk count to SMU7_MAX_LEVELS_MEMORY at the ci_smumgr.c sinks (fix.diff), or trim MCLK levels once in smu7_setup_dpm_tables_v0/v1.
Verdict
CI twin of DF-3909: the VBIOS-derived mclk DPM count (up to MAX_REGULAR_DPM_NUMBER=8, unclamped from processpptables.c:391 ucNumEntries through smu7_hwmgr.c:704-710 dpm_levels[8]; the MCLK family is the only one without an upstream trim - voltages are trimmed at smu7_hwmgr.c:324-346, PCIE capped at :557-569) is used to index arrays dimensioned SMU7_MAX_LEVELS_MEMORY=6 for CI (ci_smumgr.h:27): MemoryLevel[i] full ~40B structs at ci_smumgr.c:1314-1318, the unguarded MemoryLevel[count-1] store at :1339, SMU7_Discrete_MCRegisterSet (16x u32 VBIOS MC data) into mc_regs->data[i] at :1794-1798 (SET_COUNT=6, smu7_discrete.h:408), and the runtime mclk_levels[MemoryDpmLevelCount] writes at :2819-2846. All land in the kzalloc'd struct ci_smumgr (ci_smumgr.c:2731-2743): sinks 1-2 clobber LinkLevel[0..2], sink 3 clobbers the trailing ci_mc_reg_table - in-object heap corruption with VBIOS-chosen contents, then byte-copied into SMC. Crafted-VBIOS vector on Bonaire/Hawaii; hardware absent from the audit guest => inconclusive, source-certain, like tonga DF-3909.
No comments yet.