Unbounded sprintf loops in smu7_print_clock_levels overflow the 4096-byte sysfs page buffer once the VBIOS DPM count exceeds ~233
Summary
The print loops run to the unvalidated VBIOS-driven count (up to 255, DF-1166 premise) emitting one line per level into the raw sysfs/kernfs page buffer; measured with the exact format strings: 255 PP_PCIE or u32-clock PP_SCLK lines = 4481 bytes > PAGE_SIZE(4096), and the per-line dpm_levels[i] reads for i >= 8 are themselves OOB reads of dpm_levels[8]. HARNESS-VERIFIED: 'PP_PCIE count=255 -> 4481 bytes OVERFLOWS PAGE_SIZE(4096)'. Once a crafted VBIOS sets count > ~233, an UNPRIVILEGED read of pp_dpm_pcie/pp_dpm_sclk/pp_od_clk_voltage overflows the kernfs heap page by ~385 bytes with attacker-influenced text.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-3688 Β· 11 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 2.1 KB | β raw | |
| VERDICT.md | β | 3.0 KB | β raw | |
| sprintf_budget.c | β | 2.3 KB | view raw | |
| build.sh | β | 108 B | view raw | |
| run.sh | β | 154 B | view raw | |
| build.log | β | 69 B | view raw | |
| run.log | β | 619 B | view raw | |
| env.txt | β | 1.1 KB | view raw | |
| fix.diff | β | 1.8 KB | view raw | |
| verdict.json | β | 2.9 KB | view raw | |
| manifest.json | β | 970 B | view raw |
DF-3688 β Unbounded sprintf loop in smu7_print_clock_levels overflows the 4096-byte sysfs page buffer once the VBIOS DPM count exceeds ~233
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:L/A:H | CWE: CWE-787 Out-of-bounds Write; CWE-120 Buffer Copy without Checking Size of Input | Confidence: certain (source)
What this is
Source-certain output-budget overflow, precondition shared with DF-1166 (VBIOS dep count > 8 is unvalidated); runtime-inconclusive on this guest (no AMD GPU). smu7_print_clock_levels sprintf()s one line per DPM level into the caller's sysfs page buffer (amdgpu_pm.c amdgpu_get_pp_dpm_sclk/mclk/pcie pass the raw 4096-byte kernfs buf; pp_od_clk_voltage likewise). The loops run to sclk_table->count / mclk_table->count / pcie_table->count / odn_*->num_of_pl - all ultimately the unvalidated VBIOS u8 count (up to 255). Measured on the guest with the exact format strings: 255 PP_PCIE lines = 4481 bytes and 255 PP_SCLK lines with u32-scale clocks = 4481 bytes, both overflowing PAGE_SIZE; the per-line reads dpm_levels[i] for i >= 8 are themselves OOB reads of the dpm_levels[8] arrays. Impact: heap overflow of the kernfs page on an UNPRIVILEGED sysfs read once the count is corrupted by a crafted VBIOS; ~385 bytes past the page.
Reproduce (harness on the audit QEMU guest)
scp -F dfbsd-qemu/config sprintf_budget.c dfbsd-maxx:/tmp/DF-3688/ ssh -F dfbsd-qemu/config dfbsd-maxx 'cd /tmp/DF-3688 && cc -Wall -O0 -o t sprintf_budget.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-3688 β verdict narrative
Finding
Source-certain output-budget overflow, precondition shared with DF-1166 (VBIOS dep count > 8 is unvalidated); runtime-inconclusive on this guest (no AMD GPU). smu7_print_clock_levels sprintf()s one line per DPM level into the caller's sysfs page buffer (amdgpu_pm.c amdgpu_get_pp_dpm_sclk/mclk/pcie pass the raw 4096-byte kernfs buf; pp_od_clk_voltage likewise). The loops run to sclk_table->count / mclk_table->count / pcie_table->count / odn_*->num_of_pl - all ultimately the unvalidated VBIOS u8 count (up to 255). Measured on the guest with the exact format strings: 255 PP_PCIE lines = 4481 bytes and 255 PP_SCLK lines with u32-scale clocks = 4481 bytes, both overflowing PAGE_SIZE; the per-line reads dpm_levels[i] for i >= 8 are themselves OOB reads of the dpm_levels[8] arrays. Impact: heap overflow of the kernfs page on an UNPRIVILEGED sysfs read once the count is corrupted by a crafted VBIOS; ~385 bytes past the page.
Root cause (kernel references)
sys/dev/drm/amd/powerplay/hwmgr/smu7_hwmgr.c:4428sys/dev/drm/amd/powerplay/hwmgr/smu7_hwmgr.c:4444sys/dev/drm/amd/powerplay/hwmgr/smu7_hwmgr.c:4458sys/dev/drm/amd/powerplay/hwmgr/smu7_hwmgr.c:4468sys/dev/drm/amd/amdgpu/amdgpu_pm.c:650
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):sprintf_budget.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-3688/fix.diff
Confirmed kernel references
Detail
Evidence (decisive lines)
run.log: 'PP_SCLK count=255, u32 clocks -> 4481 bytes OVERFLOWS PAGE_SIZE(4096)', 'PP_PCIE count=255 -> 4481 bytes OVERFLOWS PAGE_SIZE(4096)'; VERDICT.md; fix.diff
PoC changes
No seed; wrote a budget harness replaying the exact format strings with measured line lengths (compile at -O0; -O2 sees the UB in the index math and misoptimizes unrelated accounting).
Verified recommended fix
Cap every print loop at MAX_REGULAR_DPM_NUMBER (the dpm_levels array bound) and at the odn entries bound; the root count validation is DF-1166's fix.
Verdict
Source-certain output-budget overflow, precondition shared with DF-1166 (VBIOS dep count > 8 is unvalidated); runtime-inconclusive on this guest (no AMD GPU). smu7_print_clock_levels sprintf()s one line per DPM level into the caller's sysfs page buffer (amdgpu_pm.c amdgpu_get_pp_dpm_sclk/mclk/pcie pass the raw 4096-byte kernfs buf; pp_od_clk_voltage likewise). The loops run to sclk_table->count / mclk_table->count / pcie_table->count / odn_*->num_of_pl - all ultimately the unvalidated VBIOS u8 count (up to 255). Measured on the guest with the exact format strings: 255 PP_PCIE lines = 4481 bytes and 255 PP_SCLK lines with u32-scale clocks = 4481 bytes, both overflowing PAGE_SIZE; the per-line reads dpm_levels[i] for i >= 8 are themselves OOB reads of the dpm_levels[8] arrays. Impact: heap overflow of the kernfs page on an UNPRIVILEGED sysfs read once the count is corrupted by a crafted VBIOS; ~385 bytes past the page.
No comments yet.