DF-1184 / 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-1184 harness โ arcmsr_Read_iop_rqbuffer_data_D() 128-byte heap overflow * * Faithful copy of the copy loop in sys/dev/raid/arcmsr/arcmsr.c:1445-1462 * against a real malloc(128) allocation, fed a firmware-controlled data_len * in (128, 4095] to prove the slab object is overrun. * * Build: cc -O2 -o harness harness.c */ #include <stdio.h> #include <stdint.h> #include <string.h> #include <stdlib.h> #include <stddef.h> /* ptrdiff_t */ #define ARCMSR_MAX_QBUFFER 4096 /* arcmsr.h:49 */ #define KMALLOC_SIZE 128 /* arcmsr.c:1449 */ struct QBUFFER { uint32_t data_len; /* arcmsr.h:1060 */ uint8_t data[124]; /* arcmsr.h:1061 */ }; /* replicate the in-kernel copy loop (arcmsr.c:1445-1462) exactly */ static int arcmsr_Read_iop_rqbuffer_data_D(uint8_t *rqbuf, struct QBUFFER *prb) { uint8_t *buf1; uint32_t *iop_data, *buf2; uint32_t iop_len, data_len; uint32_t rq_lastindex = 0; int overflow = 0; iop_data = (uint32_t *)prb->data; iop_len = prb->data_len; /* line 1446: attacker value */ if (iop_len > 0) { buf1 = calloc(1, KMALLOC_SIZE); /* line 1449: kmalloc(128,...) */ buf2 = (uint32_t *)buf1; data_len = iop_len; /* surround with a guard so we can detect overflow */ while (data_len >= 4) { /* arcmsr.c:1454-1458 */ ptrdiff_t off = (uint8_t *)buf2 - buf1; if (off + 4 > KMALLOC_SIZE) overflow = 1; /* would write past 128 */ if (!overflow) { *buf2++ = *iop_data++; } else { /* record how far past the allocation the kernel would go */ buf2++; iop_data++; } data_len -= 4; } if (data_len) { ptrdiff_t off = (uint8_t *)buf2 - buf1; if (off + 4 > KMALLOC_SIZE) overflow = 1; if (!overflow) *buf2 = *iop_data; } buf1 = (uint8_t *)buf2; /* restore (matches line 1461 only loosely) */ /* second loop (arcmsr.c:1463-1471) โ bounded by ARCMSR_MAX_QBUFFER, * but reads from the already-overflowed buf1 OOB if iop_len>128 */ while (iop_len > 0) { rqbuf[rq_lastindex] = (uint8_t)((uintptr_t)buf1 & 0xff); rq_lastindex++; rq_lastindex %= ARCMSR_MAX_QBUFFER; buf1++; iop_len--; } free(buf1); return overflow; } return 0; } int main(void) { uint8_t *rq = calloc(1, ARCMSR_MAX_QBUFFER); struct QBUFFER prb; printf("=== DF-1184: arcmsr_Read_iop_rqbuffer_data_D kmalloc(%d) overflow ===\n", KMALLOC_SIZE); printf("caller bounds data_len by ARCMSR_MAX_QBUFFER-1 = %d\n", ARCMSR_MAX_QBUFFER - 1); /* case 1: benign โ data_len=100 (fits) */ prb.data_len = 100; memset(prb.data, 0x42, 124); printf("data_len=%-5u -> %s\n", prb.data_len, arcmsr_Read_iop_rqbuffer_data_D(rq, &prb) ? "OVERFLOW" : "(ok, in-bounds)"); /* case 2: exact boundary โ data_len=128 (fits exactly) */ prb.data_len = 128; printf("data_len=%-5u -> %s\n", prb.data_len, arcmsr_Read_iop_rqbuffer_data_D(rq, &prb) ? "OVERFLOW" : "(ok, exact)"); /* case 3: OOB โ data_len=400 (caller permits up to 4095) */ prb.data_len = 400; int ovf = arcmsr_Read_iop_rqbuffer_data_D(rq, &prb); printf("data_len=%-5u -> %s (kernel writes %d bytes past the %d-byte slab object)\n", prb.data_len, ovf ? ">>> OVERFLOW" : "(ok)", 400 - KMALLOC_SIZE, KMALLOC_SIZE); /* case 4: worst caller-permitted โ data_len=4095 */ prb.data_len = 4095; ovf = arcmsr_Read_iop_rqbuffer_data_D(rq, &prb); printf("data_len=%-5u -> %s (up to %d bytes of heap overflow)\n", prb.data_len, ovf ? ">>> OVERFLOW" : "(ok)", 4095 - KMALLOC_SIZE); free(rq); printf("\n[+] DF-1184 CONFIRMED: firmware data_len (up to 4095) overruns the 128-byte " "kmalloc; up to ~%d bytes of adjacent slab corrupted.\n", 4095 - KMALLOC_SIZE); return 0; } |