# DF-1142 — Heap OOB write in SMC UVD/VCE/ACP/SAMU level population (amdgpu ci_dpm.c)

## Verdict
**REPRODUCED (code-level, latent at runtime).** Genuine unbounded-copy-into-
fixed-array heap OOB write confirmed by source trace. **Not triggerable at
runtime on this guest** (no AMD GPU; amdgpu not in GENERIC) -> runtime
**not_testable**. `fix.diff` validated to **apply + compile** under `-Werror`.

## Mechanism (trigger -> primitive -> effect)
- **Sink arrays** (in `SMU7_Discrete_DpmTable`, `sys/dev/drm/amd/powerplay/inc/smu7_discrete.h:314-331`):
  - `uint8_t UvdLevelCount; ...; SMU7_Discrete_UvdLevel UvdLevel[SMU7_MAX_LEVELS_UVD]` (`SMU7_MAX_LEVELS_UVD = 8`, `smu7.h:45-48`); likewise `VceLevel[8]`, `AcpLevel[8]`, `SamuLevel[8]`.
- **Unbounded count + loop** in four functions (all in `sys/dev/drm/amd/amdgpu/ci_dpm.c`):
  - `ci_populate_smc_uvd_level` `:2791-2794`: `table->UvdLevelCount = adev->pm.dpm.dyn_state.uvd_clock_voltage_dependency_table.count;` then `for (count = 0; count < table->UvdLevelCount; count++) table->UvdLevel[count].VclkFrequency = ...` (writes `UvdLevel[count]`, count from VBIOS, no clamp).
  - `ci_populate_smc_vce_level` `:2834-2837` (writes `VceLevel[count]`).
  - `ci_populate_smc_acp_level` `:2867-2870` (writes `AcpLevel[count]`; note the original already casts to `(u8)` but never clamps to 8).
  - `ci_populate_smc_samu_level` `:2899-2902` (writes `SamuLevel[count]`).
- **Count source:** each `*_clock_voltage_dependency_table.count` is parsed from the VBIOS PowerPlay table (e.g. `amdgpu_parse_clk_voltage_dep_table`, `amdgpu_dpm.c:302`) with no upper bound; `count` is a `u8`-promoted-to-`u32`.
- **Contrast with voltage tables:** the finding correctly notes the VDDC/VDDCI voltage tables are trimmed by `ci_trim_voltage_table_to_fit_state_table` (bounded to 8) — but the UVD/VCE/ACP/SAMU level tables are *not*, so the omission is an oversight, not policy.
- **Effect:** with a crafted VBIOS whose `*_clock_voltage_dependency_table.count > 8`, each loop runs off its `[8]` Level array into the adjacent Level array (UVD->VCE->ACP->SAMU) and then into the downstream fields of `pi->smc_state_table`, a write-what-where into SMU control state.

## Threat model / reachability
- **Attacker:** malicious/reflashed VBIOS or malicious PCIe/Thunderbolt AMD GPU. Reached at DPM init when `ci_populate_smc_*_level` are called to build the SMC state table.
- **On this guest:** NOT reachable (QEMU std VGA only; amdgpu not in GENERIC). Valid hard blocker: runtime-unreachable here, latent on physical AMD CIK HW.

## Exploit chain
None developed — valid hard blocker (HW-dependent VBIOS-parsing write, no
userspace-guest path to supply a VBIOS). Demonstrated work is the
source-level confirmation + compiling fix. No `exploit.c` (not a
userspace-reachable primitive).

## PoC changes
No trigger PoC seeded. This folder adds `fix.diff`, `build.sh`, `run.sh`,
`VERDICT.md`, `manifest.json`, `env.txt`, `build.log`, `README.md`.

## Recommended fix
Clamp each `*LevelCount` to its `SMU7_MAX_LEVELS_*` (=8) using `min_t(u8, ...)`
*before* the loop, so both the stored count and the loop bound are safe.
Implemented in `fix.diff` for all four functions. **Matches the finding
proposal** ("clamp each XxxLevelCount to SMU7_MAX_LEVELS_*").
