radeon_atom_get_clock_info stores VBIOS reference clocks unchecked β zero reference_freq reaches mode-set and DPM divisions (#DE panic)
Summary
Parse side of the DF-3613/3622/3648 sink family (and broader): usReferenceClock/usCoreReferenceClock/usMemoryReferenceClock are stored into p1pll/spll/mpll reference_freq with no zero default, unlike pll_out_min which IS defaulted. Zero reaches DIV_ROUND_CLOSEST(..., reference_freq) in radeon_compute_pll_avivo (any mode set), the SS step-size division, and si/ci DPM PLL math. The parse-side default (10000 = 100 MHz) kills the whole family. HARNESS: verbatim parse store + sink -> SIGFPE on the guest. Blank/corrupt FirmwareInfo -> first mode set or DPM enable -> kernel panic.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-3742 Β· 13 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 2.1 KB | β raw | |
| VERDICT.md | β | 2.6 KB | β raw | |
| build.log | β | 609 B | view raw | |
| build.sh | β | 65 B | view raw | |
| env.txt | β | 375 B | view raw | |
| harness_refclock.c | β | 2.9 KB | view raw | |
| run.log | β | 150 B | view raw | |
| run.sh | β | 108 B | view raw | |
| README.md | β | 2.1 KB | β raw | |
| VERDICT.md | β | 2.6 KB | β raw | |
| manifest.json | β | 1.1 KB | view raw | |
| verdict.json | β | 3.2 KB | view raw | |
| fix.diff | β | 958 B | view raw |
DF-3742 β radeon_atom_get_clock_info stores VBIOS reference clocks unchecked β zero reference_freq reaches mode-set/DPM divisions (#DE panic)
Severity: Medium Β· CWE: CWE-369 Divide By Zero Β· File: sys/dev/drm/radeon/radeon_atombios.c:1146-1147, 1188-1193, 1215-1220
What this is
Parse-side root of the reference-clock divide-by-zero family. radeon_atom_get_clock_info() stores FirmwareInfo usReferenceClock / usCoreReferenceClock / usMemoryReferenceClock into p1pll/spll/mpll reference_freq with no zero/sanity default β while pll_out_min==0 IS defaulted (:1173-1178, :1202-1207, :1229-1234). A zero reference clock then divides by zero in: (a) default modesetting: radeon_compute_pll_avivo() radeon_display.c:1027 (den=reference_freq) -> avivo_get_fb_ref_div() :1040 DIV_ROUND_CLOSEST(...,den) β any mode set; atombios_crtc.c:1128-1132 (SS step size); (b) DPM: si_calculate_sclk_params / si_populate_mclk_value (sink filings DF-3613), ci_calculate_mclk_params (DF-3622/DF-3648). This file owns the parse-side fix (default when 0), which kills the whole sink family. Harness on guest: verbatim parse store + :1040 sink -> SIGFPE (exit 42). Kernel path unreachable on this guest (no AMD GPU).
Reproduce
./build.sh # cc -O1 -g -Wall -o <bin> harness_refclock.c (DragonFly guest, cc 8.3) ./run.sh # expected: reference_freq=0 stored (no default) then DIV_ROUND_CLOSEST by 0 -> SIGFPE, exit_code=42
The harness replicates the kernel parse arithmetic VERBATIM (cited path:line) against a synthetic crafted-VBIOS buffer with canary-guarded destinations.
Guest / environment
Guest has NO AMD/radeon GPU (virtio-only); /boot/kernel/radeon.ko exists (code shipped in the default install) but cannot attach, so the kernel code path is unreachable at runtime on this guest. Evidence is source-level certainty (path:line traced) plus a verbatim-arithmetic userland harness compiled and executed on the guest (build.log/run.log). See env.txt (uname, cc, kldstat, /boot/kernel/radeon.ko presence).
Fix
default reference clocks to 10000 (100 MHz) when the VBIOS reports 0 β see fix.diff (authored after verification; never applied to sys/).
DF-3742 β radeon_atom_get_clock_info stores VBIOS reference clocks unchecked β zero reference_freq reaches mode-set/DPM divisions (#DE panic)
Severity: Medium Β· CWE: CWE-369 Divide By Zero Β· File: sys/dev/drm/radeon/radeon_atombios.c:1146-1147, 1188-1193, 1215-1220
What this is
Parse-side root of the reference-clock divide-by-zero family. radeon_atom_get_clock_info() stores FirmwareInfo usReferenceClock / usCoreReferenceClock / usMemoryReferenceClock into p1pll/spll/mpll reference_freq with no zero/sanity default β while pll_out_min==0 IS defaulted (:1173-1178, :1202-1207, :1229-1234). A zero reference clock then divides by zero in: (a) default modesetting: radeon_compute_pll_avivo() radeon_display.c:1027 (den=reference_freq) -> avivo_get_fb_ref_div() :1040 DIV_ROUND_CLOSEST(...,den) β any mode set; atombios_crtc.c:1128-1132 (SS step size); (b) DPM: si_calculate_sclk_params / si_populate_mclk_value (sink filings DF-3613), ci_calculate_mclk_params (DF-3622/DF-3648). This file owns the parse-side fix (default when 0), which kills the whole sink family. Harness on guest: verbatim parse store + :1040 sink -> SIGFPE (exit 42). Kernel path unreachable on this guest (no AMD GPU).
Reproduce
./build.sh # cc -O1 -g -Wall -o <bin> harness_refclock.c (DragonFly guest, cc 8.3) ./run.sh # expected: reference_freq=0 stored (no default) then DIV_ROUND_CLOSEST by 0 -> SIGFPE, exit_code=42
The harness replicates the kernel parse arithmetic VERBATIM (cited path:line) against a synthetic crafted-VBIOS buffer with canary-guarded destinations.
Guest / environment
Guest has NO AMD/radeon GPU (virtio-only); /boot/kernel/radeon.ko exists (code shipped in the default install) but cannot attach, so the kernel code path is unreachable at runtime on this guest. Evidence is source-level certainty (path:line traced) plus a verbatim-arithmetic userland harness compiled and executed on the guest (build.log/run.log). See env.txt (uname, cc, kldstat, /boot/kernel/radeon.ko presence).
Fix
default reference clocks to 10000 (100 MHz) when the VBIOS reports 0 β see fix.diff (authored after verification; never applied to sys/).
VERDICT β DF-3742
Status: inconclusive (source-certain; harness-reproduced arithmetic; kernel path unreachable on this guest) reproduced: 0 (in-kernel) β harness demonstration: YES Impact on real hardware: Medium β CWE-369 Divide By Zero
Finding
Parse-side root of the reference-clock divide-by-zero family. radeon_atom_get_clock_info() stores FirmwareInfo usReferenceClock / usCoreReferenceClock / usMemoryReferenceClock into p1pll/spll/mpll reference_freq with no zero/sanity default β while pll_out_min==0 IS defaulted (:1173-1178, :1202-1207, :1229-1234). A zero reference clock then divides by zero in: (a) default modesetting: radeon_compute_pll_avivo() radeon_display.c:1027 (den=reference_freq) -> avivo_get_fb_ref_div() :1040 DIV_ROUND_CLOSEST(...,den) β any mode set; atombios_crtc.c:1128-1132 (SS step size); (b) DPM: si_calculate_sclk_params / si_populate_mclk_value (sink filings DF-3613), ci_calculate_mclk_params (DF-3622/DF-3648). This file owns the parse-side fix (default when 0), which kills the whole sink family. Harness on guest: verbatim parse store + :1040 sink -> SIGFPE (exit 42). Kernel path unreachable on this guest (no AMD GPU).
Why not reproduced in-kernel
The QEMU guest is virtio-only (no AMD GPU): pciconf -l shows no 1002 device;
kldstat shows radeon not loaded. /boot/kernel/radeon.ko exists (the vulnerable code ships in
the default install) but cannot attach, so radeon_atombios.c parse paths never execute here.
All conclusions are source-level (path:line above) plus the on-guest harness (harness_refclock.c).
Harness result
Command: ./run.sh (after ./build.sh) Expected/observed: reference_freq=0 stored (no default) then DIV_ROUND_CLOSEST by 0 -> SIGFPE, exit_code=42 Full logs: build.log, run.log.
Impact / exploit chain
crafted/blank FirmwareInfo with usReferenceClock=0 -> radeon_atom_get_clock_info stores 0 -> first mode set (or DPM enable) divides by zero -> #DE kernel panic
Fix
default reference clocks to 10000 (100 MHz) when the VBIOS reports 0 β fix.diff.
Honest notes
- DF-3742 does NOT re-report pass-1 findings DF-1198/1199/1200 (power-table state_index mismatch, asic_ss_info underflow, power-table indices) β different functions/tables/fields.
- fix validation not possible on this guest (no hardware); fix.diff is source-authored only.
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.
VERDICT β DF-3742
Status: inconclusive (source-certain; harness-reproduced arithmetic; kernel path unreachable on this guest) reproduced: 0 (in-kernel) β harness demonstration: YES Impact on real hardware: Medium β CWE-369 Divide By Zero
Finding
Parse-side root of the reference-clock divide-by-zero family. radeon_atom_get_clock_info() stores FirmwareInfo usReferenceClock / usCoreReferenceClock / usMemoryReferenceClock into p1pll/spll/mpll reference_freq with no zero/sanity default β while pll_out_min==0 IS defaulted (:1173-1178, :1202-1207, :1229-1234). A zero reference clock then divides by zero in: (a) default modesetting: radeon_compute_pll_avivo() radeon_display.c:1027 (den=reference_freq) -> avivo_get_fb_ref_div() :1040 DIV_ROUND_CLOSEST(...,den) β any mode set; atombios_crtc.c:1128-1132 (SS step size); (b) DPM: si_calculate_sclk_params / si_populate_mclk_value (sink filings DF-3613), ci_calculate_mclk_params (DF-3622/DF-3648). This file owns the parse-side fix (default when 0), which kills the whole sink family. Harness on guest: verbatim parse store + :1040 sink -> SIGFPE (exit 42). Kernel path unreachable on this guest (no AMD GPU).
Why not reproduced in-kernel
The QEMU guest is virtio-only (no AMD GPU): pciconf -l shows no 1002 device;
kldstat shows radeon not loaded. /boot/kernel/radeon.ko exists (the vulnerable code ships in
the default install) but cannot attach, so radeon_atombios.c parse paths never execute here.
All conclusions are source-level (path:line above) plus the on-guest harness (harness_refclock.c).
Harness result
Command: ./run.sh (after ./build.sh) Expected/observed: reference_freq=0 stored (no default) then DIV_ROUND_CLOSEST by 0 -> SIGFPE, exit_code=42 Full logs: build.log, run.log.
Impact / exploit chain
crafted/blank FirmwareInfo with usReferenceClock=0 -> radeon_atom_get_clock_info stores 0 -> first mode set (or DPM enable) divides by zero -> #DE kernel panic
Fix
default reference clocks to 10000 (100 MHz) when the VBIOS reports 0 β fix.diff.
Honest notes
- DF-3742 does NOT re-report pass-1 findings DF-1198/1199/1200 (power-table state_index mismatch, asic_ss_info underflow, power-table indices) β different functions/tables/fields.
- fix validation not possible on this guest (no hardware); fix.diff is source-authored only.
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 after source-level verification; cannot be validated on this guest (no AMD GPU; radeon.ko cannot attach). Compiling the module or booting a patched kernel would not exercise the path without hardware.
fix.diff (unified, against read-only sys/ tree; never applied)
Confirmed kernel references
- sys/dev/drm/radeon/radeon_atombios.c:1146-1147
- sys/dev/drm/radeon/radeon_atombios.c:1173-1178
- sys/dev/drm/radeon/radeon_atombios.c:1188-1193
- sys/dev/drm/radeon/radeon_atombios.c:1215-1220
- sys/dev/drm/radeon/radeon_display.c:1027
- sys/dev/drm/radeon/radeon_display.c:1040
- sys/dev/drm/radeon/atombios_crtc.c:1128-1132
Detail
Exploit chain
crafted/blank FirmwareInfo with usReferenceClock=0 -> radeon_atom_get_clock_info stores 0 -> first mode set (or DPM enable) divides by zero -> #DE kernel panic
Evidence (decisive lines)
harness_refclock.c + build.log + run.log; VERDICT.md; kernel refs sys/dev/drm/radeon/radeon_atombios.c:1146-1147
PoC changes
harness written from scratch (no seed PoC): verbatim kernel arithmetic replica with canary-guarded destination model, built and executed on the DragonFly guest (cc 8.3); fixed nested-comment, stddef/stdint includes and volatile divisors during bring-up
Verified recommended fix
default reference clocks to 10000 (100 MHz) when the VBIOS reports 0
Verdict
Parse-side root of the reference-clock divide-by-zero family. radeon_atom_get_clock_info() stores FirmwareInfo usReferenceClock / usCoreReferenceClock / usMemoryReferenceClock into p1pll/spll/mpll reference_freq with no zero/sanity default β while pll_out_min==0 IS defaulted (:1173-1178, :1202-1207, :1229-1234). A zero reference clock then divides by zero in: (a) default modesetting: radeon_compute_pll_avivo() radeon_display.c:1027 (den=reference_freq) -> avivo_get_fb_ref_div() :1040 DIV_ROUND_CLOSEST(...,den) β any mode set; atombios_crtc.c:1128-1132 (SS step size); (b) DPM: si_calculate_sclk_params / si_populate_mclk_value (sink filings DF-3613), ci_calculate_mclk_params (DF-3622/DF-3648). This file owns the parse-side fix (default when 0), which kills the whole sink family. Harness on guest: verbatim parse store + :1040 sink -> SIGFPE (exit 42). Kernel path unreachable on this guest (no AMD GPU).
No comments yet.