# 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`):

1. `sys/dev/drm/amd/powerplay/smumgr/ci_smumgr.c:1314-1321` —
   `ci_populate_all_memory_levels`: `ci_populate_single_memory_level(..., &MemoryLevel[i])`
   writes full `SMU7_Discrete_MemoryLevel` (~40B each, `smu7_discrete.h:168-207`)
   structs at `MemoryLevel[6]`/`MemoryLevel[7]` when count ∈ {7,8} — past
   `MemoryLevel[SMU7_MAX_LEVELS_MEMORY]` (`smu7_discrete.h:325`), clobbering
   the adjacent `LinkLevel[0..2]` inside `smc_state_table` of the kzalloc'd
   `struct ci_smumgr` (`ci_smumgr.h:69-78`, alloc at `ci_smumgr.c:2731-2743`).
2. `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 writes `MemoryLevel[6]/[7]` OOB (count==0 is impossible:
   `smu7_setup_dpm_tables_v0/v1` assert dep count >= 1, `smu7_hwmgr.c:683-685/779-785`).
3. `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, but `SMU7_Discrete_MCRegisters.data[]` is sized
   `SMU7_DISCRETE_MC_REGISTER_ARRAY_SET_COUNT = SMU7_MAX_LEVELS_MEMORY = 6`
   (`smu7_discrete.h:408`) — `data[6]/[7]` are 64-byte
   `SMU7_Discrete_MCRegisterSet` writes (value[16] u32s, VBIOS MC register
   data) past the array into the trailing `struct ci_mc_reg_table` member of
   the kzalloc'd ci_smumgr (in-object, tail of allocation). Also
   `ci_update_and_upload_mc_reg_table` (`:1828-1831`) then DMAs
   `sizeof(Set)*count` bytes to the SMC.
4. `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/DownH` read/write — `MemoryDpmLevelCount`
   was set to the unclamped count at `:1337`, so counts 7/8 give further OOB
   2-byte/1-byte writes past `MemoryLevel[6]`.

## Count provenance (why 7-8 is legal input)
- `processpptables.c:391`: dep table count = VBIOS `ucNumEntries` (u8), table
  entries dynamically allocated (`hwmgr.h:120-123`).
- `smu7_hwmgr.c:704-710` (`smu7_setup_dpm_tables_v0`, selected for CI via
  `hwmgr.c:92-99` PP_TABLE_V0): dedups into `dpm_levels[8]` — counts 7-8 are
  fully in-bounds there. No `min(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).
