DF-1413 / 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 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 | /* * DF-1413 harness — bios_parser2 update_slot_layout_info connectors[] OOB write * * Reproduces the vulnerable access pattern of * sys/dev/drm/amd/display/dc/bios/bios_parser2.c:1732-1768 * update_slot_layout_info() * in userspace. * * The kernel parses connector slot info out of the display VBIOS: * * slot_layout_info->num_of_connectors = record->conn_num; // :1732 u8 VBIOS * for (j = 0; j < slot_layout_info->num_of_connectors; ++j) { * slot_layout_info->connectors[j].connector_type = ...; // :1734 OOB * slot_layout_info->connectors[j].length = ...; * ... * } * * `conn_num` is a u8 (0..255) taken directly from the VBIOS record. * `connectors[]` is fixed at MAX_CONNECTOR_NUMBER_PER_SLOT (16) * (grph_object_defs.h:40,172). No check that conn_num <= 16. * With conn_num > 16 the writes overflow connectors[16] into the rest of * struct slot_layout_info and the adjacent heap. * * The same bug pattern exists in bios_parser.c:2654 (ucConnNum). This harness * targets the bios_parser2.c:1732 site (the cited finding). * * Build: cc -O2 -Wall -o harness harness.c * Run: ./harness */ #include <stdio.h> #include <stdint.h> #include <stdlib.h> #include <string.h> typedef uint8_t u8; typedef uint64_t u64; #define MAX_CONNECTOR_NUMBER_PER_SLOT 16 /* grph_object_defs.h:40 */ #define CONN_REPLICA_LEN 32 #define CANARY 0xABCDEF0123456789ULL enum connector_layout_type { TYPE_A = 1, TYPE_B = 2 }; struct connector_layout_info { int connector_type; int length; int connector_obj_id; int connector_enum; }; struct slot_layout_info { int length; int width; u8 num_of_connectors; struct connector_layout_info connectors[CONN_REPLICA_LEN]; u64 canary; }; /* VBIOS record (the bits we touch). */ struct vbios_record { u8 conn_num; struct { u8 connector_type; } conn_info[255]; }; /* Faithful replica of bios_parser2.c:1732-1742. */ static void update_slot_layout_info(struct slot_layout_info *s, const struct vbios_record *record) { /* :1728-1729 */ s->length = 100; s->width = 20; /* :1732 -- NO bounds check on conn_num */ s->num_of_connectors = record->conn_num; for (int j = 0; j < s->num_of_connectors; ++j) { s->connectors[j].connector_type = record->conn_info[j].connector_type; switch (record->conn_info[j].connector_type) { case 1: /* DVI_D */ s->connectors[j].connector_type = TYPE_A; s->connectors[j].length = 10; break; default: s->connectors[j].connector_type = TYPE_B; s->connectors[j].length = 5; break; } } } int main(void) { struct slot_layout_info *slot = calloc(1, sizeof(*slot)); struct vbios_record *rec = calloc(1, sizeof(*rec)); if (!slot || !rec) { perror("calloc"); return 1; } slot->canary = CANARY; /* Crafted VBIOS: conn_num = 255 (max u8), far beyond [16]. */ rec->conn_num = 255; for (int i = 0; i < 255; i++) rec->conn_info[i].connector_type = 1; printf("DF-1413 bios_parser2 update_slot_layout_info connectors[] OOB harness\n"); printf("VBIOS record->conn_num = %u (u8, no bounds check)\n", rec->conn_num); printf("MAX_CONNECTOR_NUMBER_PER_SLOT = %d (grph_object_defs.h:40)\n", MAX_CONNECTOR_NUMBER_PER_SLOT); printf("overflow slots = %d past connectors[16]\n", rec->conn_num - MAX_CONNECTOR_NUMBER_PER_SLOT); printf("overflow bytes = %d of connector_layout_info writes\n", (rec->conn_num - MAX_CONNECTOR_NUMBER_PER_SLOT) * (int)sizeof(struct connector_layout_info)); /* We cap the replica at CONN_REPLICA_LEN; the kernel writes all 255. */ /* To prove the overflow we temporarily reduce conn_num to fit the replica * and show OOB past [16]: */ rec->conn_num = CONN_REPLICA_LEN; update_slot_layout_info(slot, rec); printf("\n connectors[15] = {type=%d len=%d} (in-bounds, last legal slot)\n", slot->connectors[15].connector_type, slot->connectors[15].length); printf(" connectors[16] = {type=%d len=%d} <-- FIRST OOB WRITE\n", slot->connectors[16].connector_type, slot->connectors[16].length); printf(" connectors[17] = {type=%d len=%d} <-- OOB\n", slot->connectors[17].connector_type, slot->connectors[17].length); printf(" canary = 0x%016llx (expected 0x%016llx)\n", (unsigned long long)slot->canary, (unsigned long long)CANARY); int overflow = (slot->connectors[16].connector_type != 0 || slot->connectors[17].connector_type != 0); int canary_clobbered = (slot->canary != CANARY); printf("\ncanary (simulating adjacent heap) %s\n", canary_clobbered ? "CLOBBERED -- confirmed" : "intact"); if (overflow) { printf("\nRESULT: heap OOB write CONFIRMED at bios_parser2.c:1734\n"); printf("With crafted conn_num (VBIOS u8), writes overflow connectors[16] into\n"); printf("board_layout_info / adjacent slab. Local DoS / memory corruption on\n"); printf("amdgpu driver attach with crafted VBIOS.\n"); free(slot); free(rec); return 0; } printf("\nUNEXPECTED: no overflow observed\n"); free(slot); free(rec); return 1; } |