DF-1199 / harness.c
/* * DF-1199 harness — radeon_atombios_get_asic_ss_info integer underflow * (userspace replica of sys/dev/drm/radeon/radeon_atombios.c:1536/1555/1577) * * size is uint16_t (from atom_parse_data_header, = BIOS usStructureSize). * num_indices is int. The kernel computes: * num_indices = (size - sizeof(ATOM_COMMON_TABLE_HEADER)) / sizeof(V2); * sizeof(HEADER)=4, sizeof(V2)=12. The subtraction is done in size_t (unsigned) * because sizeof yields size_t: if size<4, size_t wraps to ~0 ... and the * division yields 0x5555555555555555, truncated to int = 1431655765. The loop * then iterates 1.4 billion times reading 12 bytes per iteration past the BIOS * table into kernel memory -> panic on the first unmapped page, or a massive * kernel-memory read (info leak) until it faults. This harness reproduces the * arithmetic exactly (frev 2 path; frev 3 identical with sizeof(V3)=12). * * Build: cc -O2 -Wall -o harness harness.c * Run: ./harness */ #include <stdio.h> #include <stdint.h> #include <string.h> typedef uint16_t USHORT; typedef uint8_t UCHAR; typedef struct { USHORT usStructureSize; UCHAR a; UCHAR b; } ATOM_COMMON_TABLE_HEADER; typedef struct { UCHAR x[12]; } ATOM_ASIC_SS_ASSIGNMENT_V2; static int compute_num_indices(uint16_t size, size_t elem_sz) { int num_indices; /* exact kernel expression */ num_indices = (size - sizeof(ATOM_COMMON_TABLE_HEADER)) / elem_sz; return num_indices; } int main(void) { printf("== DF-1199 radeon_atombios_get_asic_ss_info underflow harness ==\n"); printf("sizeof(ATOM_COMMON_TABLE_HEADER)=%zu sizeof(V2)=%zu\n\n", sizeof(ATOM_COMMON_TABLE_HEADER), sizeof(ATOM_ASIC_SS_ASSIGNMENT_V2)); uint16_t sizes[] = { 0, 1, 2, 3, 4, 16, 40 }; int bug = 0; for (int k = 0; k < (int)(sizeof(sizes)/sizeof(sizes[0])); k++) { int ni = compute_num_indices(sizes[k], sizeof(ATOM_ASIC_SS_ASSIGNMENT_V2)); int dangerous = (sizes[k] < sizeof(ATOM_COMMON_TABLE_HEADER)) || ni < 0 || ni > 1000000; printf("size=%5u -> num_indices=%d%s\n", sizes[k], ni, dangerous ? " <-- UNDERFLOW (huge loop -> OOB read)" : ""); if (dangerous) bug = 1; } printf("\n"); /* Demonstrate the exact wrap value the finding cites (size=2, frev==2). */ int ni = compute_num_indices(2, 12); printf("Finding's cited value: size=2 -> num_indices=%d (== 1431655765 ? %s)\n", ni, ni == 1431655765 ? "YES" : "no"); /* Show the intermediate size_t arithmetic explicitly. */ uint16_t size = 2; size_t sub = (size_t)size - (size_t)sizeof(ATOM_COMMON_TABLE_HEADER); size_t div = sub / sizeof(ATOM_ASIC_SS_ASSIGNMENT_V2); printf("size_t trace: (%u - %zu) = 0x%016zx (%zu); /12 = 0x%016zx; " "trunc->int = %d\n", size, sizeof(ATOM_COMMON_TABLE_HEADER), sub, sub, div, ni); printf("\n[BUG %s] malicious VBIOS with size<4 drives a %d-iteration loop " "reading 12B/iter past the BIOS table.\n", bug ? "REPRODUCED" : "not-reproduced", ni); printf("Fixed kernel checks `size >= sizeof(ATOM_COMMON_TABLE_HEADER)` before " "the subtraction and bails.\n"); return bug ? 0 : 1; } |