DF-3620 / ci_cac_wildread.c
/* DF-3620 โ logic proof for the ci_get_std_voltage_value_sidd wild OOB read. * * Path (amdgpu ci_dpm.c): ci_dpm_enable -> ci_init_smc_table (ci_dpm.c:3712) * -> ci_populate_smc_voltage_tables -> ci_populate_smc_voltage_table (2341) * -> ci_get_std_voltage_value_sidd (2467): * * if (adev->pm.dpm.dyn_state.cac_leakage_table.entries) { // 2479 โ true even when count==0! * ... * if ((u32)v_index < adev->pm.dpm.dyn_state.cac_leakage_table.count) * idx = v_index; * else * idx = adev->pm.dpm.dyn_state.cac_leakage_table.count - 1; // 2487: 0-1 * *std_voltage_lo_sidd = ...entries[idx].vddc ...; // 2489 โ idx == 0xFFFF * * count==0 precondition: amdgpu_parse_extended_power_table (amdgpu_dpm.c:495-526) * only allocates the entries array when usCACLeakageTableOffset != 0, and does * size = ucNumEntries * sizeof(...); entries = kzalloc(size, ...) * With ucNumEntries == 0 the allocation is kzalloc(0). On DragonFly, * _kmalloc() returns ZERO_LENGTH_PTR == (void *)-8 for size 0 * (sys/kern/kern_slaballoc.c:888-891, ZERO_LENGTH_PTR at :193) โ NON-NULL โ * so the `if (!entries)` guard does not fire and count stays 0. * * idx is u16; count is u32; `count - 1` == 0xFFFFFFFF truncates to 0xFFFF. * entries[0xFFFF] reads at ZERO_LENGTH_PTR + 65535*8 = ~0x7FFF8 โ unmapped * low canonical address -> kernel page fault / panic (wild OOB read). * * Build: cc -O2 -Wall -o ci_cac_wildread ci_cac_wildread.c * Run: ./ci_cac_wildread (simulates the index arithmetic; the actual * dereference is emulated, since executing it for real would fault) */ #include <stdio.h> #include <stdint.h> #define ZERO_LENGTH_PTR ((void *)-8) #define WILD_READ_UADDR ((uintptr_t)ZERO_LENGTH_PTR + 65535UL * 8UL) int main(void) { uint32_t cac_count = 0; /* ucNumEntries == 0 from crafted VBIOS */ uint16_t idx; uint32_t v_index = 0; /* first matching vddc_dependency_on_sclk entry */ /* ci_dpm.c:2484-2487 */ if (v_index < cac_count) idx = v_index; else idx = (uint16_t)(cac_count - 1); /* exact expression: u32 0-1 -> u16 */ printf("cac_leakage_table.entries = %p (ZERO_LENGTH_PTR, non-NULL)\n", ZERO_LENGTH_PTR); printf("cac_leakage_table.count = %u\n", cac_count); printf("idx (u16) after count-1 = 0x%04x (%u)\n", idx, idx); printf("wild read address = entries[0xFFFF] = %#llx -> %s\n", (unsigned long long)WILD_READ_UADDR, WILD_READ_UADDR < 0x0000800000000000UL ? "low canonical (unmapped in kernel ctx) -> kernel page fault / panic" : "high canonical"); printf("sizeof(union amdgpu_cac_leakage_entry)-based stride: 8 bytes\n"); printf("offset = 65535 * 8 = %lu bytes past a ZERO_SIZE allocation\n", 65535UL * 8UL); return 0; } |