vega10 SMC PPTable populate loops write 8-entry SMC arrays (CksEnable/CksVidOffset, StaticVoltageOffsetVid, UvdDpmVoltageIndex, SocVid) with unbounded dep-table counts
Summary
vega10_populate_clock_stretcher_table writes pp_table->CksEnable[i]/CksVidOffset[i] (both [NUM_GFXCLK_DPM_LEVELS=8]) for i < vdd_dep_on_sclk->count (<=255); populate_avfs_parameters writes StaticVoltageOffsetVid[8] likewise; populate_smc_uvd_levels writes UvdDpmVoltageIndex[8] and READS vclk/dclk dpm_levels[i] ([8]) for i < mm_dep_table->count; populate_vddc_soc_levels writes SocVid[8] for i < vddc_lookup_table->count. The guarded twin at :1850 (NUM_DSPCLK_LEVELS assert) proves these checks were simply omitted. Crafted-VBIOS Vega10 at driver load: up to ~247 attacker-influenced bytes per sink written past the arrays into the SMC upload block and adjacent vega10_hwmgr heap state -> corruption -> crash to privesc on unhardened configs.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-3705 Β· 10 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 2.1 KB | β raw | |
| VERDICT.md | β | 2.3 KB | β raw | |
| trigger_notes.c | β | 829 B | view raw | |
| build.sh | β | 81 B | view raw | |
| run.sh | β | 162 B | view raw | |
| run.log | β | 1.3 KB | view raw | |
| env.txt | β | 848 B | view raw | |
| fix.diff | β | 1.7 KB | view raw | |
| manifest.json | β | 1.0 KB | view raw | |
| verdict.json | β | 2.7 KB | view raw |
DF-3705 β vega10 SMC PPTable populate loops write 8-entry SMC arrays with unbounded VBIOS dep-table counts (heap OOB writes)
File: sys/dev/drm/amd/powerplay/hwmgr/vega10_hwmgr.c (pass 2). smu7 checklist item (h): SMC-table upload level caps.
Sinks (destination arrays are all size 8 inside PPTable_t pp_table in the kzalloc'd
struct vega10_hwmgr; the only bound is the VBIOS ucNumEntries count, <=255):
1. vega10_populate_clock_stretcher_table :2060-2064 β
pp_table->CksEnable[i] / CksVidOffset[i] for i < vdd_dep_on_sclk->count
(CksEnable[8]/CksVidOffset[8], smu9_driver_if.h:229-230). Attacker byte = cks_enable /
scaled cks_voffset.
2. vega10_populate_avfs_parameters :2143-2145 β StaticVoltageOffsetVid[i] for i <
vdd_dep_on_sclk->count (StaticVoltageOffsetVid[8], smu9_driver_if.h:294).
3. vega10_populate_smc_uvd_levels :2030-2045 β UvdDpmVoltageIndex[i] (8, :187) for i <
mm_dep_table->count; the same loop also READS vclk/dclk_dpm_table->dpm_levels[i]
(dpm_levels[8]) for i < mm count β OOB read; :2043 pads from entries[count-1].
4. vega10_populate_vddc_soc_levels :1700-1708 β SocVid[i] (SocVid[NUM_EVV_VOLTAGE_LEVELS=8],
smu9_driver_if.h:167) for i < vddc_lookup_table->count (amplifier: requires lookup count>8,
which the get_vddc_lookup_table anomaly (vega10_processpptables.c:1032 stores
count=ucNumEntries into an 8-entry alloc) can produce).
Contrast (the correct pattern already in this file)
vega10_populate_single_display_type :1850-1852 guards with
PP_ASSERT_WITH_CODE(dep_table->count <= NUM_DSPCLK_LEVELS, ...) before its loop β the four
sinks above lack exactly this guard.
Trigger (requires AMD Vega10 hardware)
Craft VBIOS: GFXCLK dependency ucNumEntries=32 with cks fields set (clock stretcher enabled via feature mask), or MM dependency ucNumEntries=16. Driver enable_dpm_tasks (:2911-2919) β vega10_init_smc_table (:2550-2554 clock stretcher; :2545 uvd) writes up to 255 bytes past CksEnable[8]/UvdDpmVoltageIndex[8] into the following PPTable_t fields (SMC upload block) and beyond within struct vega10_hwmgr.
PoC status
Guest virtio-only β inconclusive, source-certain.
DF-3705 VERDICT β SMC populate level-cap overflows
status: inconclusive (hardware-gated) β source-certain. reproduced=0 on audit guest.
Proof chain (lines verified in guest /usr/src, see run.log)
- Destinations: smu9_driver_if.h:167 SocVid[8], :229-230 CksEnable[8]/CksVidOffset[8],
:294 StaticVoltageOffsetVid[8], :187 UvdDpmVoltageIndex[8] β all inside
PPTable_t pp_table=data->smc_state_table.pp_table, embedded in the kzalloc'd struct vega10_hwmgr. - Sources: vdd_dep_on_sclk / mm_dep_table counts = raw ATOM ucNumEntries (<=255), allocated to match (vega10_processpptables.c:540/:313) β unbounded w.r.t. the 8-entry SMC arrays.
- Loops with no cap: - vega10_hwmgr.c:2060-2064 (CksEnable/CksVidOffset, dep_on_sclk count) - :2143-2145 (StaticVoltageOffsetVid, dep_on_sclk count) - :2030-2045 (UvdDpmVoltageIndex writes; vclk/dclk dpm_levels[8] reads; mm count) - :1700-1708 (SocVid, vddc_lookup_table->count; >8 via the lookup-parse anomaly) The one guarded twin (:1850-1852 NUM_DSPCLK_LEVELS assert) proves the intended pattern.
- Reach: vega10_enable_dpm_tasks :2911-2919 β vega10_init_smc_table :2545/:2550-2554/:2533 β the four sinks, at driver load / resume on the crafted-VBIOS card.
Impact: attacker-influenced byte writes (cks_enable/cks_voffset-derived/sclk_offset-derived/ vddcInd-derived VID bytes) at monotonically increasing offsets past 8-entry arrays into the SMC upload block and adjacent vega10_hwmgr state; count 255 β up to ~247 bytes per sink, three sinks chainable in one init. Heap corruption β crash or worse on unhardened configs. Severity High under the crafted-VBIOS threat model.
Guest result
0 AMD PCI devices; amdgpu.ko never binds. Sink loops + array defs confirmed in guest /usr/src (run.log). Verdict: inconclusive, source-certain.
Fix
fix.diff adds the missing count <= ARRAY_SIZE asserts (returning -EINVAL) exactly
mirroring :1850-1852 for the four sinks.
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.diff authored against read-only sys/ tree; no AMD hw on guest so no patched-kernel validation possible
fix.diff; run.log hardware gate
Confirmed kernel references
- sys/dev/drm/amd/powerplay/hwmgr/vega10_hwmgr.c:2060
- sys/dev/drm/amd/powerplay/hwmgr/vega10_hwmgr.c:2143
- sys/dev/drm/amd/powerplay/hwmgr/vega10_hwmgr.c:2030
- sys/dev/drm/amd/powerplay/hwmgr/vega10_hwmgr.c:1700
- sys/dev/drm/amd/powerplay/inc/smu9_driver_if.h:229
- sys/dev/drm/amd/powerplay/inc/smu9_driver_if.h:294
Detail
Exploit chain
crafted VBIOS (GFXCLK dep ucNumEntries>8 + clock-stretcher/AVFS/MM tables) -> driver load -> populate loops write past 8-entry SMC arrays -> heap corruption in struct vega10_hwmgr -> crash/privesc primitive on unhardened configs (hardware-gated, not demonstrated)
Evidence (decisive lines)
VERDICT.md proof chain; run.log (guest-src excerpts :2060-2063, :2143-2145, :2030-2036, :1700-1704, array defs); trigger_notes.c; fix.diff
PoC changes
no runnable PoC possible (hardware-gated); documented VBIOS recipe per sink
Verified recommended fix
Add count<=ARRAY asserts before each loop mirroring :1850-1852 (fix.diff)
Verdict
Source-certain heap OOB writes at SMC-table population: vega10_populate_clock_stretcher_table (:2060-2064 CksEnable[8]/CksVidOffset[8]), vega10_populate_avfs_parameters (:2143-2145 StaticVoltageOffsetVid[8]), vega10_populate_smc_uvd_levels (:2030-2045 UvdDpmVoltageIndex[8] + dpm_levels[8] OOB reads) and vega10_populate_vddc_soc_levels (:1700-1708 SocVid[8]) all loop on VBIOS dep-table ucNumEntries (<=255, uncapped) while the SMC arrays are 8 (smu9_driver_if.h:167,187,229-230,294); the guarded twin at :1850-1852 shows the missing check. Up to ~247 attacker-influenced bytes per sink written into the PPTable upload block and adjacent vega10_hwmgr heap state at driver load. Not reproducible on the virtio-only audit guest (0 AMD PCI devices); lines verified in guest /usr/src.
No comments yet.