--- a/sys/dev/disk/advansys/adwcam.c +++ b/sys/dev/disk/advansys/adwcam.c @@ -847,7 +847,13 @@ case 9: { struct sg_map_node *sg_map; + u_int i; + /* DF-4309: destroy the per-ACB buffer maps (created in + * adwallocacbs) before the buffer tag (case 2). */ + for (i = 0; i < adw->num_acbs; i++) + bus_dmamap_destroy(adw->buffer_dmat, + adw->acbs[i].dmamap); while ((sg_map = SLIST_FIRST(&adw->sg_maps)) != NULL) { SLIST_REMOVE_HEAD(&adw->sg_maps, links); bus_dmamap_unload(adw->sg_dmat, @@ -1265,6 +1271,8 @@ struct adw_carrier *free_carrier; struct acb *acb; union ccb *ccb; + u_int32_t areq_ba; + u_int32_t next_ba; #if 0 kprintf("0x%x, 0x%x, 0x%x, 0x%x\n", @@ -1276,8 +1284,23 @@ /* * The firmware copies the adw_scsi_req_q.acb_baddr * field into the areq_ba field of the carrier. + * + * DF-4307: areq_ba/next_ba are device-written 32-bit + * tokens. acbbotov()/carrierbotov() do no bounds + * checking, so a bogus token yields an out-of-bounds + * kernel pointer that is read from AND written through + * below. Validate both against the allocations before + * converting them to pointers. */ - acb = acbbotov(adw, adw->responseq->areq_ba); + areq_ba = adw->responseq->areq_ba; + if (areq_ba >= adw->num_acbs * sizeof(struct acb) || + (areq_ba % sizeof(struct acb)) != 0) { + kprintf("%s: bogus completion token 0x%x\n", + adw_name(adw), areq_ba); + adw_reset_bus(adw); + break; + } + acb = acbbotov(adw, areq_ba); /* * The least significant four bits of the next_ba @@ -1285,8 +1308,15 @@ * advance through the list. */ free_carrier = adw->responseq; - adw->responseq = - carrierbotov(adw, free_carrier->next_ba & ADW_NEXT_BA_MASK); + next_ba = free_carrier->next_ba & ADW_NEXT_BA_MASK; + if (next_ba >= (adw->max_acbs + ADW_NUM_CARRIER_QUEUES + 1) + * sizeof(struct adw_carrier)) { + kprintf("%s: bogus carrier link 0x%x\n", + adw_name(adw), next_ba); + adw_reset_bus(adw); + break; + } + adw->responseq = carrierbotov(adw, next_ba); free_carrier->next_ba = adw->free_carriers->carr_offset; adw->free_carriers = free_carrier; @@ -1401,6 +1431,18 @@ case QHSTA_M_INVALID_DEVICE: ccb->ccb_h.status = CAM_PATH_INVALID; break; + case QHSTA_M_DIRECTION_ERR: + case QHSTA_M_DIRECTION_ERR_HUNG: + /* Data phase mismatch (often target misbehavior). */ + ccb->ccb_h.status = CAM_DATA_RUN_ERR; + break; + case QHSTA_M_FROZEN_TIDQ: + /* TID queue frozen - requeue, don't panic. */ + ccb->ccb_h.status = CAM_REQUEUE_REQ; + break; + case QHSTA_M_SGBACKUP_ERROR: + ccb->ccb_h.status = CAM_DATA_RUN_ERR; + break; case QHSTA_M_NO_AUTO_REQ_SENSE: /* * User didn't request sense, but we got a