DF-4307 / fix.diff
--- a/sys/dev/disk/advansys/adwcam.c +++ b/sys/dev/disk/advansys/adwcam.c @@ -1265,6 +1265,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 +1278,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 +1302,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; |