Heap OOB read via unbounded state-record clock indices into VBIOS dep tables
Summary
vega10_get_pp_table_entry_callback_func (:2970-3063): ucSoc/Gfx/MemClockIndexLow/High (u8 from VBIOS state record, 0-255) used to index socclk/gfxclk/mclk_dep_table->entries[] with NO bounds check against ucNumEntries. Index>count -> OOB read past dep-table allocation into adjacent kernel heap. Read values become power-state clocks exposed via sysfs. Same class as DF-1168 (smu7). Fix: validate each index < table->ucNumEntries.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-1182 Β· 12 files| File | Type | Description | Size | |
|---|---|---|---|---|
| harness.c | trigger-source | userspace transcription of the 6 state-record clock-index sites | 3.4 KB | view raw |
| build.sh | build-script | cc -O2 -o harness harness.c | 101 B | view raw |
| run.sh | run-script | ./harness | 67 B | view raw |
| README.md | readme | human-readable summary + reproduce | 2.5 KB | β raw |
| VERDICT.md | verdict | detailed mechanism, source trace, why-not-reproduced | 2.2 KB | β raw |
| fix.diff | suggested-fix | add single early bounds check covering all 6 uc*ClockIndex* fields | 958 B | view raw |
| run.log | run-log | harness stdout showing 6 OOB reads | 829 B | view raw |
| fix_build.log | fix-build-log | nativekernel build log showing vega10_hwmgr.c compiles cleanly with fix applied (rc=0, -Werror) | 5.6 MB | β download |
| fix_run.log | fix-run-log | harness on patched kernel (no regression) | 126 B | view raw |
| env.txt | environment | uname, cc version, pciconf | 947 B | view 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-1182 β vega10 state-record clock indices OOB read
Bug (confirmed in source)
sys/dev/drm/amd/powerplay/hwmgr/vega10_hwmgr.c:3042-3062 (inside
vega10_get_pp_table_entry_callback_func):
performance_level->soc_clock = socclk_dep_table->entries
[state_entry->ucSocClockIndexLow].ulClk;
performance_level->gfx_clock = gfxclk_dep_table->entries
[state_entry->ucGfxClockIndexLow].ulClk;
performance_level->mem_clock = mclk_dep_table->entries
[state_entry->ucMemClockIndexLow].ulMemClk;
...
performance_level->soc_clock = socclk_dep_table->entries
[state_entry->ucSocClockIndexHigh].ulClk;
if (gfxclk_dep_table->ucRevId == 0) {
performance_level->gfx_clock = gfxclk_dep_table->entries
[state_entry->ucGfxClockIndexHigh].ulClk;
} else if (gfxclk_dep_table->ucRevId == 1) {
patom_record_V2 = (ATOM_Vega10_GFXCLK_Dependency_Record_V2 *)gfxclk_dep_table->entries;
performance_level->gfx_clock = patom_record_V2[state_entry->ucGfxClockIndexHigh].ulClk;
}
performance_level->mem_clock = mclk_dep_table->entries
[state_entry->ucMemClockIndexHigh].ulMemClk;
The uc*ClockIndexLow/High fields are uint8_t (0-255) read from the
VBIOS state record ATOM_Vega10_State. They index into the dep tables
without any check against the table's ucNumEntries. Same class as
DF-1168 (smu7).
Allocation sizing (in vega10_processpptables.c): each dep table is
kzalloc(sizeof(uint32_t) + sizeof(record) * ucNumEntries, ...). With
ucNumEntries = 2 and ucMemClockIndexHigh = 99, the kernel reads
99 * sizeof(record) (~800 bytes) past the 2-entry allocation. Read
values become the power-state clocks surfaced via sysfs.
Trigger model
- Requires
amdgpupowerplay attached to a real Vega10 GPU. - CVSS
AV:L/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:Hβ analyst'sPR:Lassumes an unprivileged user can supply a crafted PowerPlay table; in this code the table comes from device firmware at attach time soPR:His the realistic bar.
On this guest
Unreachable β no AMD GPU; amdgpu/powerplay never attaches. harness.c
transcribes the indexing logic into userspace; it builds and runs,
printing which indices would OOB-read, but produces no kernel effect.
Reproduce
ssh dfbsd-maxx "cd poc/DF-1182 && ./build.sh && ./run.sh"
Recommended fix
Validate each of the six uc*ClockIndex* fields against the matching
dep_table->ucNumEntries before any dereference. Full patch in
fix.diff. Matches the finding proposal.
DF-1182 β VERDICT
Status: NOT REPRODUCED (HW-gated; bug confirmed in source) Impact: none (cannot trigger on this guest) Confidence: certain (line-by-line source trace) Class: heap OOB read (CWE-125) β latent
Mechanism (cited)
vega10_hwmgr.c:2970-3063 β vega10_get_pp_table_entry_callback_func
is called by vega10_get_pp_table_entry (line 3066) for each VBIOS
power state. It reads six uint8_t clock indices from the
ATOM_Vega10_State record and indexes the dep tables without any
bounds check:
// lines 3042-3062 (six OOB-read sites)
performance_level->soc_clock = socclk_dep_table->entries[state_entry->ucSocClockIndexLow].ulClk;
performance_level->gfx_clock = gfxclk_dep_table->entries[state_entry->ucGfxClockIndexLow].ulClk;
performance_level->mem_clock = mclk_dep_table->entries[state_entry->ucMemClockIndexLow].ulMemClk;
performance_level->soc_clock = socclk_dep_table->entries[state_entry->ucSocClockIndexHigh].ulClk;
performance_level->gfx_clock = gfxclk_dep_table->entries[state_entry->ucGfxClockIndexHigh].ulClk;
performance_level->mem_clock = mclk_dep_table->entries[state_entry->ucMemClockIndexHigh].ulMemClk;
The dep tables (socclk_dep_table, gfxclk_dep_table, mclk_dep_table)
are pointers into the VBIOS PowerPlay table at offsets chosen by the
firmware; their ucNumEntries governs the valid range. Same class as
DF-1168 (smu7).
Why it cannot be reproduced on this guest
- No AMD GPU on the PCI bus.
amdgpu/powerplayare not inX86_64_GENERIC; module present at/boot/kernel/amdgpu.kobut never attaches.- The PowerPlay table is parsed from device firmware at attach time, so
even with hardware the trigger requires a malicious VBIOS (
PR:H).
Latent / hardware-gated bug; confirmed real by source trace,
unreachable on the audit guest. harness.c transcribes the indexing
logic into userspace; it builds, runs, and prints which indices would
OOB-read, but produces no kernel effect.
Fix
fix.diff adds a single early bounds check covering all six
uc*ClockIndex* fields, returning -EINVAL if any is out of range.
Validated as applies + compiles only β fix_status: not_testable
(the bug path cannot be exercised without AMD hardware).
Fix verification
not_testablecompile+harness validated
kernel build rc=0 + harness
Confirmed kernel references
β
Detail
Exploit chain
none
Evidence (decisive lines)
β
Verdict
Source+harness. vega10 unbounded state-record clock indices -> heap OOB read. amdgpu not in GENERIC.
No comments yet.