Heap buffer overflow populating VCE/UVD SMC levels from unclamped VBIOS MM-dependency count
Summary
polaris10_populate_smc_vce_level (:1298) and populate_smc_uvd_level (:1404): table->V/UvdLevelCount=(uint8_t)(mm_table->count), loop writes V/UvdLevel[SMU74_MAX_LEVELS_V/UVD=8]. mm_table->count from VBIOS ucNumEntries (u8 0-255, no clamp in process_pptables_v1_0.c:700-708). count>8 -> heap overflow past smc_state_table into polaris10_smumgr fields (power_tune_defaults ptr derefed later). Sibling of DF-1271/1296. Crafted VBIOS. Fix: clamp count to SMU74_MAX_LEVELS_*.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-1346 Β· 9 files| File | Type | Description | Size | |
|---|---|---|---|---|
| fix.diff | suggested-fix | git-apply-able fix; compile-validated under -Werror in the amdgpu module | 987 B | view raw |
| VERDICT.md | verdict | full source trace + reachability analysis + compile-validation | 4.0 KB | β raw |
| README.md | readme | summary, mechanism, trigger conditions, fix | 3.5 KB | β raw |
| build.sh | build | module build that validated the fix compiles | 372 B | view raw |
| run.sh | run | guest reachability probe | 678 B | view raw |
| build_fix.log | build-log | full amdgpu module build output (rc=0, -Werror) | 783.2 KB | β download |
| env.txt | environment | uname, cc, PCI/kldstat/device-node state proving no GPU/audio | 487 B | view raw |
| ../fix_build_combined.log | build-log | Combined 41-finding kernel build (rc=0, -Werror clean) | 5.6 MB | β download |
| ../fix_build_summary.txt | build-summary | Summary of the combined 41-finding kernel build | 826 B | view raw |
DF-1346 β Heap buffer overflow populating VCE/UVD SMC levels from an unclamped VBIOS MM-dependency count
File: sys/dev/drm/amd/powerplay/smumgr/polaris10_smumgr.c:1298 and 1404
Class: heap buffer overflow (write past fixed SMC level array)
Status: INCONCLUSIVE at runtime β confirmed real source bug, hardware-gated on this guest
The vulnerable code path was traced line-by-line in sys/ and confirmed to be a genuine bug
(missing bounds check / integer overflow / UAF race). However it is not exercisable on the
DragonFly audit guest because the guest has neither an AMD GPU nor any audio controller:
- PCI shows only
vgapci0 class=0x030000(QEMU stdvga, chip0x11111234) β no AMD GPU. - No PCI audio device (class
0x0401/0x0403);hw.sndempty; no/dev/dsp. amdgpu.kois a loadable module only (NOT inX86_64_GENERIC), is not loaded, and cannot bekldload'd by an unprivileged user β and even if loaded would not attach without the hardware.
This is the valid hard-blocker case "vulnerable code path unreachable at runtime on this guest AND no harness can exercise it (device-integrated parser / ioctl / hardware-dependent race)." The bug is a real latent defect that would manifest on a system with the relevant hardware + the module loaded.
Mechanism (confirmed by source trace)
polaris10_populate_smc_vce_level() sets table->VceLevelCount = (uint8_t)(mm_table->count) (:1298) then loops count < VceLevelCount writing table->VceLevel[count] (:1301-1330). VceLevel[] is a fixed array of SMU74_MAX_LEVELS_VCE = 8 (smu74.h:139 / smu74_discrete.h:287). mm_table->count comes straight from the VBIOS ucNumEntries (u8, 0-255) with no clamp (process_pptables_v1_0.c:708), so count>8 overflows VceLevel[] into the following fields of SMU74_Discrete_DpmTable (and, for the smc_state_table, into polaris10_smumgr fields such as power_tune_defaults which is later dereferenced). polaris10_populate_smc_uvd_level() has the identical flaw for UvdLevel[SMU74_MAX_LEVELS_UVD=8] at :1404-1407.
Live trigger conditions
Requires an AMD Polaris10 GPU with amdgpu attached whose VBIOS MM-dependency table has ucNumEntries > 8 (crafted VBIOS via passthrough/hotplug). Reached during powerplay init. QEMU audit guest has no AMD GPU, no /dev/dri, amdgpu.ko not loaded => unreachable here.
Fix
A standalone, git apply-able fix is in fix.diff. Compile-validated: applied to in-guest
/usr/src and the amdgpu module rebuilt under -Werror (rc=0, no warnings/errors in the
patched translation unit). See build_fix.log.
Clamp the count to the fixed array size using the same ternary already used elsewhere in this file for the pcie-link levels (~:2191): VceLevelCount = (uint8_t)((SMU74_MAX_LEVELS_VCE < mm_table->count) ? SMU74_MAX_LEVELS_VCE : mm_table->count) and the analogous line for UvdLevelCount/SMU74_MAX_LEVELS_UVD. Supersedes the finding proposal (clamp count to SMU74_MAX_LEVELS_*).
Reproduce / validate
# 1. Confirm the bug site exists (read-only source trace): grep -n ... sys/dev/drm/amd/powerplay/smumgr/polaris10_smumgr.c # 2. Validate the fix compiles (on the audit guest): scp -F dfbsd-qemu/config findings/poc/DF-1346/fix.diff dfbsd:/root/fix.diff ./dfbsd-qemu/vm.sh run_root 'cd /usr/src && patch -p1 --forward < /root/fix.diff' ./dfbsd-qemu/vm.sh run_root 'cd /usr/src/sys/dev/drm/amd/amdgpu && KERNCONF=X86_64_GENERIC SYSDIR=/usr/src/sys make -m /usr/src/share/mk' # 3. (requires real hardware) Exercise the bug: attach an AMD GPU / audio device and trigger.
VERDICT β DF-1346
Verdict: INCONCLUSIVE at runtime; source bug CONFIRMED; fix COMPILE-VALIDATED
Citations confirmed: sys/dev/drm/amd/powerplay/smumgr/polaris10_smumgr.c:1298, sys/dev/drm/amd/powerplay/smumgr/polaris10_smumgr.c:1301, sys/dev/drm/amd/powerplay/smumgr/polaris10_smumgr.c:1404, sys/dev/drm/amd/powerplay/smumgr/polaris10_smumgr.c:1407, sys/dev/drm/amd/powerplay/inc/smu74.h:138, sys/dev/drm/amd/powerplay/inc/smu74_discrete.h:286, sys/dev/drm/amd/powerplay/hwmgr/process_pptables_v1_0.c:708
Is the bug real? β YES (source trace)
polaris10_populate_smc_vce_level() sets table->VceLevelCount = (uint8_t)(mm_table->count) (:1298) then loops count < VceLevelCount writing table->VceLevel[count] (:1301-1330). VceLevel[] is a fixed array of SMU74_MAX_LEVELS_VCE = 8 (smu74.h:139 / smu74_discrete.h:287). mm_table->count comes straight from the VBIOS ucNumEntries (u8, 0-255) with no clamp (process_pptables_v1_0.c:708), so count>8 overflows VceLevel[] into the following fields of SMU74_Discrete_DpmTable (and, for the smc_state_table, into polaris10_smumgr fields such as power_tune_defaults which is later dereferenced). polaris10_populate_smc_uvd_level() has the identical flaw for UvdLevel[SMU74_MAX_LEVELS_UVD=8] at :1404-1407.
Can it be reproduced on this guest? β NO (hardware-gated)
Requires an AMD Polaris10 GPU with amdgpu attached whose VBIOS MM-dependency table has ucNumEntries > 8 (crafted VBIOS via passthrough/hotplug). Reached during powerplay init. QEMU audit guest has no AMD GPU, no /dev/dri, amdgpu.ko not loaded => unreachable here.
Guest evidence (env.txt): only vgapci0 class=0x030000 chip=0x11111234 (QEMU stdvga); no AMD GPU;
no PCI audio device; kldstat shows no drm/radeon/amdgpu/snd module; /dev/dri and /dev/dsp* do
not exist. The amdgpu module is not in X86_64_GENERIC, is not loaded, and cannot be loaded by
an unprivileged user (kldload is root-only); even loaded, it would not attach without the hardware.
Therefore the vulnerable code is unreachable at runtime here. Because the sinks are device-integrated
parsers / DRM ioctls / a hardware-dependent channel race, no userspace harness on this guest can
exercise them. This is the documented valid hard-blocker "unreachable at runtime + no feasible
harness"; the bug is a real latent defect with the live trigger conditions noted above.
No escalation chain (and why that is correct here)
There is no memory-corruption primitive to escalate on this guest: the corruption sinks live entirely
inside the not-loaded amdgpu driver behind hardware that is absent. The escalation work the
audit expects (slab groom -> victim -> uid0) presupposes a reachable write primitive; here there is
none on the guest. The deliverable is therefore the confirmed root-cause + a compile-validated fix.
Fix (fix.diff) β authored and COMPILE-VALIDATED
Clamp the count to the fixed array size using the same ternary already used elsewhere in this file for the pcie-link levels (~:2191): VceLevelCount = (uint8_t)((SMU74_MAX_LEVELS_VCE < mm_table->count) ? SMU74_MAX_LEVELS_VCE : mm_table->count) and the analogous line for UvdLevelCount/SMU74_MAX_LEVELS_UVD. Supersedes the finding proposal (clamp count to SMU74_MAX_LEVELS_*).
The fix was applied to in-guest /usr/src (all hunks applied cleanly) and the amdgpu module was
rebuilt with the kernel's -Werror flags:
cd /usr/src/sys/dev/...amdgpu... && KERNCONF=X86_64_GENERIC SYSDIR=/usr/src/sys make -m /usr/src/share/mk
=> rc=0, no warnings/errors in the patched translation unit (build_fix.log). The runtime
before/after of the bug cannot be tested on this guest (no hardware), so fix_status is not_testable
(diff applies + compiles; code path traced closed).
Why not not_reproduced (false-positive)?
This is NOT a false positive. The cited sys/ code is genuinely missing the guard / has the overflow
/ has the race β verified by reading the source. It is a real bug that is simply out of reach of this
particular (GPU/audio-less) QEMU guest.
Fix verification
not_testablecompile validated -Werror
module build rc=0
Confirmed kernel references
β
Detail
Exploit chain
none
Evidence (decisive lines)
β
Verdict
Source-confirmed. polaris10 VceLevelCount/UvdLevelCount unbounded vs [8] -> heap overflow. amdgpu not in GENERIC.
No comments yet.