DF-3648 / 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 | /* * ci_primitives_harness.c — userland proof-of-primitive harness for the * source findings in DragonFlyBSD sys/dev/drm/radeon/ci_dpm.c (pass 2). * * It models, with the exact types used in the kernel: * [A] DF-3644: ci_get_svi2_voltage_table() copies a VBIOS dependency count * (u8, up to 255) into struct atom_voltage_table.entries[32] BEFORE the * si_trim_voltage_table_to_fit_state_table() call -> OOB write geometry. * [B] DF-3647: ci_calculate_mclk_params()/ci_calculate_sclk_params() divide * by ss.rate which radeon_atombios_get_asic_ss_info() copies raw from * the VBIOS (radeon_atombios.c:1546) -> div-by-zero -> SIGFPE. * [C] DF-3648: ci_calculate_mclk_params() divides freq_nom by * rdev->clock.mpll.reference_freq stored unchecked from the VBIOS * (radeon_atombios.c:1216-1221) -> div-by-zero -> SIGFPE. * [D] DF-3649: ci_get_std_voltage_value_sidd() computes * idx = (u16)(cac_leakage_table.count - 1) with count==0 (entries is * ZERO_LENGTH_PTR == (void*)-8, non-NULL, passes the guard) -> index * 65535 -> wild read address. * [E] DF-3650: the VCE fill loop indexes clockInfo[clk_idx * ucEntrySize] * with clk_idx = ucClockInfoIndex & 0x3f (<=63) and ucEntrySize a raw * u8, with NO ucNumEntries bound -> byte-offset geometry. * * Build: cc -O2 -o harness ci_primitives_harness.c * Run: ./harness (survives; prints geometry + child exit codes) */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdint.h> #include <unistd.h> #include <signal.h> #include <sys/wait.h> #define MAX_VOLTAGE_ENTRIES 32 struct atom_voltage_table_entry { uint16_t value; uint32_t smio_low; }; struct atom_voltage_table { uint32_t count; uint32_t mask_low; uint32_t phase_delay; struct atom_voltage_table_entry entries[MAX_VOLTAGE_ENTRIES]; }; struct dep_entry { uint32_t clk; uint32_t v; }; struct dep_table { uint32_t count; struct dep_entry entries[255]; }; /* canary placed immediately after a truncated (stack) voltage table to * emulate the next field of struct ci_power_info (vddci_voltage_table) */ struct pi_slice { struct atom_voltage_table vddc; /* the overflow source */ unsigned char canary[64]; /* stands in for the rest */ }; /* [A] exact copy loop of ci_get_svi2_voltage_table (ci_dpm.c:2159-2163) */ static void get_svi2(struct dep_table *dep, struct atom_voltage_table *vt) { uint32_t i; vt->mask_low = 0; vt->phase_delay = 0; vt->count = dep->count; for (i = 0; i < vt->count; i++) { vt->entries[i].value = (uint16_t)dep->entries[i].v; vt->entries[i].smio_low = 0; } } static int expect_fpe(void (*fn)(void)) { pid_t p = fork(); if (p == 0) { fn(); _exit(0); } int st = 0; waitpid(p, &st, 0); if (WIFSIGNALED(st) && WTERMSIG(st) == SIGFPE) return 1; /* integer div-by-zero faulted */ return 0; } static volatile uint32_t g_sink; static volatile uint32_t g_zero = 0; /* runtime zero: VBIOS-supplied value */ static void div_ss_rate(void) { uint32_t x = 100 * 5 / g_zero; g_sink = x; } static void div_ref_clock(void) { uint32_t x = 300000 / g_zero; g_sink = x; } int main(void) { /* ---------- [A] DF-3644: entries[32] overflow geometry ---------- */ struct pi_slice *pi = calloc(1, sizeof(*pi)); struct dep_table *dep = calloc(1, sizeof(*dep)); memset(pi->canary, 0xAA, sizeof(pi->canary)); dep->count = 200; /* crafted VBIOS u8 count */ for (uint32_t i = 0; i < dep->count; i++) dep->entries[i] = (struct dep_entry){ .clk = 50000 + i * 1000, .v = 900 + i }; /* u16 voltages */ get_svi2(dep, &pi->vddc); /* ci_dpm.c:2160 loop */ size_t overflow_bytes = (dep->count > MAX_VOLTAGE_ENTRIES) ? (dep->count - MAX_VOLTAGE_ENTRIES) * sizeof(struct atom_voltage_table_entry) : 0; int canary_hit = memcmp(pi->canary, "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA", 8) != 0; printf("[A] DF-3644: count=%u entries_cap=%d -> OOB write of %zu bytes past entries[32]; " "bytes past end of struct atom_voltage_table: %s; canary clobbered: %s\n", dep->count, MAX_VOLTAGE_ENTRIES, overflow_bytes, overflow_bytes > 64 ? "YES (canary/adjacent memory)" : "marginal", canary_hit ? "YES" : "no"); free(pi); free(dep); /* ---------- [B] DF-3647: ss.rate == 0 division ---------- */ printf("[B] DF-3647: 100*5/ss.rate(0) -> %s\n", expect_fpe(div_ss_rate) ? "SIGFPE (kernel: #DE panic)" : "no fault (unexpected)"); /* ---------- [C] DF-3648: reference_freq == 0 division ---------- */ printf("[C] DF-3648: freq_nom/ref(0) -> %s\n", expect_fpe(div_ref_clock) ? "SIGFPE (kernel: #DE panic)" : "no fault (unexpected)"); /* ---------- [D] DF-3649: idx = (u16)(count-1), count==0 ---------- */ uint32_t cac_count = 0; uint16_t idx = (uint16_t)(cac_count - 1); /* ci_dpm.c:2382 */ union cac_entry { struct { uint16_t vddc; uint32_t leak; } a; }; union cac_entry *entries = (union cac_entry *)(uintptr_t)0xFFFFFFFFFFFFFFF8UL; /* ZERO_LENGTH_PTR (kern_slaballoc.c:193) */ uint64_t addr = (uint64_t)(uintptr_t)&entries[idx]; printf("[D] DF-3649: count=0 -> idx=%u; &entries[idx] = 0x%llx -> %s\n", idx, (unsigned long long)addr, "unmapped kernel address (wild read -> page fault / panic)"); /* ---------- [E] DF-3650: VCE clk_idx byte-offset geometry ---------- */ uint8_t clk_idx = 63 & 0x3f; /* r600_dpm.c:1127 mask */ uint8_t entry_size = 255; /* raw u8 from VBIOS */ uint32_t num_entries = 1; /* crafted tiny array */ printf("[E] DF-3650: clk_idx=%u * entrySize=%u = %u bytes past clockInfo[0]" " while ucNumEntries=%u (unchecked, ci_dpm.c:5656-5658 vs :5637)\n", clk_idx, entry_size, (unsigned)(clk_idx * entry_size), num_entries); return 0; } |