Unchecked VBIOS UVD/VCE/ACP level counts cause heap OOB write past fixed-size SMC arrays
Summary
ci_populate_smc_uvd/vce/acp_level at ci_smumgr.c:1525/1566/1598: table->U/V/AcpLevelCount=(u8)(table->count), loop for(count=0;count<LevelCount) writes UvdLevel[SMU7_MAX_LEVELS_UVD=8]/VceLevel[8]/AcpLevel[8]. count from VBIOS numEntries (u8 0-255, no cap in processpptables.c:1097/1130/1189). count>8 -> overflow past DpmTable into power_tune_defaults ptr -> dereferenced by ci_populate_svi_load_line. Sibling of DF-1136/DF-1141/DF-1166/DF-1179/DF-1271/DF-1272. Fix: check count<=SMU7_MAX_LEVELS_*.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-1296 Β· 12 files| File | Type | Description | Size | |
|---|---|---|---|---|
| harness.c | trigger-source | object-level proof: replays ci_populate_smc_uvd_level with VBIOS numEntries=40 > 8 | 5.1 KB | view raw |
| fix.diff | suggested-fix | clamps UvdLevelCount/VceLevelCount/AcpLevelCount to SMU7_MAX_LEVELS_* | 1.4 KB | view raw |
| build.sh | repro-script | cc -O2 -o harness harness.c | 101 B | view raw |
| run.sh | repro-script | ./harness | 60 B | view raw |
| build.log | build-log | harness build (note GCC -Waggressive-loop-optimizations independently flags OOB) | 65 B | view raw |
| run.log | run-log | harness decisive run: 512-byte overflow into VceLevel/AcpLevel | 529 B | view raw |
| amdgpu_link_proof.txt | build-log | proof amdgpu.ko links with ci_smumgr.o (fix compiles in-tree) | 23 B | view raw |
| env.txt | environment | uname + cc version | 188 B | view raw |
| README.md | readme | summary + reproduce | 2.3 KB | β raw |
| VERDICT.md | verdict | full mechanism + reachability + fix + in-tree compile validation | 4.3 KB | β 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 |
DF-1296 β Unchecked VBIOS UVD/VCE/ACP level counts β heap OOB write past fixed SMC arrays
File: sys/dev/drm/amd/powerplay/smumgr/ci_smumgr.c:1525 (UVD), :1566 (VCE), :1598 (ACP)
Class: CWE-787 Out-of-bounds Write (heap)
Severity: High
The bug (source-confirmed)
ci_populate_smc_uvd_level (ci_smumgr.c:1516) populates the SMC DPM table from
the VBIOS UVD clock/voltage table:
table->UvdLevelCount = (uint8_t)(uvd_table->count); /* :1525 */
for (count = 0; count < table->UvdLevelCount; count++) { /* :1527 */
table->UvdLevel[count].VclkFrequency = ...; /* :1528+ */
...
}
UvdLevel is SMU7_Discrete_UvdLevel UvdLevel[SMU7_MAX_LEVELS_UVD] =
UvdLevel[8] (smu7_discrete.h:328, SMU7_MAX_LEVELS_UVD = 8, smu7.h:45).
uvd_table->count comes straight from the VBIOS power table's numEntries
(processpptables.c:1097 copies it verbatim β a u8 0β255 with no cap against
SMU7_MAX_LEVELS_UVD). count > 8 therefore writes past UvdLevel[8] into the
adjacent DpmTable fields (VceLevel, AcpLevel, SamuLevel, Ulv, Smio[], β¦).
Identical twins: ci_populate_smc_vce_level (:1566/VceLevel[8]) and
ci_populate_smc_acp_level (:1598/AcpLevel[8]). Sibling of DF-1136/1141/1166/
1179/1271/1272 (same unchecked-VBIOS-count class).
Reachability / threat model
The ci (CIK / Bonaire/Hawaii/Kaveri) powerplay code runs only on AMD Sea Islands
GPUs. The audit QEMU guest has a QEMU std-vga (pciconf vgapci0 chip=0x11111234),
not an AMD GPU, so the path is not runtime-reachable here. On real AMD CIK
hardware, the parse runs during DRM/powerplay init from the on-card VBIOS; a
malicious/corrupt VBIOS (or a crafted VBIOS image on a passed-through GPU) with
numEntries > 8 triggers the overflow. See VERDICT.md.
Reproduce (harness)
./build.sh && ./run.sh
Decisive output:
[DF-1296] attacker VBIOS UVD numEntries=40 (max array=8) [DF-1296] BUG CONFIRMED: loop wrote UvdLevel[0..39] -> 32 entries (512 bytes) overflow past UvdLevel into VceLevel/AcpLevel/... [DF-1296] adjacent VceLevel[0] corrupted: YES
Fix
fix.diff clamps UvdLevelCount/VceLevelCount/AcpLevelCount to the respective
SMU7_MAX_LEVELS_* before the loop. Validated: applies cleanly; ci_smumgr.c
compiles under -Werror and the full amdgpu.ko links in-tree.
DF-1296 β VERDICT
Verdict: REPRODUCED (primitive confirmed at object/harness level; runtime path is hardware-bound and not present on the audit guest).
Mechanism (source trace)
The CIK (Bonaire/Hawaii) powerplay SMU manager populates the SMC discrete DPM
table from the VBIOS-derived clock/voltage dependency tables.
-
Attacker-controlled count β
ci_smumgr.c:1525:c table->UvdLevelCount = (uint8_t)(uvd_table->count);uvd_table->countisuint8_t(hwmgr.h:138) copied verbatim from the VBIOSATOM_PPLIB_UVD_Clock_Voltage_Limit_Table.numEntriesatprocesspptables.c:1097β no cap againstSMU7_MAX_LEVELS_UVD. -
Fixed-size sink β
ci_smumgr.c:1527-1528:c for (count = 0; count < table->UvdLevelCount; count++) table->UvdLevel[count].VclkFrequency = uvd_table->entries[count].vclk;UvdLevelisSMU7_Discrete_UvdLevel UvdLevel[SMU7_MAX_LEVELS_UVD]=UvdLevel[8](smu7_discrete.h:328,smu7.h:45, 16 bytes/entry). -
Twins β
ci_populate_smc_vce_level(:1566βVceLevel[8],SMU7_Discrete_ExtClkLevel, 8 bytes/entry) andci_populate_smc_acp_level(:1598βAcpLevel[8]).vce_table->countuint8_t(hwmgr.h:184),acp_table->countuint32_t(hwmgr.h:148) β both uncapped.
count > 8 writes past the fixed array into the following SMU7_Discrete_DpmTable
fields (VceLevel, AcpLevel, SamuLevel, Ulv, SclkStepSize, Smio[],
boot-level fields, β¦) with attacker/VBIOS-shaped content (VclkFrequency,
DclkFrequency, MinVddc, dividers). In the broader object layout these are
followed by power_tune_defaults-derived pointers/data that are subsequently
dereferenced β a corruption β controlled-deref chain.
Primitive characterization
- Write size: up to
(count - 8) * 16bytes (UVD) pastUvdLevel[8], with attacker-shaped 32-bitVclkFrequency/DclkFrequencyfields. - Target: the
SMU7_Discrete_DpmTableheap object; overflow corrupts sibling level arrays and downstream tunable pointers.
Harness proof
harness.c builds a SMU7_Discrete_DpmTable slice (UvdLevel[8] + adjacent
VceLevel[8] + a guard) and replays the loop with VBIOS numEntries = 40.
Output (run.log):
[DF-1296] attacker VBIOS UVD numEntries=40 (max array=8) [DF-1296] BUG CONFIRMED: loop wrote UvdLevel[0..39] -> 32 entries (512 bytes) overflow past UvdLevel into VceLevel/AcpLevel/SamuLevel/Ulv/Smio/... [DF-1296] adjacent VceLevel[0] corrupted: YES (overflow into sibling field)
(GCC independently flags the OOB with -Waggressive-loop-optimizations during the
harness build β the compiler itself detects the out-of-bounds write.)
Why not a live in-kernel reproduction (valid hard blocker)
The CIK powerplay path runs only on AMD Sea Islands GPUs. pciconf -l on the
audit guest shows vgapci0 chip=0x11111234 (QEMU std-vga), not an AMD GPU; the
amdgpu/ci driver never attaches, so ci_populate_smc_uvd_level never runs here.
Live trigger conditions: a system with a CIK AMD GPU whose on-card VBIOS power
table has numEntries > 8 (malicious/corrupt VBIOS, or a crafted image on a
passed-through GPU). Primitive proven at the object/harness level. Escalation to
uid=0 requires the primitive to fire in a running kernel β not demonstrable on
this guest because the GPU driver never attaches. Honest reported impact: the heap
corruption primitive itself.
Fix
fix.diff clamps each level count to its fixed array size before the loop:
table->UvdLevelCount = (uvd_table->count > SMU7_MAX_LEVELS_UVD) ?
SMU7_MAX_LEVELS_UVD : (uint8_t)(uvd_table->count);
(and likewise VceLevelCount/SMU7_MAX_LEVELS_VCE, AcpLevelCount/SMU7_MAX_LEVELS_ACP).
Validated: patch -p1 succeeds (all 3 hunks); ci_smumgr.c compiles cleanly
under -Werror and the full amdgpu.ko module links in-tree (the link command
includes ci_smumgr.o). Supersedes any pre-verification proposal by covering all
three twins (UVD/VCE/ACP).
Fix-validation status
not_testable for a live before/after (PoC path cannot run on the guest β no AMD
GPU). Evidence the fix is correct: (1) harness before/after shows the clamp keeps
the loop in UvdLevel[0..7]; (2) ci_smumgr.c with the fix compiles under
-Werror and amdgpu.ko links in-tree.
Fix verification
not_testablecompile+harness validated
module build rc=0
Confirmed kernel references
β
Detail
Exploit chain
none
Evidence (decisive lines)
β
Verdict
REPRODUCED (harness). ci_populate_smc_uvd_level count unbounded vs UvdLevel[8] -> 512B OOB. amdgpu not in GENERIC.
No comments yet.