DF-3614 / verdict.json
{ "finding_id": "DF-3614", "status": "reproduced", "reproduced": 1, "impact": "dos", "confidence": "certain", "verdict": "radeon twin of the lowSMIO half of amdgpu DF-3585 (the entries[] half is pass-1 DF-1136; both stem from the untrimmed SVI2 dependency count, but the lowSMIO sink at :4046-4047 survives an entries-only clamp and is a distinct write primitive). Mixed-VRM VBIOS: VDDC via GPIO_LUT (voltage_control true) + VDDCI via SVID2 (vddci_control false, vddci_control_svi2 true). si_construct_voltage_tables:3998-4004 fills vddci_voltage_table.count = vddci_dependency_on_mclk.count (u8, up to 255) with NO trim (the GPIO paths trim at :3973/:3993/:4020). si_populate_smc_voltage_tables then takes the non-SVI2-VDDC else branch (:4058 false since VDDC is GPIO) and at :4079-4080 calls si_populate_smc_voltage_table, whose loop at :4046 OR-writes table->lowSMIO[i] for i<count into the packed STATETABLE embedded in the kzallocd si_power_info (si_dpm.h:196). count 33..255 writes up to 1020 bytes past lowSMIO[0] (struct is 3012 B; overflow is contained within the si_power_info allocation) corrupting voltageMaskTable, phaseMaskTable, dpm2Params, initialState/ACPIState/ULVState/driverState and dpmLevels \u2014 data that si_init_smc_table uploads verbatim to the GPU SMC at :4779-4781, programming wrong voltage masks / TDP limits / state clocks (hardware misconfiguration, undervoltage/overvoltage risk). Harness with verbatim packed struct replicas from sislands_smc.h confirms the exact corrupted fields; gcc itself flags the replica loop (-Warray-bounds: iteration 32 invokes undefined behavior).", "exploit_chain": "crafted VBIOS: VDDC GPIO_LUT + VDDCI SVID2 + vddci_dependency_on_mclk.ucNumEntries>32 -> si_get_svi2_voltage_table sets count untrimmed -> si_populate_smc_voltage_tables else-branch -> si_populate_smc_voltage_table loop -> lowSMIO[32..254] |= value -> SMC statetable fields corrupted -> uploaded to GPU power-management microcontroller (wrong voltage masks/TDP/clocks)", "evidence": "run.log [A]: lowSMIO[32]=0xa5a55a5a (OOB start); voltageMaskTable.lowMask[0] (VDDC mask) = 0xa5a55a5a CORRUPTED; dpm2Params.TDPLimit = 0xa5a55a5a CORRUPTED; initialState.levels[0].sclk.sclk_value = 0xa5a55a5a CORRUPTED; last touched offset 1087 inside 3012-byte STATETABLE; [B] fixed: lowSMIO[32]=0x00000000 (untouched); build warnings: iteration 32 invokes undefined behavior [-Warray-bounds]", "kernel_refs": [ "sys/dev/drm/radeon/si_dpm.c:4046", "sys/dev/drm/radeon/si_dpm.c:3998", "sys/dev/drm/radeon/si_dpm.c:4079", "sys/dev/drm/radeon/si_dpm.c:4058", "sys/dev/drm/radeon/sislands_smc.h:215", "sys/dev/drm/radeon/si_dpm.h:196" ], "poc_changes": "n/a (pass-2 finding authored with its harness; no seeded PoC to fix)", "attempts": 2, "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT #0: Thu Jul 2 06:02:54 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64", "runtime_sec": 90, "guest_dirty": 0, "build_cmd": "cd findings/poc/DF-3614 && sh build.sh", "run_cmd": "cd findings/poc/DF-3614 && sh run.sh", "code_hash": "29bea2edcd4874b6fdfadea6031e40cc0661578c1f341f7d36a6f9e7be601c36", "notes": "Harness-level reproduction (exact kernel expressions executed with crafted-VBIOS inputs; SIGSEGV/SIGFPE/corruption observed). No AMD SI GPU on the virtio-only QEMU guest -> no live radeon DPM path possible; radeon.ko present at /boot/kernel. Guest /usr/src restored to pristine (sha 774b9b851b3b4ccb...) after fix validation.", "recommended_fix": "see fix.diff", "fix_status": "fixed", "fix_baseline_reproduced": 1, "fix_patched_reproduced": 0, "fix_verdict": "git apply --check OK (standalone and stacked with the other four si_dpm.c fixes); targeted module TU compile `make KERNBUILDDIR=/usr/obj/usr/src/sys/X86_64_GENERIC si_dpm.o` => rc=0, -Werror clean (87080 B). Harness fixed-path shows the primitive gone (fix.diff bounds the sink loop: `for (i = 0; i < voltage_table->count && i < SISLANDS_MAX_NO_VREG_STEPS; i++)`. git apply --check OK; stacked module TU compile -Werror clean; harness [B] shows the statetable tail intact.)", "fix_evidence": "fix.diff bounds the sink loop: `for (i = 0; i < voltage_table->count && i < SISLANDS_MAX_NO_VREG_STEPS; i++)`. git apply --check OK; stacked module TU compile -Werror clean; harness [B] shows the statetable tail intact." } |