β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-3689

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)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-3689 Β· 11 files
FileTypeDescriptionSize
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).

VERDICT.md
↓ download raw

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)

What was verified, and how

  1. 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.
  2. Harness on the guest (DragonFly 6.5-DEVELOPMENT x86_64, cc base toolchain): vddind_oob_read.c reproduces the exact kernel code shape and/or arithmetic with 1:1 struct layouts. Output in run.log. Compiled with -O0 β€” at -O2 the compiler detects the array-index UB and misoptimizes the accounting loops (itself a hint of what the kernel code does).
  3. 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_testable
baseline no→ patch + rebuild →patched clean

fix.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
↓ fix.diffper-fix-DF-3689

Confirmed kernel references

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].