⬢ DragonFlyBSD Kernel Audit
DF-4120 / fix.diff
← back to finding ↓ download raw
Fix for DF-4120: second divide-by-zero window in cypress_populate_mclk_value
(clk_s == 0 at cypress_dpm.c:566-567; sibling of DF-1387).

DF-1387 (known, lines 553-565) covers ss.rate == 0 making
    clk_s = reference_clock * 5 / (decoded_ref * ss.rate)      (:565)
divide by zero.  A SECOND, different window exists one expression
later even when ss.rate != 0:

    clk_s = reference_clock * 5 / (decoded_ref * ss.rate);     (:565)
    clk_v = ss.percentage * (0x4000 * dividers.whole_fb_div +
                             0x800 * dividers.frac_fb_div) /
            (clk_s * 625);                                     (:566-567)

Integer division truncates: whenever decoded_ref * ss.rate >
reference_clock * 5, clk_s becomes 0 and the division at :567 is a
divide-by-zero (#DE -> kernel panic).  All three inputs are
VBIOS-controlled: ss.rate is a raw u16 (usSpreadRateInKhz,
radeon_atombios.c:1546, max 65535), decoded_ref comes from
rv740_get_decoded_reference_divider() over the computed ref divider
(values 1..~15), and reference_clock = rdev->clock.mpll.reference_freq
(commonly 10000 in 10 kHz units; 2700 on 27 MHz-reference boards).
E.g. reference_freq=2700, decoded_ref=2, ss.rate=6751+ kHz, or
reference_freq=10000, decoded_ref=2, ss.rate=25001+ kHz => panic at
DPM enable / every power-state change on the affected mclk level.
Physically implausible spread rates for real SS silicon, trivially
craftable in a VBIOS image — same acceptance basis as DF-1387
("no zero check upstream").

Fix: bail out when clk_s truncates to zero (keeps the DF-1387 guard
for the zero divisor at :565 as a separate, already-known fix).

--- a/sys/dev/drm/radeon/cypress_dpm.c
+++ b/sys/dev/drm/radeon/cypress_dpm.c
@@ -562,6 +562,10 @@ static int cypress_populate_mclk_value(struct radeon_device *rdev,
 			if (!decoded_ref)
 				return -EINVAL;
 			clk_s = reference_clock * 5 / (decoded_ref * ss.rate);
+			if (clk_s == 0) {
+				/* DF-4120: divisor would be zero at clk_v */
+				return -EINVAL;
+			}
 			clk_v = ss.percentage *
 				(0x4000 * dividers.whole_fb_div + 0x800 * dividers.frac_fb_div) / (clk_s * 625);