Kernel stack overflow: smu7_get_sclks/smu7_get_mclks copy up to 255 VBIOS entries into amd_pp_clocks.clock[16]/latency[16] held in the caller's stack frame
Summary
Both clock getters write clocks->clock[i]/latency[i] for i < dep_table->count with no bound against MAX_NUM_CLOCKS=16; the count is the unvalidated VBIOS u8. The only caller chain places the struct on the stack (dm_pp_get_clock_levels_by_type), reached at display mode validation: 239 u32 writes (956 bytes) past each array, 892 bytes past the struct - smashing caller locals and the return path with attacker-controlled u32 clock values. V0 branch additionally NULL-derefs a missing dependency table. HARNESS-VERIFIED on the guest: phase 1 accounts every OOB write against a padded frame incl. a return-address model slot; phase 2 performs the smash on a genuine stack local and dies SIGSEGV. Crafted VBIOS with a >16-entry dependency table on a DC-enabled SMU7 part: kernel stack buffer overflow (control-flow corruption) triggered by routine display mode validation at boot or hotplug.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-3684 Β· 12 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 2.5 KB | β raw | |
| VERDICT.md | β | 3.6 KB | β raw | |
| pp_clocks_stack.c | β | 5.1 KB | view raw | |
| build.sh | β | 109 B | view raw | |
| run.sh | β | 154 B | view raw | |
| build.log | β | 70 B | view raw | |
| run.log | β | 797 B | view raw | |
| run.2.log | β | 797 B | view raw | |
| env.txt | β | 1.1 KB | view raw | |
| fix.diff | β | 1.8 KB | view raw | |
| verdict.json | β | 3.5 KB | view raw | |
| manifest.json | β | 1.1 KB | view raw |
DF-3684 β Kernel stack overflow: smu7_get_sclks/smu7_get_mclks copy up to 255 VBIOS entries into amd_pp_clocks.clock[16]/latency[16] stack frame
File: sys/dev/drm/amd/powerplay/hwmgr/smu7_hwmgr.c (pass 2, GLM 5.3 re-audit)
Severity: High | CVSS: CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H | CWE: CWE-787 Out-of-bounds Write (stack) | Confidence: certain (source)
What this is
Source-certain kernel STACK buffer overflow; runtime-inconclusive on this guest (no AMD GPU). smu7_get_sclks (smu7_hwmgr.c:4625-4632) and smu7_get_mclks (:4662-4672) write clocks->clock[i]/latency[i] for i < dep_table->count with no bound against MAX_NUM_CLOCKS=16 (dm_pp_interface.h:161-167). The count is the raw VBIOS ucNumEntries (u8 <= 255, unvalidated by both processpptables.c:391 and process_pptables_v1_0.c:436/468/389). The sole caller chain places the struct on the stack: dm_pp_get_clock_levels_by_type (amdgpu_dm_pp_smu.c:296-316) declares 'struct amd_pp_clocks pp_clks = { 0 };' and passes it through amd_pp_dpm_get_clock_by_type -> phm_get_clock_by_type -> smu7_get_clock_by_type. With count=255 the loops write 239 u32s (956 bytes) past each 16-element array - 892 bytes beyond the 132-byte struct - into the caller's stack frame (validation_clks, saved registers, return path) with attacker-controlled u32 values (VBIOS clk * 10). Reached on display mode validation (DC init / mode set / hotplug). The V0 branch additionally NULL-derefs hwmgr->dyn_state.vddc_dependency_on_mclk when the VBIOS omits the table (offset 0 -> NULL is parse-legal). Harness confirmed full accounting plus a live stack smash killing the process (SIGSEGV), i.e. in-kernel: kernel-mode page fault or silent control-flow corruption.
Reproduce (harness on the audit QEMU guest)
scp -F dfbsd-qemu/config pp_clocks_stack.c dfbsd-maxx:/tmp/DF-3684/ ssh -F dfbsd-qemu/config dfbsd-maxx 'cd /tmp/DF-3684 && cc -Wall -O0 -o t pp_clocks_stack.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-3684 β verdict narrative
Finding
Source-certain kernel STACK buffer overflow; runtime-inconclusive on this guest (no AMD GPU). smu7_get_sclks (smu7_hwmgr.c:4625-4632) and smu7_get_mclks (:4662-4672) write clocks->clock[i]/latency[i] for i < dep_table->count with no bound against MAX_NUM_CLOCKS=16 (dm_pp_interface.h:161-167). The count is the raw VBIOS ucNumEntries (u8 <= 255, unvalidated by both processpptables.c:391 and process_pptables_v1_0.c:436/468/389). The sole caller chain places the struct on the stack: dm_pp_get_clock_levels_by_type (amdgpu_dm_pp_smu.c:296-316) declares 'struct amd_pp_clocks pp_clks = { 0 };' and passes it through amd_pp_dpm_get_clock_by_type -> phm_get_clock_by_type -> smu7_get_clock_by_type. With count=255 the loops write 239 u32s (956 bytes) past each 16-element array - 892 bytes beyond the 132-byte struct - into the caller's stack frame (validation_clks, saved registers, return path) with attacker-controlled u32 values (VBIOS clk * 10). Reached on display mode validation (DC init / mode set / hotplug). The V0 branch additionally NULL-derefs hwmgr->dyn_state.vddc_dependency_on_mclk when the VBIOS omits the table (offset 0 -> NULL is parse-legal). Harness confirmed full accounting plus a live stack smash killing the process (SIGSEGV), i.e. in-kernel: kernel-mode page fault or silent control-flow corruption.
Root cause (kernel references)
sys/dev/drm/amd/powerplay/hwmgr/smu7_hwmgr.c:4625sys/dev/drm/amd/powerplay/hwmgr/smu7_hwmgr.c:4632sys/dev/drm/amd/powerplay/hwmgr/smu7_hwmgr.c:4662sys/dev/drm/amd/powerplay/hwmgr/smu7_hwmgr.c:4667sys/dev/drm/amd/include/dm_pp_interface.h:161sys/dev/drm/amd/display/amdgpu_dm/amdgpu_dm_pp_smu.c:296sys/dev/drm/amd/display/amdgpu_dm/amdgpu_dm_pp_smu.c:303
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):pp_clocks_stack.creproduces the exact kernel code shape and/or arithmetic with 1:1 struct layouts. Output inrun.log;run.2.logfor the live-crash phase. 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-3684/fix.diff
Confirmed kernel references
- sys/dev/drm/amd/powerplay/hwmgr/smu7_hwmgr.c:4625
- sys/dev/drm/amd/powerplay/hwmgr/smu7_hwmgr.c:4632
- sys/dev/drm/amd/powerplay/hwmgr/smu7_hwmgr.c:4662
- sys/dev/drm/amd/powerplay/hwmgr/smu7_hwmgr.c:4667
- sys/dev/drm/amd/include/dm_pp_interface.h:161
- sys/dev/drm/amd/display/amdgpu_dm/amdgpu_dm_pp_smu.c:296
- sys/dev/drm/amd/display/amdgpu_dm/amdgpu_dm_pp_smu.c:303
Detail
Evidence (decisive lines)
run.log: 'OOB u32 writes past clock[16] and latency[16]: 239 each (956 bytes each)', 'bytes written past end of amd_pp_clocks struct: 892', 'return-address model slot: 0x1dc130001d5f88 (expected 0xdeadbeef00000000)'; run.2.log: phase-2 live stack-local smash ends in SIGSEGV exit=139; VERDICT.md; fix.diff
PoC changes
No seed; wrote two-phase harness: (1) padded-frame accounting of every OOB write, (2) real stack-local smash demonstrating SIGSEGV.
Verified recommended fix
Bound both copy loops in smu7_get_sclks/smu7_get_mclks to MAX_NUM_CLOCKS, report the truncated count, and NULL-check the V0 dyn_state tables.
Verdict
Source-certain kernel STACK buffer overflow; runtime-inconclusive on this guest (no AMD GPU). smu7_get_sclks (smu7_hwmgr.c:4625-4632) and smu7_get_mclks (:4662-4672) write clocks->clock[i]/latency[i] for i < dep_table->count with no bound against MAX_NUM_CLOCKS=16 (dm_pp_interface.h:161-167). The count is the raw VBIOS ucNumEntries (u8 <= 255, unvalidated by both processpptables.c:391 and process_pptables_v1_0.c:436/468/389). The sole caller chain places the struct on the stack: dm_pp_get_clock_levels_by_type (amdgpu_dm_pp_smu.c:296-316) declares 'struct amd_pp_clocks pp_clks = { 0 };' and passes it through amd_pp_dpm_get_clock_by_type -> phm_get_clock_by_type -> smu7_get_clock_by_type. With count=255 the loops write 239 u32s (956 bytes) past each 16-element array - 892 bytes beyond the 132-byte struct - into the caller's stack frame (validation_clks, saved registers, return path) with attacker-controlled u32 values (VBIOS clk * 10). Reached on display mode validation (DC init / mode set / hotplug). The V0 branch additionally NULL-derefs hwmgr->dyn_state.vddc_dependency_on_mclk when the VBIOS omits the table (offset 0 -> NULL is parse-legal). Harness confirmed full accounting plus a live stack smash killing the process (SIGSEGV), i.e. in-kernel: kernel-mode page fault or silent control-flow corruption.
No comments yet.