DF-4350 / fix.diff
--- a/sys/dev/disk/advansys/advansys.c +++ b/sys/dev/disk/advansys/advansys.c @@ -1036,16 +1036,31 @@ struct adv_q_done_info scsiq; u_int doneq_head; u_int done_qno; + int doneq_budget; doneq_head = adv_read_lram_16(adv, ADVV_DONE_Q_TAIL_W) & 0xFF; done_qno = adv_read_lram_8(adv, ADV_QNO_TO_QADDR(doneq_head) + ADV_SCSIQ_B_FWD); + doneq_budget = ADV_MAX_TOTAL_QNG + ADV_MAX_SG_LIST; while (done_qno != ADV_QLINK_END) { union ccb* ccb; struct adv_ccb_info *cinfo; u_int done_qaddr; u_int sg_queue_cnt; + /* + * DF-4350: loop termination depends entirely on the + * device-written FWD bytes in LRAM; a corrupt or hostile + * cyclic chain used to spin adv_intr() forever in + * interrupt context. A legitimate done queue can never + * be longer than every queue entry plus every SG link. + */ + if (doneq_budget-- <= 0) { + kprintf("%s: done queue walk exceeded %d entries, " + "stopping (corrupt LRAM?)\n", adv_name(adv), + ADV_MAX_TOTAL_QNG + ADV_MAX_SG_LIST); + break; + } done_qaddr = ADV_QNO_TO_QADDR(done_qno); /* Pull status from this request */ |