On adapter types E and F the adapter reports a completed command by writing its slot number into the completion queue, and the driver turns that number straight into an array subscript: cmdSMID = acb->pCompletionQ[doneq_index].cmdSMID; ccb = acb->pccb_pool[cmdSMID]; cmdSMID is a uint16_t taken from DMA memory the adapter writes, so it can be anything up to 65535, while acb->pccb_pool[] has ARCMSR_MAX_FREECCB_NUM (1024) entries. Nothing compares the two. arcmsr_drain_donequeue() cannot stand in for the missing check: it starts by reading pCCB->acb and pCCB->startdone, so by the time it can notice anything the bad pointer has already been loaded and dereferenced. arcmsr_hbaF_postqueue_isr() rejects the single value 0xffff because that is its queue-empty sentinel, which leaves 1024..65534 accepted. Reject an index the driver never handed out, in the two interrupt handlers and in the polling path. Bound against ARCMSR_MAX_FREECCB_NUM rather than acb->maxFreeCCB, which is what actually guards the array: maxFreeCCB is derived from the host_can_queue module parameter and gets 64 added to it when it is below ARCMSR_MAX_FREECCB_NUM, so it can be larger than the array and would let a slot number past the end through. In the interrupt handlers, release the entry and step the queue index before continuing, the same way the accepted path does. Stopping at the bad entry instead would leave the consumer index behind the producer, so the adapter would re-raise the interrupt on an entry the driver has already refused and the completions queued behind it would never be reaped. Measured with a KASAN build and a crafted completion queue. struct AdapterControlBlock is 17504 bytes with pccb_pool at offset 496, so a reported slot of 60000 reads 480496 bytes in, past the end of the controller block entirely: BUG: KASAN: use-after-free in arcmsr_hbaE_postqueue_isr+0x242/0x2d0 [arcmsr] Read of size 8 at addr ffff88800bf354f0 by task init/1 and the pointer it picked up then faults in arcmsr_drain_donequeue: RIP: 0010:arcmsr_drain_donequeue+0x27/0x360 [arcmsr] Kernel panic - not syncing: Attempted to kill init! exitcode=0x0000000b A smaller out-of-range slot such as 2000 still lands inside the controller block (offset 16496), so there is nothing for KASAN to report; the driver simply reads an unrelated field of its own AdapterControlBlock as a command pointer and faults on it in the same place. With host_can_queue raised so that maxFreeCCB becomes 1087, a reported slot of 1050 faults the same way while comparing below maxFreeCCB. A queue holding a rejected entry followed by a good one drains to the producer index and the good completion is reaped; valid slot numbers are accepted unchanged. Fixes: 235090241c55 ("scsi: arcmsr: Add code for ACB_ADAPTER_TYPE_E") Cc: stable@vger.kernel.org Signed-off-by: Yehyeong Lee --- drivers/scsi/arcmsr/arcmsr_hba.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/drivers/scsi/arcmsr/arcmsr_hba.c b/drivers/scsi/arcmsr/arcmsr_hba.c index 255f1d898079f..9519715883b5a 100644 --- a/drivers/scsi/arcmsr/arcmsr_hba.c +++ b/drivers/scsi/arcmsr/arcmsr_hba.c @@ -2677,6 +2677,14 @@ static void arcmsr_hbaE_postqueue_isr(struct AdapterControlBlock *acb) pmu = acb->pmuE; while ((readl(&pmu->reply_post_producer_index) & 0xFFFF) != doneq_index) { cmdSMID = acb->pCompletionQ[doneq_index].cmdSMID; + if (cmdSMID >= ARCMSR_MAX_FREECCB_NUM) { + pr_err_ratelimited("arcmsr%d: bad cmdSMID %u from iop\n", + acb->host->host_no, cmdSMID); + doneq_index++; + if (doneq_index >= acb->completionQ_entry) + doneq_index = 0; + continue; + } ccb = acb->pccb_pool[cmdSMID]; error = (acb->pCompletionQ[doneq_index].cmdFlag & ARCMSR_CCBREPLY_FLAG_ERROR_MODE1) ? true : false; @@ -2706,6 +2714,15 @@ static void arcmsr_hbaF_postqueue_isr(struct AdapterControlBlock *acb) cmdSMID = acb->pCompletionQ[doneq_index].cmdSMID; if (cmdSMID == 0xffff) break; + if (cmdSMID >= ARCMSR_MAX_FREECCB_NUM) { + pr_err_ratelimited("arcmsr%d: bad cmdSMID %u from iop\n", + acb->host->host_no, cmdSMID); + acb->pCompletionQ[doneq_index].cmdSMID = 0xffff; + doneq_index++; + if (doneq_index >= acb->completionQ_entry) + doneq_index = 0; + continue; + } ccb = acb->pccb_pool[cmdSMID]; error = (acb->pCompletionQ[doneq_index].cmdFlag & ARCMSR_CCBREPLY_FLAG_ERROR_MODE1) ? true : false; @@ -3880,6 +3897,11 @@ static int arcmsr_hbaE_polling_ccbdone(struct AdapterControlBlock *acb, doneq_index = 0; acb->doneq_index = doneq_index; spin_unlock_irqrestore(&acb->doneq_lock, flags); + if (cmdSMID >= ARCMSR_MAX_FREECCB_NUM) { + pr_err_ratelimited("arcmsr%d: bad cmdSMID %u from iop\n", + acb->host->host_no, cmdSMID); + continue; + } pCCB = acb->pccb_pool[cmdSMID]; poll_ccb_done |= (pCCB == poll_ccb) ? 1 : 0; /* check if command done with no error*/ -- 2.43.0