DF-1284 / 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 | /* * DF-1284 harness: unbounded scatter-gather loop overflows pmap->psg[17] in * hptmv's OsSendCommand CAM_SCATTER_VALID path (entry.c). * * Object-level proof. The hptmv driver attaches to HighPoint RocketRAID * controllers; none are present in the audit QEMU guest, so the SIM action * path is not runtime-reachable here. This harness replays the exact loop * with a CAM CCB whose sglist_cnt (u16) exceeds MAX_SG_DESCRIPTORS (17). * * Cited path: sys/dev/raid/hptmv/entry.c:2800 (pSgTable=pmap->psg), * :2852 CAM_SCATTER_VALID branch, :2859 for(idx<sglist_cnt) writes * pSgTable[idx], :2860-2862 field writes. psg is SCAT_GATH[17] * (osbsd.h:143, MAX_SG_DESCRIPTORS=17 global.h:47). sglist_cnt is * u_int16_t (cam_ccb.h:604), NOT validated against MAX_SG_DESCRIPTORS. * * Build: cc -O2 -o harness harness.c * Run: ./harness */ #include <stdio.h> #include <stdlib.h> #include <stdint.h> #include <string.h> typedef uint64_t ULONG_PTR; typedef uint32_t ULONG; typedef uint16_t USHORT; typedef uint8_t UCHAR; typedef ULONG_PTR PVOID; #define MAX_SG_DESCRIPTORS 17 /* global.h:47 */ #define SG_FLAG_EOT 0x8000 /* global.h:119 */ /* Mirror of sys/dev/raid/hptmv/global.h:103 SCAT_GATH */ typedef struct _SCAT_GATH { ULONG_PTR dSgAddress; USHORT wSgSize; USHORT wSgFlag; } SCAT_GATH; /* Mirror of sys/dev/raid/hptmv/osbsd.h:138 BUS_DMAMAP (the allocation that * holds psg[]; pmap points at one of these). What follows psg[] in the real * struct is the next slab object / the IALAdapter linkage -- the guard here * stands in for the adjacent heap victim. */ typedef struct _BUS_DMAMAP { struct _BUS_DMAMAP *next; void *pAdapter; /* struct IALAdapter * */ void *dma_map; /* bus_dmamap_t */ SCAT_GATH psg[MAX_SG_DESCRIPTORS]; /* ---- end of the real struct; guard below simulates adjacent slab ---- */ SCAT_GATH guard[64]; } BUS_DMAMAP; /* bus_dma_segment_t is { uint32_t ds_addr (or uint64_t); bus_size_t ds_len } */ typedef struct { uint64_t ds_addr; uint64_t ds_len; } bus_dma_segment_t; int main(void) { BUS_DMAMAP *pmap = calloc(1, sizeof(*pmap)); if (!pmap) { perror("calloc"); return 1; } SCAT_GATH *pSgTable = pmap->psg; /* entry.c:2800 */ /* paint the guard so we can detect overflow */ memset(pmap->guard, 0xAA, sizeof(pmap->guard)); printf("[DF-1284] psg[%d] in BUS_DMAMAP, sizeof(SCAT_GATH)=%zu, " "psg spans bytes 0..%zu\n", MAX_SG_DESCRIPTORS, sizeof(SCAT_GATH), sizeof(pmap->psg)); /* attacker: a CAM CCB with CAM_SCATTER_VALID and a huge sglist_cnt. * sglist_cnt is u_int16_t, range 0..65535. XPT does NOT clamp it to the * SIM's segment limit. */ uint16_t sglist_cnt = 64; /* >> MAX_SG_DESCRIPTORS(17) */ bus_dma_segment_t *sgList = calloc(sglist_cnt, sizeof(*sgList)); for (uint16_t i = 0; i < sglist_cnt; i++) { sgList[i].ds_addr = 0x1000+i; sgList[i].ds_len = 4096; } printf("[DF-1284] attacker sglist_cnt=%u (max allowed for psg[]=%d)\n", sglist_cnt, MAX_SG_DESCRIPTORS); /* === entry.c:2859-2863 (the unbounded loop) === */ int idx; for (idx = 0; idx < sglist_cnt; idx++) { pSgTable[idx].dSgAddress = (ULONG_PTR)(UCHAR *)sgList[idx].ds_addr; /* :2860 */ pSgTable[idx].wSgSize = (USHORT)sgList[idx].ds_len; /* :2861 */ pSgTable[idx].wSgFlag = (idx == sglist_cnt-1) ? SG_FLAG_EOT : 0; /* :2862 */ } /* count guard entries clobbered (== adjacent slab object(s) overwritten) */ unsigned hit = 0; for (unsigned g = 0; g < 64; g++) { uint8_t *p = (uint8_t *)&pmap->guard[g]; uint8_t *e = p + sizeof(pmap->guard[g]); for (; p < e; p++) if (*p != 0xAA) { hit++; break; } } printf("[DF-1284] overflow past psg[17]: %s (%u guard entries clobbered; " "%zd bytes of adjacent heap overwritten)\n", hit ? "YES" : "no", hit, (ssize_t)((sglist_cnt - MAX_SG_DESCRIPTORS) * sizeof(SCAT_GATH))); puts("\n--- Fix demonstration ---"); /* entry.c already has, in the *other* path (hptmv_dmamap_callback:2652): * HPT_ASSERT(nsegs <= MAX_SG_DESCRIPTORS); * The CAM_SCATTER_VALID path needs the same clamp: */ if (sglist_cnt > MAX_SG_DESCRIPTORS) { printf("[DF-1284] FIX: clamp/REJECT sglist_cnt=%u > MAX_SG_DESCRIPTORS=%d " "-> no overflow\n", sglist_cnt, MAX_SG_DESCRIPTORS); } free(sgList); free(pmap); return hit ? 0 : 2; } |