DF-3612 / harness.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 | /* * DF-3612 โ integer divide-by-zero from VBIOS spread-spectrum rate * (radeon si_dpm.c:4831 / 4931). Same family as DF-1137/DF-1129 but a * distinct sink. * * radeon_atombios_get_asic_ss_info() (radeon_atombios.c:1543-1548) copies * `ss->rate = le16_to_cpu(ss_assign->v1.usSpreadRateInKhz)` straight from * the VBIOS ASIC_InternalSS_Info table and returns true โ with NO zero * check. si_dpm.c then divides by it whenever the SS entry matches: * * engine SS (si_calculate_sclk_params): * si_dpm.c:4829 if (radeon_atombios_get_asic_ss_info(rdev, &ss, * ASIC_INTERNAL_ENGINE_SS, vco_freq)) { * si_dpm.c:4831 u32 clk_s = reference_clock * 5 / (reference_divider * ss.rate); // #DE * si_dpm.c:4832 u32 clk_v = 4 * ss.percentage * fbdiv / (clk_s * 10000); // #DE if clk_s==0 * * memory SS (si_populate_mclk_value): * si_dpm.c:4931 u32 clks = reference_clock * 5 / ss.rate; // #DE * * Reached on every power-state conversion (si_convert_power_level_to_smc -> * si_populate_sclk_value / si_populate_mclk_value) when pi->sclk_ss / * pi->mclk_ss is set (VBIOS SS table present, rv770_get_engine_memory_ss). * On x86-64 the division compiles to `div` -> #DE -> kernel panic. * * Harness: [A] executes the exact expressions with ss.rate == 0 in forked * children (SIGFPE == kernel #DE panic). [B] runs the guarded variant from * fix.diff. */ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/wait.h> #include <stdint.h> struct radeon_atom_ss { uint32_t percentage; uint32_t rate; uint32_t type; }; static volatile uint32_t sink; /* radeon_atombios.c:1543-1548 โ no zero check on rate */ static int radeon_atombios_get_asic_ss_info(struct radeon_atom_ss *ss, uint16_t vbios_rate) { ss->percentage = 2; /* usSpreadSpectrumPercentage */ ss->rate = vbios_rate; return 1; /* entry matched, "success" */ } /* si_dpm.c:4825-4840 verbatim structure */ static void run_engine_ss(uint32_t reference_clock, uint32_t reference_divider, uint16_t vbios_rate) { struct radeon_atom_ss ss; uint32_t fbdiv = 0x1234; if (radeon_atombios_get_asic_ss_info(&ss, vbios_rate)) { uint32_t clk_s = reference_clock * 5 / (reference_divider * ss.rate); /* :4831 */ uint32_t clk_v = 4 * ss.percentage * fbdiv / (clk_s * 10000); /* :4832 */ sink = clk_s + clk_v; } printf(" UNREACHABLE: division by zero survived\n"); } /* si_dpm.c:4916-4940 verbatim structure */ static void run_memory_ss(uint32_t reference_clock, uint16_t vbios_rate) { struct radeon_atom_ss ss; uint32_t memory_clock = 150000, freq_nom = memory_clock * 2; uint64_t tmp64 = freq_nom / reference_clock; /* DF-3613 sink; nonzero here */ uint64_t tmp = tmp64 * tmp64; if (radeon_atombios_get_asic_ss_info(&ss, vbios_rate)) { uint32_t clks = reference_clock * 5 / ss.rate; /* :4931 */ uint32_t clkv = (uint32_t)((((131 * ss.percentage * ss.rate) / 100) * tmp) / freq_nom); sink = clks + clkv; } printf(" UNREACHABLE: division by zero survived\n"); } /* fix.diff variant */ static int run_engine_ss_fixed(uint32_t reference_clock, uint32_t reference_divider, uint16_t vbios_rate) { struct radeon_atom_ss ss; uint32_t fbdiv = 0x1234; if (radeon_atombios_get_asic_ss_info(&ss, vbios_rate)) { if (ss.rate == 0) /* fix.diff guard */ return -1; uint32_t clk_s = reference_clock * 5 / (reference_divider * ss.rate); uint32_t clk_v = (4 * ss.percentage * fbdiv) / (clk_s * 10000); sink = clk_s + clk_v; } return 0; } static void report(int st) { if (WIFSIGNALED(st)) printf(" child died from SIGFPE (signal %d): integer divide by zero\n" " >>> In kernel: #DE fault -> panic during DPM power-state upload <<<\n", WTERMSIG(st)); else printf(" child exited %d (NOT expected)\n", WEXITSTATUS(st)); } int main(void) { uint32_t reference_clock = 27000, reference_divider = 2; pid_t p; int st = 0; printf("== DF-3612 harness: crafted VBIOS ASIC_InternalSS_Info with usSpreadRateInKhz == 0 ==\n\n"); printf("radeon_atombios.c:1546: ss->rate = le16(vbios) [no zero check], returns true\n"); printf("reference_clock=%u reference_divider=%u\n\n", reference_clock, reference_divider); printf("[A1] engine SS: si_dpm.c:4831 clk_s = ref*5 / (ref_div * ss.rate) with rate=0\n"); fflush(stdout); p = fork(); if (p == 0) { run_engine_ss(reference_clock, reference_divider, 0); _exit(0); } waitpid(p, &st, 0); report(st); printf("\n[A2] memory SS: si_dpm.c:4931 clks = ref*5 / ss.rate with rate=0\n"); fflush(stdout); p = fork(); if (p == 0) { run_memory_ss(reference_clock, 0); _exit(0); } waitpid(p, &st, 0); report(st); printf("\n[B] Fixed path (fix.diff: `if (ss.rate == 0) return -EINVAL;` before use)\n"); int rc = run_engine_ss_fixed(reference_clock, reference_divider, 0); printf(" rate=0 -> rc=%d (SS skipped, no trap, DPM continues without SS)\n", rc); rc = run_engine_ss_fixed(reference_clock, reference_divider, 30); printf(" rate=30 -> rc=0 (SS programmed normally)\n"); return 0; } |