Signed-compare bypass in smu7_odn_edit_dpm_table: negative sysfs level passes the bounds check and wraps to a u32 wild write index
Summary
'echo "s -1 <clk> <mV>" > pp_od_clk_voltage' parses -1 via kstrtol; the guard 'input[i] >= num_of_pl' compares signed long vs uint32_t (signed compare) so negatives pass, then 'uint32_t input_level = input[i]' wraps to 0xFFFFFFFF and indexes entries[8] of phm_odn_clock_levels: any u32 byte offset selectable (e.g. +51,539,607,540), writing range-checked-but-attacker-chosen clock/voltage values. Gated behind root sysfs AND od_enabled (cleared in the default mask) - root + non-default module param. HARNESS-VERIFIED: bounds check passes for -1; computed write offset 4294967295 * 12 = 51539607540 bytes.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-3687 Β· 11 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 2.4 KB | β raw | |
| VERDICT.md | β | 3.3 KB | β raw | |
| odn_negative_level.c | β | 2.7 KB | view raw | |
| build.sh | β | 112 B | view raw | |
| run.sh | β | 154 B | view raw | |
| build.log | β | 274 B | view raw | |
| run.log | β | 450 B | view raw | |
| env.txt | β | 1.1 KB | view raw | |
| fix.diff | β | 401 B | view raw | |
| verdict.json | β | 3.2 KB | view raw | |
| manifest.json | β | 980 B | view raw |
DF-3687 β Signed-compare bypass in smu7_odn_edit_dpm_table: negative sysfs level passes the bounds check and wraps to a u32 wild write index
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:H/UI:N/S:U/C:N/I:H/A:H | CWE: CWE-197 Numeric Truncation Error; CWE-787 Out-of-bounds Write | Confidence: certain (source)
What this is
Source-certain wild-write primitive gated behind root + non-default module config; runtime-inconclusive on this guest (no AMD GPU). amdgpu_set_pp_od_clk_voltage (amdgpu_pm.c:543-611) parses 's
Reproduce (harness on the audit QEMU guest)
scp -F dfbsd-qemu/config odn_negative_level.c dfbsd-maxx:/tmp/DF-3687/ ssh -F dfbsd-qemu/config dfbsd-maxx 'cd /tmp/DF-3687 && cc -Wall -O0 -o t odn_negative_level.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-3687 β verdict narrative
Finding
Source-certain wild-write primitive gated behind root + non-default module config; runtime-inconclusive on this guest (no AMD GPU). amdgpu_set_pp_od_clk_voltage (amdgpu_pm.c:543-611) parses 's
Root cause (kernel references)
sys/dev/drm/amd/powerplay/hwmgr/smu7_hwmgr.c:4855sys/dev/drm/amd/powerplay/hwmgr/smu7_hwmgr.c:4859sys/dev/drm/amd/powerplay/hwmgr/smu7_hwmgr.c:4864sys/dev/drm/amd/powerplay/inc/hardwaremanager.h:384sys/dev/drm/amd/amdgpu/amdgpu_pm.c:585
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):odn_negative_level.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-3687/fix.diff
Confirmed kernel references
Detail
Evidence (decisive lines)
run.log: 'bounds check ... (long -1 vs u32 8): PASSED (signed compare, negative passes)', 'uint32_t input_level = 0xffffffff', 'write offset ... = 4294967295 * 12 = 51539607540 bytes past entries[0]'; VERDICT.md; fix.diff (reject input[i] < 0)
PoC changes
No seed; wrote an arithmetic harness reproducing kstrtol('-1') -> signed compare -> u32 truncation -> byte offset, using the exact C semantics shared by userland and kernel.
Verified recommended fix
Reject negative levels: 'if (i + 3 > size || input[i] < 0 || input[i] >= num_of_pl) return -EINVAL;' in smu7_odn_edit_dpm_table.
Verdict
Source-certain wild-write primitive gated behind root + non-default module config; runtime-inconclusive on this guest (no AMD GPU). amdgpu_set_pp_od_clk_voltage (amdgpu_pm.c:543-611) parses 's
No comments yet.