VBIOS usVoltageIndex (USHORT 0..65535) OOB heap write in sumo_construct_vid_mapping_table β up to 5 chosen-offset 16-bit-pair writes into/after the kv_power_info heap object
Summary
sumo_construct_vid_mapping_table stores vid_7bit/vid_2bit at entries[usVoltageIndex] where entries[] has 4 slots but usVoltageIndex is an unvalidated USHORT from the VBIOS. No bounds check exists; index>=4 writes at entries+4*idx..+4*idx+2, any 4-byte-aligned slot in entries+[4..262142]. vid_mapping_table sits ~160 bytes into the kzalloc'd ~1.3KB kv_power_info whose fields include SMC SRAM write offsets, graphics_level[8], and level counts; beyond ~1.3KB the writes hit adjacent slab objects. Executed unconditionally at every Kaveri/Kabini/Mullins boot. Values semi-chosen. VERIFIED: harness proves the write set (entries+1200..+1210 payload 0xBEEF/0xCAFE/0x1234 for indices 300-302) and SIGSEGV at +262140 on the verbatim store. VBIOS-image control (physical flash/supply-chain/passthrough) -> boot-time heap OOB write with chosen offset and semi-chosen u16 data.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-3887 Β· 11 files| File | Type | Description | Size | |
|---|---|---|---|---|
| vid_mapping_oob_write.c | β | 6.1 KB | view raw | |
| build.sh | β | 95 B | view raw | |
| run.sh | β | 116 B | view raw | |
| build.log | β | 107 B | view raw | |
| run.log | β | 654 B | view raw | |
| run.2.log | β | 88 B | view raw | |
| env.txt | β | 561 B | view raw | |
| fix.diff | β | 810 B | view raw | |
| VERDICT.md | β | 5.2 KB | β raw | |
| README.md | β | 1.8 KB | β raw | |
| verdict.json | β | 3.4 KB | view raw |
DF-3887 β VBIOS u16 voltage-index OOB heap write in sumo_construct_vid_mapping_table
File: sys/dev/drm/amd/amdgpu/kv_dpm.c:167-170 (function :159-191, reached from kv_parse_sys_info_table :2613)
Class: CWE-787 out-of-bounds write (VBIOS-controlled index into a 4-slot array)
Verdict: inconclusive at runtime on this guest (no AMD GPU β hardware-gated); source-certain; harness proves the exact write set + SIGSEGV on the verbatim access.
Build (on the DragonFly guest, as root)
mkdir -p /tmp/DF-3887 && cp vid_mapping_oob_write.c /tmp/DF-3887/h.c cd /tmp/DF-3887 && cc -Wall -O0 -o t h.c
Run
./t # accounting mode β exact byte offsets written outside entries[] ./t crash # verbatim kernel write with usVoltageIndex=65535 -> SIGSEGV (exit 139)
Expected
- accounting:
OOB WRITE: entries+1200 = 0x012c,entries+1202 = 0xbeef,entries+1204 = 0x012d,entries+1206 = 0xcafe,entries+1208 = 0x012e,entries+1210 = 0x1234β six 16-bit OOB writes forusVoltageIndex300..302; the fully-VBIOS-controlledusVoltageID(0xBEEF/0xCAFE/0x1234) and the index itself are the payload. Max index 65535 β write windowentries + [4 .. 262142]bytes, versus a 16-byte array inside a ~1.3 KBkv_power_infoheap object. - crash:
Segmentation fault (core dumped), exit=139 (kernel equivalent: page fault in kernel mode β panic). - The
entries-4 = 0x0002line in run.log is the in-structnum_entriesfield being legally written by the compaction loop β not corruption.
Kernel trigger
KV/KV-based APU (CHIP_KAVERI/KABINI/MULLINS) with a crafted VBIOS whose
IntegratedSystemInfo V1_8 sAvail_SCLK[i].usVoltageIndex >= 4 β executed
unconditionally at every kv_dpm_init (driver load / boot).
DF-3887 β verdict narrative
Finding
sumo_construct_vid_mapping_table() (sys/dev/drm/amd/amdgpu/kv_dpm.c:159-191)
fills pi->sys_info.vid_mapping_table.entries[] directly from the VBIOS:
for (i = 0; i < SUMO_MAX_HARDWARE_POWERLEVELS; i++) { /* :165 */
if (table[i].ulSupportedSCLK != 0) { /* :166 */
vid_mapping_table->entries[table[i].usVoltageIndex].vid_7bit = /* :167 */
table[i].usVoltageID;
vid_mapping_table->entries[table[i].usVoltageIndex].vid_2bit = /* :169 */
table[i].usVoltageIndex;
}
}
entries[] is struct sumo_vid_mapping_entry entries[SUMO_MAX_NUMBER_VOLTAGES]
(kv_dpm.h) β 4 slots Γ 4 bytes = 16 bytes. usVoltageIndex is a USHORT
(0..65535) from ATOM_AVAILABLE_SCLK_LIST (sys/dev/drm/amd/include/atombios.h:5705-5710),
copied verbatim out of the card's VBIOS ATOM_INTEGRATED_SYSTEM_INFO_V1_8.sAvail_SCLK[5]
(atombios.h:6172). There is no bounds check anywhere between the VBIOS image and
the array store. A VBIOS (flash-modified, supply-chain, or VM firmware-passthrough)
with usVoltageIndex >= 4 executes up to 5 chosen-offset 16-bit-pair writes:
- write 1 (value):
usVoltageIDβ fully attacker-chosen 16-bit - write 2 (value):
usVoltageIndexitself - offsets:
entries + 4*usVoltageIndexand+2, i.e. any 4-byte-aligned slot inentries + [4 .. 262142]
vid_mapping_table lives ~160 bytes into the heap kv_power_info
(kzalloc at kv_dpm.c:2797, ~1.3 KB: graphics_level[8], SMC offsets
dpm_table_start/sram_end, level counts, boot levels...). The write window
therefore covers both intra-object corruption (e.g. pi->dpm_table_start, which
later becomes the destination offset of amdgpu_kv_copy_bytes_to_smc writes, and
graphics_dpm_level_count) and, beyond ~1.3 KB, adjacent kernel heap objects β a
genuine arbitrary-offset heap OOB write with (two-u16) attacker-controlled data,
executed at every Kaveri/Kabini/Mullins boot (kv_dpm_init β kv_parse_sys_info_table
:2613).
This is distinct from pass-1 DF-1268 (unbounded counts overflowing the fixed
graphics_level/uvd_level/... arrays via kv_init_graphics_levels/kv_populate_*)
and DF-1269 (unvalidated indices/offsets in kv_parse_power_table causing OOB
reads): the victim here is sys_info.vid_mapping_table.entries[], the input is the
IGP table's sAvail_SCLK (not the powerplay tables), and the primitive is a write.
Root cause (kernel references)
sys/dev/drm/amd/amdgpu/kv_dpm.c:167-170β uncheckedusVoltageIndexstoresys/dev/drm/amd/amdgpu/kv_dpm.hβentries[SUMO_MAX_NUMBER_VOLTAGES /* 4 */]sys/dev/drm/amd/include/atombios.h:5705-5710βUSHORT usVoltageIndexsys/dev/drm/amd/amdgpu/kv_dpm.c:2613β call site (init path)
What was verified, and how
- Source-level certainty. Full-file pass-2 audit; struct geometry verified in
kv_dpm.h and atombios.h (array=4Γu16-pairs, index=USHORT); call path
kv_dpm_init:2848 β kv_parse_sys_info_table:2613runs unconditionally for crev==8 IGP tables (the only supported revision, :2574-2577). - Harness on the guest (DragonFly 6.5-DEVELOPMENT x86_64, base cc 8.3):
vid_mapping_oob_write.creplicates the verbatim loop with 1:1 layouts. Accounting mode shows the exact OOB write set (entries+1200..+1210 for indices 300-302, payload 0xBEEF/0xCAFE/0x1234); crash mode executes the verbatim store withusVoltageIndex=65535against a single page mapping β write at +262140 β SIGSEGV exit=139 (kernel equivalent: page fault in kernel mode β panic; in the kernel the same store into mapped adjacent heap silently corrupts it instead). - Hardware absence. The guest is virtio-only (pciconf: no 1002:xxxx device; amdgpu.ko present but unloadable/useless). The in-kernel trigger needs a Kaveri/Kabini/Mullins APU with a crafted VBIOS (physical flash / supply chain / VM GPU firmware). Per pass-2 spawn instructions this is recorded as inconclusive, impact none on this guest (source + harness: certain).
Exploit chain (up to the hardware gate)
flash crafted VBIOS (physical/supply-chain) β boot β kv_dpm_init β OOB write
window [entries+4 .. entries+262142] inside/after the kv_power_info heap object β
corrupt dpm_table_start/sram_end (SMC SRAM write offsets used by every
amdgpu_kv_copy_bytes_to_smc), graphics_dpm_level_count, or adjacent slab objects β
second-order controlled writes to SMU SRAM / kernel heap. Not exercisable on this
guest.
Fix validation
fix.diff authored post-verification against the read-only sys/ tree (clamp
usVoltageIndex < SUMO_MAX_NUMBER_VOLTAGES before both stores, mirroring the
clock_array_index >= ucNumEntries check Linux already has at :2758). Not built:
no AMD hardware on the guest β fix_status: not_testable; the diff is minimal and
mechanically checkable against :165-171.
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 the read-only sys/ tree after source+harness verification; not built (no AMD hardware on guest to load the drm stack).
findings/poc/DF-3887/fix.diff
Confirmed kernel references
Detail
Exploit chain
flash crafted VBIOS (physical/supply-chain) -> boot -> kv_dpm_init OOB write window [entries+4 .. entries+262142] -> corrupt pi->dpm_table_start/sram_end (destinations of later amdgpu_kv_copy_bytes_to_smc writes), graphics_dpm_level_count, or adjacent heap objects -> second-order controlled SMC-SRAM/heap writes. Not exercisable on the GPU-less guest.
Evidence (decisive lines)
run.log: 'OOB WRITE: entries+1200..+1210 = 0x012c/0xbeef/0x012d/0xcafe/0x012e/0x1234', '7 OOB 16-bit writes landed OUTSIDE the 16-byte entries array'; run.2.log: 'crash mode ... +65535*4 = 262140 bytes past it' then Segmentation fault exit=139; VERDICT.md; fix.diff
PoC changes
No seed (pass-2 self-authored). Added stddef.h for ptrdiff_t after first guest build failed; guest loop verified accounting+crash modes.
Verified recommended fix
Skip sAvail_SCLK entries whose usVoltageIndex >= SUMO_MAX_NUMBER_VOLTAGES in sumo_construct_vid_mapping_table (kv_dpm.c:166), mirroring the existing clock_array_index check at :2758.
Verdict
Source-certain heap OOB write; runtime-inconclusive on this guest (no AMD GPU). sumo_construct_vid_mapping_table (kv_dpm.c:167-170) stores two u16s at vid_mapping_table->entries[usVoltageIndex] where entries[] has 4 slots (kv_dpm.h SUMO_MAX_NUMBER_VOLTAGES) but usVoltageIndex is an unvalidated USHORT (0..65535) from the VBIOS sAvail_SCLK list (atombios.h:5705). A crafted VBIOS executes up to 5 chosen-offset 16-bit-pair writes in entries+[4..262142] - covering the ~1.3KB kv_power_info heap object (dpm_table_start/sram_end SMC write offsets, graphics_level[], level counts) and adjacent slab objects - at every KV boot via kv_dpm_init -> kv_parse_sys_info_table (:2613). Harness (1:1 layouts, verbatim loop) shows the exact OOB write set (entries+1200..+1210, payload 0xBEEF/0xCAFE/0x1234 for indices 300-302) and crash mode SIGSEGVs at +262140 (kernel equivalent: page fault -> panic, or silent adjacent-heap corruption on mapped pages). Distinct from pass-1 DF-1268 (count overflow of graphics_level/uvd_level arrays) and DF-1269 (kv_parse_power_table OOB reads): different victim array, different input table, write vs read.
No comments yet.