Heap OOB read via unvalidated u8 vddInd/vddcInd from VBIOS indexing the voltage-lookup tables
Summary
smu7_patch_voltage_dependency_tables_with_lookup_table and smu7_patch_voltage_workaround index vddc/vddgfx_lookup_table->entries[vddInd] with a raw VBIOS u8 (0..255) while the tables are kmalloc'd for 16 records and count may be smaller: reads land up to 2550 bytes past entries[0]. The garbage becomes the programmed vddc (hardware-safety impact); no direct userspace disclosure found. DF-1168 covers the clock-index sinks - this is the distinct vddInd sink. HARNESS-VERIFIED: 'dep entries whose vddInd read lands past the 16-record kmalloc: 239 / 255', 'max index 255 -> byte offset 2550'.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-3689 Β· 11 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 2.3 KB | β raw | |
| VERDICT.md | β | 3.3 KB | β raw | |
| vddind_oob_read.c | β | 2.5 KB | view raw | |
| build.sh | β | 109 B | view raw | |
| run.sh | β | 154 B | view raw | |
| build.log | β | 70 B | view raw | |
| run.log | β | 365 B | view raw | |
| env.txt | β | 1.1 KB | view raw | |
| fix.diff | β | 1.4 KB | view raw | |
| verdict.json | β | 3.2 KB | view raw | |
| manifest.json | β | 1.0 KB | view raw |
DF-3689 β Heap OOB read via unvalidated u8 vddInd/vddcInd from VBIOS indexing the voltage-lookup tables
File: sys/dev/drm/amd/powerplay/hwmgr/smu7_hwmgr.c (pass 2, GLM 5.3 re-audit)
Severity: Low | CVSS: CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:H | CWE: CWE-125 Out-of-bounds Read | Confidence: certain (source)
What this is
Source-certain heap OOB read; runtime-inconclusive on this guest (no AMD GPU). smu7_patch_voltage_dependency_tables_with_lookup_table uses each dependency record's vddInd/vddcInd - a raw u8 (0..255) from the VBIOS ATOM tables (process_pptables_v1_0.c:398 ucVddcInd etc.) - to index vddc/vddgfx_lookup_table->entries[] with no range check (:1860-1862, :1866-1868, :1873-1875, :1879-1881); smu7_patch_voltage_workaround does the same at :2135. The lookup tables are kmalloc'd for max_levels=16 records (process_pptables_v1_0.c:286-287/:295-296) while their count field is the VBIOS ucNumEntries, so indices above both 16 and count read adjacent kernel heap. The garbage value read is stored into the dependency record's vddc and later programs real voltages (hardware-safety impact) and feeds smu7_calc_voltage_dependency_tables; no direct userspace disclosure was found (sysfs pp_table dumps the raw atom table, not the patched records). DF-1168 covers the ucMemoryClockIndex/ucEngineClockIndex sinks; this is the distinct vddInd sink family. Harness: 239/255 crafted dep entries read past the 16-record allocation, max offset 2550 bytes past entries[0].
Reproduce (harness on the audit QEMU guest)
scp -F dfbsd-qemu/config vddind_oob_read.c dfbsd-maxx:/tmp/DF-3689/ ssh -F dfbsd-qemu/config dfbsd-maxx 'cd /tmp/DF-3689 && cc -Wall -O0 -o t vddind_oob_read.c && ./t'
Expected: the run prints the confirmed-primitive line (RESULT: ... CONFIRMED ...).
See run.log (and run.2.log where present) for the decisive captured output,
build.log for the guest toolchain transcript, env.txt for the guest environment
(virtio-only, no AMD GPU β pciconf -l shows only 0x1237/0x1111 QEMU devices;
drm.ko/amdgpu.ko exist in /boot/kernel but are not loaded and have no hardware
to bind), which is why the in-kernel trigger itself is inconclusive on this guest.
Fix
fix.diff (authored against the read-only sys/ tree; never applied there).
DF-3689 β verdict narrative
Finding
Source-certain heap OOB read; runtime-inconclusive on this guest (no AMD GPU). smu7_patch_voltage_dependency_tables_with_lookup_table uses each dependency record's vddInd/vddcInd - a raw u8 (0..255) from the VBIOS ATOM tables (process_pptables_v1_0.c:398 ucVddcInd etc.) - to index vddc/vddgfx_lookup_table->entries[] with no range check (:1860-1862, :1866-1868, :1873-1875, :1879-1881); smu7_patch_voltage_workaround does the same at :2135. The lookup tables are kmalloc'd for max_levels=16 records (process_pptables_v1_0.c:286-287/:295-296) while their count field is the VBIOS ucNumEntries, so indices above both 16 and count read adjacent kernel heap. The garbage value read is stored into the dependency record's vddc and later programs real voltages (hardware-safety impact) and feeds smu7_calc_voltage_dependency_tables; no direct userspace disclosure was found (sysfs pp_table dumps the raw atom table, not the patched records). DF-1168 covers the ucMemoryClockIndex/ucEngineClockIndex sinks; this is the distinct vddInd sink family. Harness: 239/255 crafted dep entries read past the 16-record allocation, max offset 2550 bytes past entries[0].
Root cause (kernel references)
sys/dev/drm/amd/powerplay/hwmgr/smu7_hwmgr.c:1860sys/dev/drm/amd/powerplay/hwmgr/smu7_hwmgr.c:1866sys/dev/drm/amd/powerplay/hwmgr/smu7_hwmgr.c:1873sys/dev/drm/amd/powerplay/hwmgr/smu7_hwmgr.c:1879sys/dev/drm/amd/powerplay/hwmgr/smu7_hwmgr.c:2135sys/dev/drm/amd/powerplay/hwmgr/process_pptables_v1_0.c:286
What was verified, and how
- Source-level certainty. Full-file pass-2 audit of smu7_hwmgr.c (5147 lines) with the parse-side preconditions traced into processpptables.c / process_pptables_v1_0.c (VBIOS u8 counts unvalidated; offset==0 subtables parse-legal NULL) and the struct geometry verified in ppatomctrl.h / smu7_hwmgr.h / hardwaremanager.h / dm_pp_interface.h.
- Harness on the guest (DragonFly 6.5-DEVELOPMENT x86_64,
ccbase toolchain):vddind_oob_read.creproduces the exact kernel code shape and/or arithmetic with 1:1 struct layouts. Output inrun.log. Compiled with-O0β at-O2the compiler detects the array-index UB and misoptimizes the accounting loops (itself a hint of what the kernel code does). - Hardware absence. The guest is virtio-only: no AMD GPU on PCI, amdgpu not loaded. The in-kernel trigger requires an SMU7 ASIC (CI/Topaz/Tonga/Fiji/Polaris/ Vegam) with a crafted VBIOS, VFIO passthrough of a malicious card, or a root sysfs pp_table write. Per pass-2 spawn instructions this is recorded as inconclusive (matching DF-3585/DF-3619/DF-3644 precedent for the same class), impact none on this guest.
Fix validation
fix.diff authored post-verification against the read-only sys/ tree. Not built: a
kernel rebuild with drm cannot be exercised on a GPU-less guest, so fix validation is
not_testable; the diffs are minimal, local, and mechanically checkable against the
cited lines.
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).
findings/poc/DF-3689/fix.diff
Confirmed kernel references
- sys/dev/drm/amd/powerplay/hwmgr/smu7_hwmgr.c:1860
- sys/dev/drm/amd/powerplay/hwmgr/smu7_hwmgr.c:1866
- sys/dev/drm/amd/powerplay/hwmgr/smu7_hwmgr.c:1873
- sys/dev/drm/amd/powerplay/hwmgr/smu7_hwmgr.c:1879
- sys/dev/drm/amd/powerplay/hwmgr/smu7_hwmgr.c:2135
- sys/dev/drm/amd/powerplay/hwmgr/process_pptables_v1_0.c:286
Detail
Evidence (decisive lines)
run.log: 'dep entries whose vddInd read lands past the 16-record kmalloc: 239 / 255', 'max index 255 -> byte offset 2550 past entries[0] (allocation holds 160 bytes of entries)'; VERDICT.md; fix.diff
PoC changes
No seed; wrote an allocation-geometry harness computing which vddInd values read past the 16-record kmalloc block.
Verified recommended fix
Validate vddInd/vddcInd (and the workaround's index) against the target lookup table's count before indexing, returning -EINVAL on out-of-range.
Verdict
Source-certain heap OOB read; runtime-inconclusive on this guest (no AMD GPU). smu7_patch_voltage_dependency_tables_with_lookup_table uses each dependency record's vddInd/vddcInd - a raw u8 (0..255) from the VBIOS ATOM tables (process_pptables_v1_0.c:398 ucVddcInd etc.) - to index vddc/vddgfx_lookup_table->entries[] with no range check (:1860-1862, :1866-1868, :1873-1875, :1879-1881); smu7_patch_voltage_workaround does the same at :2135. The lookup tables are kmalloc'd for max_levels=16 records (process_pptables_v1_0.c:286-287/:295-296) while their count field is the VBIOS ucNumEntries, so indices above both 16 and count read adjacent kernel heap. The garbage value read is stored into the dependency record's vddc and later programs real voltages (hardware-safety impact) and feeds smu7_calc_voltage_dependency_tables; no direct userspace disclosure was found (sysfs pp_table dumps the raw atom table, not the patched records). DF-1168 covers the ucMemoryClockIndex/ucEngineClockIndex sinks; this is the distinct vddInd sink family. Harness: 239/255 crafted dep entries read past the 16-record allocation, max offset 2550 bytes past entries[0].
No comments yet.