DF-3853 / harness.c
/* * DF-3853 harness โ BIOS sysclk reference_freq == 0 -> kernel divide by zero. * * Kernel source replicated 1:1 (arithmetic copied verbatim): * sys/dev/drm/radeon/radeon_combios.c:758 * spll->reference_freq = RBIOS16(pll_info + 0x1a); [NO zero check] * (p1pll :741 and mpll :773 equally unchecked; the sibling clamps in * radeon_clocks.c:194-216 only sanitize reference_div, never *_freq) * sys/dev/drm/radeon/radeon_clocks.c:344-385 calc_eng_mem_clock(): * req_clock /= (2 * spll->reference_freq); <- divide by zero * sys/dev/drm/radeon/radeon_pm.c:224 radeon_set_engine_clock(rdev, sclk) * -> radeon_legacy_set_engine_clock (radeon_asic.c:253,... pre-AVIVO) * -> calc_eng_mem_clock * * On hardware this is a 32-bit integer division by zero -> #DE -> kernel * panic. In userspace the identical arithmetic raises SIGFPE (core dumped). */ #include <stdio.h> #include <stdint.h> #include <string.h> #include <stdlib.h> static uint8_t bios[512]; /* crafted COMBIOS image */ #define RBIOS8(i) (bios[i]) #define RBIOS16(i) (RBIOS8(i) | (RBIOS8((i) + 1) << 8)) struct radeon_pll { uint32_t reference_freq; /* 10 kHz units */ uint32_t reference_div; }; static struct radeon_pll spll; /* r100-era hardware register read stub (RADEON_M_SPLL_REF_FB_DIV): a real * chip returns its configured ref div here; pick a plausible non-zero so * the ONLY zero denominator is the BIOS-controlled reference_freq. */ static uint32_t RREG32_PLL(uint32_t reg) { (void)reg; return 0x0021; } /* ---- radeon_combios.c:725-784 radeon_combios_get_clock_info() ------- */ static void combios_get_clock_info(uint16_t pll_info) { spll.reference_freq = RBIOS16(pll_info + 0x1a); /* :758 โ unchecked */ spll.reference_div = RBIOS16(pll_info + 0x1c); /* :759 */ printf("[combios] spll->reference_freq=%u spll->reference_div=%u " "(no validation in parser or in radeon_get_clock_info clamp block)\n", spll.reference_freq, spll.reference_div); } /* ---- radeon_clocks.c:344-385 calc_eng_mem_clock() โ verbatim -------- */ static uint32_t calc_eng_mem_clock(uint32_t req_clock, int *fb_div, int *post_div) { int ref_div = spll.reference_div; if (!ref_div) ref_div = RREG32_PLL(0) & 0x3ff; /* reg fallback */ if (req_clock < 15000) { *post_div = 8; req_clock *= 8; } else if (req_clock < 30000) { *post_div = 4; req_clock *= 4; } else if (req_clock < 60000) { *post_div = 2; req_clock *= 2; } else *post_div = 1; req_clock *= ref_div; req_clock += spll.reference_freq; printf("[calc] about to execute: req_clock /= (2 * %u) <- denominator is ZERO\n", spll.reference_freq); req_clock /= (2 * spll.reference_freq); /* <-- SIGFPE / #DE */ *fb_div = req_clock & 0xff; req_clock = (req_clock & 0xffff) << 1; req_clock *= spll.reference_freq; req_clock /= ref_div; req_clock /= *post_div; return req_clock; } int main(void) { uint16_t pll_info = 0x30; int fb, post; /* crafted PLL info table: sysclk reference frequency word = 0 */ memset(bios, 0, sizeof(bios)); bios[pll_info] = 11; /* rev > 10 flags consumed fine */ /* bios[pll_info + 0x1a .. 0x1b] stay 0 -> reference_freq == 0 */ bios[pll_info + 0x1c] = 0x0c; /* reference_div = 12 (valid) */ printf("== DF-3853: crafted COMBIOS PLL table, sysclk reference_freq = 0 ==\n"); combios_get_clock_info(pll_info); fflush(stdout); /* PM transition -> radeon_set_engine_clock -> calc_eng_mem_clock: * same division the kernel executes on a mode/PM clock change. */ printf("[pm] radeon_legacy_set_engine_clock -> calc_eng_mem_clock:\n"); uint32_t out = calc_eng_mem_clock(25000, &fb, &post); printf("unreachable: 0x%x\n", out); return 0; } |