DF-3743 / harness_ssrate.c
/* * DF-3743 harness: spread-spectrum rate parsed to zero -> kernel divide-by-zero (#DE) * * Parse side (the bug), sys/dev/drm/radeon/radeon_atombios.c, radeon_atombios_get_asic_ss_info(): * v1 :1546 ss->rate = le16_to_cpu(ss_assign->v1.usSpreadRateInKhz); // 0 unchecked * v2 :1564-1569 ss->rate = usSpreadRateIn10Hz; if ((crev == 2) && (ENGINE||MEMORY)) * ss->rate /= 100; // 1..99 -> 0 (integer truncation) * v3 :1586-1594 same /= 100 for ENGINE/MEMORY SS * and radeon_atombios_get_ppll_ss_info() :1390-1413 NEVER assigns ss->rate at all * (memset zero at :1390 stays) yet returns true. * All three paths can return success with ss->rate == 0. * * Sinks (integer division by ss->rate): * si_dpm.c:4833 u32 clk_s = reference_clock * 5 / (reference_divider * ss.rate); * ci_dpm.c:3229 identical expression * (sink-side filings DF-3621/DF-3647; the parse-side zero-rate is proven here) */ #include <stdio.h> #include <stdlib.h> #include <stdint.h> #include <signal.h> typedef uint8_t u8; typedef uint16_t u16; typedef uint32_t u32; struct radeon_atom_ss_model { u16 percentage; u8 type; u32 rate; u16 percentage_divider; }; static volatile int stage; static void fpe_handler(int sig) { (void)sig; printf("SIGFPE (#DE) at stage %d โ kernel equivalent: divide-by-zero panic\n", stage); _exit(42); } int main(void) { setbuf(stdout, NULL); signal(SIGFPE, fpe_handler); /* ---- v3 parse path, verbatim arithmetic radeon_atombios.c:1580-1597 ---- */ u16 usSpreadRateIn10Hz = 50; /* plausible BIOS value: 0.5 kHz in 10 Hz units */ int id = 1; /* ASIC_INTERNAL_ENGINE_SS */ struct radeon_atom_ss_model ss; memset(&ss, 0, sizeof(ss)); ss.rate = usSpreadRateIn10Hz; if ((id == 1) || (id == 0)) /* ENGINE or MEMORY */ ss.rate /= 100; /* :1594 โ 50/100 == 0 */ printf("[v3] usSpreadRateIn10Hz=%u -> ss.rate=%u (parse returns success)\n", usSpreadRateIn10Hz, ss.rate); if (ss.rate != 0) { printf("unexpected\n"); return 1; } /* ---- sink, verbatim si_dpm.c:4833 / ci_dpm.c:3229 ---- */ u32 reference_clock = 27000, reference_divider = 2; volatile u32 vrate = ss.rate; /* volatile: kernel really divides */ printf("dividing by ss.rate=%u now (si_dpm.c:4833 / ci_dpm.c:3229)...\n", vrate); volatile u32 clk_s; stage = 1; clk_s = reference_clock * 5 / (reference_divider * vrate); /* #DE */ (void)clk_s; /* ---- ppll path: rate never assigned (memset 0), atombios_crtc.c consumer ---- */ stage = 2; struct radeon_atom_ss_model ppll_ss; memset(&ppll_ss, 0, sizeof(ppll_ss)); /* radeon_atombios.c:1390 */ ppll_ss.percentage = 22; /* radeon_atombios_get_ppll_ss_info (:1400-1413) assigns percentage/type/step/delay/ * range/refdiv only โ rate stays 0 and the function returns true */ volatile u32 vrate2 = ppll_ss.rate; /* 0, never assigned by the parser */ volatile u32 clk_s2; clk_s2 = reference_clock * 5 / (reference_divider * vrate2); /* #DE */ (void)clk_s2; printf("FAIL: no SIGFPE\n"); return 1; } |