DF-1377 / fix.diff
diff --git a/sys/dev/disk/buslogic/bt.c b/sys/dev/disk/buslogic/bt.c --- a/sys/dev/disk/buslogic/bt.c +++ b/sys/dev/disk/buslogic/bt.c @@ -103,8 +103,20 @@ static __inline struct bt_ccb * btccbptov(struct bt_softc *bt, u_int32_t ccb_addr) { - return (bt->bt_ccb_array + - ((struct bt_ccb*)(uintptr_t)ccb_addr - (struct bt_ccb*)(uintptr_t)bt->bt_ccb_physbase)); + struct bt_ccb *bccb; + int index; + + bccb = bt->bt_ccb_array + + ((struct bt_ccb*)(uintptr_t)ccb_addr - (struct bt_ccb*)(uintptr_t)bt->bt_ccb_physbase); + index = (int)(bccb - bt->bt_ccb_array); + /* ccb_addr is a u32 DMA-supplied by the HBA. Without this range + * check, a malicious/buggy HBA can make the computed bccb fall + * outside bt_ccb_array[0..max_ccbs); btdone() then OOB-reads + * bccb->ccb and btfreeccb() OOB-writes bccb->links.sle_next with + * a controlled pointer. */ + if (index < 0 || index >= bt->max_ccbs) + return (NULL); + return (bccb); } static __inline u_int32_t @@ -1362,8 +1374,18 @@ if ((intstat & IMB_LOADED) != 0) { while (bt->cur_inbox->comp_code != BMBI_FREE) { + struct bt_ccb *bccb = + btccbptov(bt, bt->cur_inbox->ccb_addr); + if (bccb == NULL) { + device_printf(bt->dev, "bccb out of " + "range, addr 0x%x, ignoring\n", + bt->cur_inbox->ccb_addr); + bt->cur_inbox->comp_code = BMBI_FREE; + btnextinbox(bt); + continue; + } btdone(bt, - btccbptov(bt, bt->cur_inbox->ccb_addr), + bccb, bt->cur_inbox->comp_code); bt->cur_inbox->comp_code = BMBI_FREE; btnextinbox(bt); |