DF-1139 / harness.c
/* * DF-1139 harness: radeon/si_dpm.c si_get_std_voltage_value * -> OOB read when cac_leakage_table.count==0 (si_dpm.c:4158-4187) * * Userspace logic harness. r600_dpm.c:1039-1043 allocates the leakage table * with kzalloc(ucNumEntries * sizeof(entry)); when ucNumEntries==0, kzalloc(0) * returns ZERO_SIZE_PTR (==16), which is non-NULL. The guard at si_dpm.c:4158 * `if (entries)` only checks for NULL, so the body executes with count==0; * the fallback at line 4172 reads entries[(u32)count - 1] = entries[0xFFFFFFFF] * -> ~64 GB OOB read -> page fault -> panic. * * Build: cc -O2 -o harness harness.c * Run: ./harness */ #include <stdio.h> #include <stdint.h> #include <stdlib.h> #include <string.h> #define ZERO_SIZE_PTR ((void *)16) /* matches Linux/DragonFly SMALL_ALLOC */ struct cac_leakage_entry { uint16_t vddc; uint16_t pad; uint32_t leakage; }; struct cac_leakage_table { struct cac_leakage_entry *entries; uint32_t count; }; /* Mirror of buggy guard (si_dpm.c:4158) -- only checks non-NULL pointer */ static int buggy_read(struct cac_leakage_table *t, uint32_t v_index) { if (t->entries) { /* fallthrough path si_dpm.c:4170-4172 -- when v_index >= count * (always true when count==0), uses entries[count-1] */ uint32_t back_idx = t->count - 1; /* (u32)(0 - 1) */ return t->entries[back_idx].vddc; /* OOB read */ } return -1; } /* Fixed guard -- also requires count > 0 */ static int fixed_read(struct cac_leakage_table *t, uint32_t v_index) { if (t->entries && t->count > 0) { uint32_t back_idx = t->count - 1; return t->entries[back_idx].vddc; } return -1; } int main(void) { /* Simulate a crafted VBIOS: ATOM_PPLIB_CAC_Leakage_Table with ucNumEntries==0 */ struct cac_leakage_table crafted = { .entries = (struct cac_leakage_entry *)ZERO_SIZE_PTR, .count = 0, }; printf("=== DF-1139 harness: crafted VBIOS with cac_leakage_table.ucNumEntries==0 ===\n\n"); printf(" r600_dpm.c:1039-1043 calls kzalloc(0 * sizeof(entry)) = kzalloc(0)\n"); printf(" -> returns ZERO_SIZE_PTR == %p (NON-NULL)\n", ZERO_SIZE_PTR); printf(" -> count = 0 (loaded directly from BIOS u8)\n\n"); printf("[A] Buggy guard (si_dpm.c:4158 `if (entries)`):\n"); printf(" entries=%p is non-NULL, so body executes with count==u%d\n", crafted.entries, 0); /* We do NOT actually dereference ZERO_SIZE_PTR+0xFFFFFFFF -- userspace would SIGSEGV * just like the kernel would page-fault. We compute the offset to demonstrate. */ uint32_t back_idx = (uint32_t)(crafted.count - 1); uint64_t fault_addr = (uint64_t)crafted.entries + back_idx * sizeof(struct cac_leakage_entry); printf(" reads entries[count-1] = entries[0x%08x]\n", back_idx); printf(" effective address = %p + 0x%08x * %zu = 0x%016llx\n", crafted.entries, back_idx, sizeof(struct cac_leakage_entry), (unsigned long long)fault_addr); printf(" >>> In kernel: page-fault at ~%llu GB offset -> panic <<<\n\n", (unsigned long long)fault_addr / 1024 / 1024 / 1024); printf("[B] Fixed guard (fix.diff: `if (entries && count > 0)`):\n"); int rc = fixed_read(&crafted, 0); printf(" rc=%d (rejected, no OOB read)\n", rc); return 0; } |