DF-1783 / harness.c
/* * DF-1783 - radeon_bios.c heap overflow in radeon_atrm_call: * unbounded memcpy of ACPI-returned buffer into 256 KB bios buffer. * * Vulnerable code (sys/dev/drm/radeon/radeon_bios.c): * 207 memcpy(bios+offset, obj->Buffer.Pointer, obj->Buffer.Length); * // uses ACPI-returned Length, NOT the caller's len arg * 208 len = obj->Buffer.Length; * * No obj->Type == ACPI_TYPE_BUFFER check. ACPI_OBJECT is a union; if the * method returns an Integer/String, Buffer.Pointer/Length alias other * union fields -> wild pointer / wild length. * * Caller (radeon_atrm_get_bios at line 269) allocates 256*1024, then * loops i=0..63 calling radeon_atrm_call(..., i*4096, 4096). At i=63, * offset = 63*4096 = 258048; the buffer has 256*1024-258048 = 4096 bytes * left. If the ACPI method returns a buffer with Length > 4096 at i=63, * memcpy overflows rdev->bios by (Length - 4096) bytes into adjacent heap. * * The only upper bound check at line 281 is `if (ret < ATRM_BIOS_PAGE) * break;` which catches short reads but NOT over-long ones. * * Trigger: malicious platform firmware / custom SSDT / PCI option ROM * installing a malicious _ATRM method. * * This harness simulates the i=63 case. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdint.h> #define ATRM_BIOS_PAGE 4096 #define BIOS_SIZE (256 * 1024) int main(void) { uint8_t *bios = malloc(BIOS_SIZE); memset(bios, 0, BIOS_SIZE); printf("=== DF-1783 radeon_atrm_call heap overflow harness ===\n"); printf("rdev->bios = malloc(%d) [radeon_bios.c:269]\n", BIOS_SIZE); printf("\n"); /* emulate the buggy caller loop at radeon_bios.c:275-283 */ long total = 0; int overflows = 0; for (int i = 0; i < BIOS_SIZE / ATRM_BIOS_PAGE; i++) { long offset = (long)i * ATRM_BIOS_PAGE; /* Malicious _ATRM returns 8192 bytes at i=63 (the last iter) */ long acpi_len = (i == 63) ? 8192 : 4096; long space = BIOS_SIZE - offset; printf("i=%2d: offset=%ld, ACPI len=%ld, space left=%ld%s\n", i, offset, acpi_len, space, (acpi_len > space) ? " <-- OVERFLOW" : ""); if (acpi_len > space) overflows++; /* buggy memcpy uses ACPI Length unconditionally */ long copy = acpi_len; /* line 207: obj->Buffer.Length */ if (offset + copy > BIOS_SIZE) { printf(" memcpy writes %ld bytes past rdev->bios end!\n", offset + copy - BIOS_SIZE); } total += copy; if (acpi_len < ATRM_BIOS_PAGE) break; /* line 281: only catches SHORT */ } printf("\n"); if (overflows > 0) { printf("VERDICT: BUG CONFIRMED. radeon_atrm_call memcpy uses the\n" " ACPI-returned Length without clamping to the caller's\n" " `len` argument or checking obj->Type==BUFFER. A malicious\n" " _ATRM method overflows rdev->bios (256 KB) at i=63.\n"); free(bios); return 0; } printf("VERDICT: not reproduced.\n"); free(bios); return 1; } |