DF-1018 / harness.c
/* * DF-1018 harness โ dastart TRIM unbounded ranges -> heap overflow + OOB DMA * * BUG: In dastart() at sys/bus/cam/scsi/scsi_da.c:1345-1360, the inner * while(count>0) loop writes req->data[ranges*8+0..7] incrementing * 'ranges' with NO bound against TRIM_MAX_RANGES (512). 'count' comes * from bp->b_bcount / softc->params.secsize, where secsize is device- * controlled (READ_CAPACITY). If secsize=1 and b_bcount=0x7FFF8000 * (the max set by DAIOCTRIM at :463), count=2147418112, producing * ~32768 ranges into data[4096] -> ~258KB heap overflow past the * trim_request.data[] array into da_softc (bios[], etc.). * * Additionally, cam_fill_csio at :1378 sets dxfer_len = * ((ranges+63)/64)*512 = ~262KB with data_ptr=req->data (only 4096 * valid), causing OOB DMA read of ~258KB kernel heap sent to the * device (info leak). * * The merge check at :1365-1367 only bounds SUBSEQUENT bios, not the * FIRST bio which is processed unconditionally. * * This harness demonstrates the overflow arithmetic. Runtime trigger * requires a SCSI Direct Access device with secsize=1 (or small) and * a TRIM request โ this QEMU guest has only a DVD-ROM (no da device). * * Build: cc -o harness harness.c * Run: ./harness */ #include <stdio.h> #include <stdlib.h> #include <stdint.h> #define TRIM_MAX_BLOCKS 8 #define TRIM_MAX_RANGES (TRIM_MAX_BLOCKS * 64) /* 512 */ int main(void) { /* Simulate trim_request.data[] โ 4096 bytes = 512 * 8 */ uint8_t data[TRIM_MAX_RANGES * 8]; /* 4096 bytes */ uint32_t data_len = sizeof(data); /* Simulate a malicious device with secsize=1 */ uint32_t secsize = 1; /* device-controlled, no validation */ /* DAIOCTRIM max b_bcount = 0x7FFF8000 (scsi_da.c:463) */ uint32_t b_bcount = 0x7FFF8000; /* dastart:1341 โ count = b_bcount / secsize */ uint32_t count = b_bcount / secsize; printf("DF-1018: TRIM heap overflow arithmetic\n"); printf(" TRIM_MAX_RANGES: %d (data array = %u bytes)\n", TRIM_MAX_RANGES, data_len); printf(" secsize (device-controlled): %u\n", secsize); printf(" b_bcount (DAIOCTRIM max): 0x%X\n", b_bcount); printf(" count = b_bcount/secsize: %u\n", count); /* Simulate the inner while(count>0) loop */ int ranges = 0; while (count > 0) { int c = count > 0xffff ? 0xffff : count; int off = ranges * 8; if (off >= (int)data_len) { /* OVERFLOW: writing past data[4096] */ if (ranges == TRIM_MAX_RANGES) printf("\n *** OVERFLOW STARTS HERE: ranges=%d, off=%d >= data_len=%u ***\n", ranges, off, data_len); } count -= c; ranges++; } uint32_t overflow_bytes = (ranges * 8) - data_len; uint32_t dxfer_len = ((ranges + 63) / 64) * 512; printf(" Total ranges written: %d (into array of %d)\n", ranges, TRIM_MAX_RANGES); printf(" Total bytes written: %d\n", ranges * 8); printf(" Heap overflow: %u bytes past data[]\n", overflow_bytes); printf(" OOB DMA dxfer_len: %u bytes (cam_fill_csio :1378)\n", dxfer_len); printf(" OOB DMA info leak: %u bytes of kernel heap sent to device\n", dxfer_len - data_len); printf("\n BUG CONFIRMED: %d ranges written into data[%d] = %u byte overflow.\n", ranges, TRIM_MAX_RANGES, overflow_bytes); printf(" The first bio is processed unconditionally (merge check at :1365\n"); printf(" only bounds subsequent bios). secsize=1 makes count enormous.\n"); return 0; } |