[PATCH v2 5/5] scsi: aic94xx: check that an ESCB lookup returned an ESCB
From: Yehyeong Lee
Date: Thu Oct 15 2026 - 04:23:45 EST
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@xxxxxxxxxxxxxxx
Signed-off-by: Yehyeong Lee <yhlee@xxxxxxxxxxxxxxxxxx>
---
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