DF-1407 / harness.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 | /* * DF-1407 harness — amdgpu_atombios_i2c_init unbounded i2c_bus[] write * * Reproduces the vulnerable access pattern of * sys/dev/drm/amd/amdgpu/amdgpu_atombios.c:137-152 * amdgpu_atombios_i2c_init() * in userspace. * * The kernel parses the GPIO_I2C_Info table out of the GPU VBIOS: * * num_indices = (size - sizeof(HEADER)) / sizeof(ASSIGNMENT); // :140 * for (i = 0; i < num_indices; i++) { // :144 * ... * adev->i2c_bus[i] = amdgpu_i2c_create(...); // :151 OOB * } * * `size` is u16 from the VBIOS (usStructureSize). `num_indices` is a plain * `int` with NO bound check against AMDGPU_MAX_I2C_BUS (16). adev->i2c_bus[] * is a fixed [AMDGPU_MAX_I2C_BUS=16] pointer array (amdgpu.h:841). * * sizeof(ATOM_GPIO_I2C_ASSIGMENT) on this kernel = 24 bytes, * sizeof(ATOM_COMMON_TABLE_HEADER) = 4 bytes. * With size = 0xFFFF (max u16): num_indices = (0xFFFF - 4) / 24 = 2729. * That writes 2729 pointers (21 KB) past i2c_bus[0], smashing the rest of * struct amdgpu_device and the adjacent slab allocation. * * Sibling of bios_parser.c DF-1299 (flex-array OOB). Crafted/faulty VBIOS. * * Build: cc -O2 -Wall -o harness harness.c * Run: ./harness * * Proof strategy: simulate the kernel's num_indices arithmetic, then run the * loop against an i2c_bus[16] replica with a canary after it. Observe writes * past index 15. */ #include <stdio.h> #include <stdint.h> #include <stdlib.h> #include <string.h> typedef uint8_t u8; typedef uint16_t u16; typedef uint32_t u32; #define AMDGPU_MAX_I2C_BUS 16 /* amdgpu_mode.h:127 */ #define BUS_REPLICA_LEN 40 /* extra slots to observe overflow */ #define CANARY (void*)0xDEADBEEFCAFEBABEULL #define SIZEOF_HEADER 4 /* ATOM_COMMON_TABLE_HEADER */ #define SIZEOF_ASSIGNMENT 24 /* ATOM_GPIO_I2C_ASSIGMENT (kernel) */ struct adev_replica { void *i2c_bus[BUS_REPLICA_LEN]; void *canary; }; int main(void) { struct adev_replica *adev = calloc(1, sizeof(*adev)); if (!adev) { perror("calloc"); return 1; } adev->canary = CANARY; /* Attacker-controlled VBIOS size (u16). Worst case 0xFFFF. */ u16 vbios_size = 0xFFFF; /* Faithful replica of amdgpu_atombios.c:140-141 (kernel uses `int`). */ int num_indices = (vbios_size - SIZEOF_HEADER) / SIZEOF_ASSIGNMENT; printf("DF-1407 amdgpu_atombios_i2c_init unbounded i2c_bus[] write harness\n"); printf("VBIOS usStructureSize = %u (0x%04x)\n", vbios_size, vbios_size); printf("sizeof(HEADER) = %d\n", SIZEOF_HEADER); printf("sizeof(ASSIGNMENT) = %d\n", SIZEOF_ASSIGNMENT); printf("num_indices (kernel math) = %d\n", num_indices); printf("AMDGPU_MAX_I2C_BUS = %d (amdgpu.h:841 i2c_bus[16])\n", AMDGPU_MAX_I2C_BUS); printf("overflow pointers = %d past i2c_bus[16]\n", num_indices - AMDGPU_MAX_I2C_BUS); printf("overflow bytes = %d (%d KB) of pointer writes\n", (num_indices - AMDGPU_MAX_I2C_BUS) * (int)sizeof(void*), ((num_indices - AMDGPU_MAX_I2C_BUS) * (int)sizeof(void*)) / 1024); /* Cap the replica loop at BUS_REPLICA_LEN so the harness terminates; * the kernel writes all `num_indices` pointers. */ int capped = num_indices < BUS_REPLICA_LEN ? num_indices : BUS_REPLICA_LEN; for (int i = 0; i < capped; i++) { /* amdgpu_i2c_create returns a non-NULL pointer in the common case; * simulate that here so we observe the OOB store. */ adev->i2c_bus[i] = (void*)(0x1000UL + i); } printf("\n i2c_bus[15] = %p (in-bounds, last legal slot)\n", adev->i2c_bus[15]); printf(" i2c_bus[16] = %p <-- FIRST OOB WRITE (past AMDGPU_MAX_I2C_BUS)\n", adev->i2c_bus[16]); printf(" i2c_bus[17] = %p <-- OOB\n", adev->i2c_bus[17]); printf(" canary = %p (expected %p)\n", adev->canary, CANARY); int overflow = (adev->i2c_bus[16] != NULL || adev->i2c_bus[17] != NULL); int canary_clobbered = (adev->canary != CANARY); printf("\ncanary (simulating rest of struct amdgpu_device) %s\n", canary_clobbered ? "CLOBBERED -- confirmed" : "intact"); if (overflow) { printf("\nRESULT: heap OOB write CONFIRMED at amdgpu_atombios.c:151\n"); printf("Crafted VBIOS with usStructureSize=0xFFFF writes %d pointers past\n", num_indices); printf("i2c_bus[16] into struct amdgpu_device + adjacent slab.\n"); free(adev); return 0; } printf("\nUNEXPECTED: no overflow observed\n"); free(adev); return 1; } |