asd_get_response_tasklet() and asd_get_tmf_resp_tasklet() look an Empty SCB up by the index_escb field of the done list status block and then use it as one: escb = asd_tc_index_find(&asd_ha->seq, (int)le16_to_cpu(resp_sb->index_escb)); ... edb = asd_ha->seq.edb_arr[edb_id + escb->edb_index]; ... asd_invalidate_edb(escb, edb_id); Nothing checks that what came back is an Empty SCB. There is one index space for every aSCB the driver allocates -- asd_ascb_alloc() is the only caller of asd_tc_index_get() -- so an in-range index can just as well name a live task, a control phy SCB or an internal command. That matters because struct scb is a union, and asd_invalidate_edb() reads its escb member without looking at the opcode: struct empty_scb *escb = &ascb->scb->escb; struct sg_el *eb = &escb->eb[edb_id]; ... eb->flags |= ELEMENT_NOT_VALID; escb->num_valid--; On an SCB that is not an Empty SCB those two stores land in whichever union member it really is -- for an initiate_ssp_task that region holds the SSP frame header and the scatter-gather elements of a command that is still in flight. Check the opcode, the way asd_dl_tasklet_handler() already does when it decides whether a done list entry belongs to an Empty SCB: } else if (ascb->scb->header.opcode == EMPTY_SCB) { asd_get_response_tasklet() reports SAS_INTERRUPTED, as it does for the other two ways it can fail; asd_get_tmf_resp_tasklet() returns TMF_RESP_FUNC_FAILED, which is what res already holds. This is not a claim of a reachable type confusion. The index originates with the driver and the sequencer echoes it back, and whether it can echo a different one is not something the tree answers. The check is cheap and the idiom is already in the driver. Fixes: 2908d778ab3e ("[SCSI] aic94xx: new driver") Cc: stable@vger.kernel.org Signed-off-by: Yehyeong Lee --- drivers/scsi/aic94xx/aic94xx_task.c | 8 ++++++++ drivers/scsi/aic94xx/aic94xx_tmf.c | 7 +++++++ 2 files changed, 15 insertions(+) diff --git a/drivers/scsi/aic94xx/aic94xx_task.c b/drivers/scsi/aic94xx/aic94xx_task.c index bb5094ca5de15..a8131407b2d08 100644 --- a/drivers/scsi/aic94xx/aic94xx_task.c +++ b/drivers/scsi/aic94xx/aic94xx_task.c @@ -170,6 +170,14 @@ static void asd_get_response_tasklet(struct asd_ascb *ascb, return; } + if (escb->scb->header.opcode != EMPTY_SCB) { + ASD_DPRINTK("index_escb %d is not an ESCB, opcode is 0x%x!\n", + le16_to_cpu(resp_sb->index_escb), + escb->scb->header.opcode); + ts->stat = SAS_INTERRUPTED; + return; + } + if (edb_id < 0 || edb_id >= ASD_EDBS_PER_SCB) { ASD_DPRINTK("edb_id is %d! resp_sb->flags is 0x%x\n", edb_id, resp_sb->flags); diff --git a/drivers/scsi/aic94xx/aic94xx_tmf.c b/drivers/scsi/aic94xx/aic94xx_tmf.c index 1ee6009d83051..e8205e64b870f 100644 --- a/drivers/scsi/aic94xx/aic94xx_tmf.c +++ b/drivers/scsi/aic94xx/aic94xx_tmf.c @@ -282,6 +282,13 @@ static int asd_get_tmf_resp_tasklet(struct asd_ascb *ascb, return res; } + if (escb->scb->header.opcode != EMPTY_SCB) { + ASD_DPRINTK("index_escb %d is not an ESCB, opcode is 0x%x!\n", + le16_to_cpu(resp_sb->index_escb), + escb->scb->header.opcode); + return res; + } + if (edb_id < 0 || edb_id >= ASD_EDBS_PER_SCB) { ASD_DPRINTK("edb_id is %d! resp_sb->flags is 0x%x\n", edb_id, resp_sb->flags); -- 2.43.0