DF-1250 / harness.c
/* * DF-1250 harness โ radeon_combios_check_hardcoded_edid() BIOS over-read. * * sys/dev/drm/radeon/radeon_combios.c:364-388: * raw = rdev->bios + edid_info; (line 373) * size = EDID_LENGTH * (raw[0x7e] + 1); (line 374) -> up to 128*256 = 32768 * edid = kmalloc(size, ...); (line 375) * memcpy(edid, raw, size); (line 379) * if (!drm_edid_is_valid(edid)) ... (line 381) -- checked AFTER memcpy * * `rdev->bios` can be a small allocation: radeon_read_platform_bios() reads * bios[2]*512 bytes; a malicious ROM can declare a 512-byte BIOS image while * the hardcoded-EDID table's raw[0x7e] byte == 0xff, making size = 32768. * The memcpy then reads 32768 bytes from a 512-byte allocation: ~32 KB of * adjacent kernel heap is leaked into `edid`, which the caller may later copy * to userspace. drm_edid_is_valid is checked only AFTER the over-read, so the * leak happens unconditionally. * * Kernel trigger requires an AMD/ATI radeon GPU whose (malicious) Video BIOS * contains a crafted COMBIOS hardcoded-EDID table โ absent from this guest. * This harness replicates the size computation + memcpy and proves the * over-read math against a 512-byte BIOS slab. */ #include <stdio.h> #include <stdint.h> #include <string.h> #include <stdlib.h> #define EDID_LENGTH 128 int main(void) { /* Model rdev->bios as a 512-byte allocation (platform BIOS path). */ size_t bios_alloc = 512; uint8_t *bios = calloc(1, bios_alloc); /* The hardcoded-EDID table lives at some offset inside the BIOS; model * edid_info near the start so raw[0x7e] is still inside the alloc. */ int edid_info = 0x40; uint8_t *raw = bios + edid_info; /* Malicious ROM sets the extension-count byte raw[0x7e] = 0xff. */ raw[0x7e] = 0xff; /* radeon_combios.c:374 โ size = EDID_LENGTH * (raw[0x7e] + 1) */ int size = EDID_LENGTH * (raw[0x7e] + 1); printf("bios allocation = %zu bytes\n", bios_alloc); printf("edid_info = 0x%x, raw = bios+%d\n", edid_info, edid_info); printf("raw[0x7e] = 0x%02x\n", raw[0x7e]); printf("computed size (radeon_combios.c:374) = %d bytes\n", size); /* How far past the BIOS allocation would the memcpy read? */ size_t copy_end = (size_t)edid_info + (size_t)size; long overread = (long)copy_end - (long)bios_alloc; printf("memcpy reads raw[0 .. %d) = bios[%d .. %zu)\n", size, edid_info, copy_end); printf("over-read past BIOS allocation: %ld bytes of adjacent kernel heap\n", overread); if (overread > 0) { printf("\nPRIMITIVE CONFIRMED: memcpy(edid, raw, %d) reads %ld bytes past a %zu-byte BIOS slab.\n", size, overread, bios_alloc); printf("drm_edid_is_valid is checked only AFTER (radeon_combios.c:381) -> leak is unconditional. Bug is REAL.\n"); free(bios); return 0; } printf("\nUNEXPECTED: no over-read\n"); free(bios); return 1; } |