All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 0/5] scsi: aic94xx: validate the indices the sequencer supplies
@ 2026-10-15  8:19 Yehyeong Lee
  2026-10-15  8:19 ` [PATCH v2 1/5] scsi: aic94xx: bail out of escb_tasklet_complete() on a bad EDB index Yehyeong Lee
                   ` (4 more replies)
  0 siblings, 5 replies; 7+ messages in thread
From: Yehyeong Lee @ 2026-10-15  8:19 UTC (permalink / raw)
  To: Martin K . Petersen, James E . J . Bottomley
  Cc: linux-scsi, linux-kernel, stable, yhlee

This supersedes

  [PATCH 0/3] scsi: aic94xx: validate the sequencer-supplied EDB index
  <20261015072612.4144043-1-yhlee@isslab.korea.ac.kr>

which should be dropped: its patch 2 was wrong in a way worth stating
plainly.

aic94xx takes two numbers out of the done list the sequencer writes and
uses both without looking at them.  One is the empty data buffer index,
which v1 bounded in three places.  The other is the transaction context
index, which v1 did not touch, and which turns out to be the same class of
problem one level up.

 1  escb_tasklet_complete() already noticed a bad EDB index and printed
    about it, then fell through and used it anyway.  Add the missing
    return.  Unchanged from v1.
 2  asd_get_response_tasklet() never looked.  Bound it -- and, new in v2,
    report SAS_INTERRUPTED on the way out.
 3  asd_get_tmf_resp_tasklet() never looked either.  Unchanged from v1.
 4  asd_tc_index_find() indexes tc_index_array[] with the number the
    sequencer echoes back, in all three of its callers, without a bound.
    New in v2.
 5  the pointer that comes back is used as an Empty SCB without checking
    that it is one, and struct scb is a union.  New in v2.

On patch 2, the correction.  v1's bail returned without touching ts->stat,
and asd_task_tasklet_complete() has already set ts->resp to
SAS_TASK_COMPLETE and ts->stat to SAS_PROTO_RESPONSE by then.  That status
is a promise that this function will fill in the real one.  For SSP,
sas_end_task() starts from DID_OK and SAS_SAM_STAT_GOOD and its
SAS_PROTO_RESPONSE arm only prints a notice, so the command completes as a
success.  For STP, sas_ata_task_done() takes SAS_PROTO_RESPONSE to mean the
ending FIS holds the status and copies ATA_RESP_FIS_SIZE out of a ts->buf
that was never written.  So a hardening patch turned a bad index into a
silently successful command, which is worse than what it was fixing.  v2
reports SAS_INTERRUPTED instead, which is what this driver already uses for
a response that arrived and could not be used, and it does the same for the
lookup failure above it, which had the same hole before this series
existed.

Patches 4 and 5 finish the class rather than leaving half of it.  The index
in question is one the driver itself allocated and handed to the sequencer
in scb->header.index, so it comes back echoed; asd_tc_index_find() reads
tc_index_array[] with it, the array has hw_prof.max_scbs entries, and
max_scbs cannot exceed 5957 while the field is a __le16.  All three callers
of that helper already handle a NULL return, so the bound needs no caller
changes.  Patch 5 adds the opcode check that asd_dl_tasklet_handler()
already does.

I have not reproduced any of this on a controller.  Injecting the three bad
values through a KUnit test on a KASAN kernel does reach
seq->edb_arr[-1] and tc_index_array[] past its end, and does leave a
non-Empty SCB modified, which shows the arithmetic and the missing checks
are real; it does not show that the sequencer emits those values, and
nothing in the tree answers that, since it is firmware loaded with
request_firmware() and there is no Documentation/ file or MAINTAINERS entry
for this driver.  The commit messages are written on that basis and claim
no reachable memory error.

Patch 1 keeps Cc: stable, as in v1.  Patches 4 and 5 carry it too: unlike
the EDB bound, those two are an unchecked array index and a union
reinterpretation that the driver never guarded at all.  Patches 2 and 3
still do not, and I would rather a maintainer decide.

Yehyeong Lee (5):
  scsi: aic94xx: bail out of escb_tasklet_complete() on a bad EDB index
  scsi: aic94xx: validate the EDB index in asd_get_response_tasklet()
  scsi: aic94xx: validate the EDB index in asd_get_tmf_resp_tasklet()
  scsi: aic94xx: bound the transaction context index from the sequencer
  scsi: aic94xx: check that an ESCB lookup returned an ESCB

 drivers/scsi/aic94xx/aic94xx_hwi.h  |  2 ++
 drivers/scsi/aic94xx/aic94xx_scb.c  |  1 +
 drivers/scsi/aic94xx/aic94xx_task.c | 16 ++++++++++++++++
 drivers/scsi/aic94xx/aic94xx_tmf.c  | 13 +++++++++++++
 4 files changed, 32 insertions(+)


base-commit: 2c3418fffa9d037b2038a6db48be63f9e2291806
-- 
2.43.0


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH v2 1/5] scsi: aic94xx: bail out of escb_tasklet_complete() on a bad EDB index
  2026-10-15  8:19 [PATCH v2 0/5] scsi: aic94xx: validate the indices the sequencer supplies Yehyeong Lee
@ 2026-10-15  8:19 ` Yehyeong Lee
  2026-10-15  8:19 ` [PATCH v2 2/5] scsi: aic94xx: validate the EDB index in asd_get_response_tasklet() Yehyeong Lee
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 7+ messages in thread
From: Yehyeong Lee @ 2026-10-15  8:19 UTC (permalink / raw)
  To: Martin K . Petersen, James E . J . Bottomley
  Cc: linux-scsi, linux-kernel, stable, yhlee

escb_tasklet_complete() derives the empty data buffer index from the done
list opcode the sequencer wrote:

	int edb = (dl->opcode & DL_PHY_MASK) - 1; /* [0xc1,0xc7] -> [0,6] */

DL_PHY_MASK is 7, so the subtraction yields -1 through 6, and the
function already says that anything outside [0,6] is wrong:

	if (edb > 6 || edb < 0) {
		ASD_DPRINTK("edb is 0x%x! dl->opcode is 0x%x\n", ...);
		...
	}

The block reports the problem and then falls through to let it happen.
edb is used twice afterwards: asd_bytes_dmaed_tasklet() adds
ascb->edb_index to it to index seq->edb_arr[], and the out: label at the
end of the function hands the same value to asd_invalidate_edb(), which
indexes both escb->eb[] and seq->edb_arr[]. asd_assign_edbs2escbs() gives
the first ESCB edb_index 0, so for that one edb == -1 reads
seq->edb_arr[-1], one pointer before the array, and dereferences whatever
it finds.

Finish the check the way it reads: return instead of falling through.

Whether the sequencer can really report an opcode that makes this zero is
not something the tree answers. It is firmware loaded with
request_firmware(), the done list opcode is described only by the comment
above, and there is no Documentation/ file and no MAINTAINERS entry for
this driver. This is not a claim of a reachable out-of-bounds access,
only that the driver goes on to index an array with a value it has just
decided is invalid. The intent to bound what the sequencer supplies is
already in the same file, in asd_bytes_dmaed_tasklet():

	size = min(size, (u16) sizeof(phy->frame_rcvd));

which clamps a device-supplied length a few lines after indexing
seq->edb_arr[] with a device-supplied index and no bound.

One consequence worth naming: an ESCB dropped this way is not invalidated
and so is not reposted to the sequencer, because the repost happens
inside asd_invalidate_edb(). That takes one empty buffer out of
circulation per malformed opcode. It is the lesser of the two, and
recovering the ESCB instead would mean choosing some other buffer to
invalidate, which this index cannot tell us.

Fixes: 2908d778ab3e ("[SCSI] aic94xx: new driver")
Cc: stable@vger.kernel.org
Signed-off-by: Yehyeong Lee <yhlee@isslab.korea.ac.kr>
---
 drivers/scsi/aic94xx/aic94xx_scb.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/scsi/aic94xx/aic94xx_scb.c b/drivers/scsi/aic94xx/aic94xx_scb.c
index 08c8dad9ad621..d050e5325b943 100644
--- a/drivers/scsi/aic94xx/aic94xx_scb.c
+++ b/drivers/scsi/aic94xx/aic94xx_scb.c
@@ -421,6 +421,7 @@ static void escb_tasklet_complete(struct asd_ascb *ascb,
 			    le64_to_cpu(ascb->scb->header.next_scb),
 			    le16_to_cpu(ascb->scb->header.index),
 			    ascb->scb->header.opcode);
+		return;
 	}
 
 	/* Catch these before we mask off the sb_opcode bits */
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH v2 2/5] scsi: aic94xx: validate the EDB index in asd_get_response_tasklet()
  2026-10-15  8:19 [PATCH v2 0/5] scsi: aic94xx: validate the indices the sequencer supplies Yehyeong Lee
  2026-10-15  8:19 ` [PATCH v2 1/5] scsi: aic94xx: bail out of escb_tasklet_complete() on a bad EDB index Yehyeong Lee
@ 2026-10-15  8:19 ` Yehyeong Lee
  2026-10-15  8:19 ` [PATCH v2 3/5] scsi: aic94xx: validate the EDB index in asd_get_tmf_resp_tasklet() Yehyeong Lee
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 7+ messages in thread
From: Yehyeong Lee @ 2026-10-15  8:19 UTC (permalink / raw)
  To: Martin K . Petersen, James E . J . Bottomley
  Cc: linux-scsi, linux-kernel, stable, yhlee

asd_get_response_tasklet() takes the empty data buffer index out of the
done list status block the sequencer wrote, with the same arithmetic
escb_tasklet_complete() uses on its own copy:

	int  edb_id = ((resp_sb->flags & 0x70) >> 4)-1;

The extracted field is three bits, so the subtraction yields -1 through
6, and the function indexes seq->edb_arr[] with it without looking:

	edb = asd_ha->seq.edb_arr[edb_id + escb->edb_index];
	r = edb->vaddr;

asd_assign_edbs2escbs() gives the first ESCB edb_index 0, so for that one
edb_id == -1 reads seq->edb_arr[-1], one pointer before the kmalloc'd
array, and the next line dereferences it. asd_invalidate_edb() at the end
of the function would index escb->eb[] and seq->edb_arr[] with the same
value.

Reject an index outside [0, ASD_EDBS_PER_SCB) and return, and set
ts->stat on the way out, because returning without touching it reports
the command as a success.

asd_task_tasklet_complete() sets ts->resp and ts->stat before it calls
this function:

	case TC_SSP_RESP:
	case TC_ATA_RESP:
		ts->resp = SAS_TASK_COMPLETE;
		ts->stat = SAS_PROTO_RESPONSE;
		asd_get_response_tasklet(ascb, dl);

SAS_PROTO_RESPONSE is a promise that this function will go on to put the
real status there, from the response IU for SSP or the ending FIS for
STP. Returning early breaks that promise and leaves the promise in place.
For SSP, sas_end_task() starts from DID_OK and SAS_SAM_STAT_GOOD, and its
SAS_PROTO_RESPONSE case only prints a notice, so the command completes as
a success. For STP, sas_ata_task_done() takes SAS_PROTO_RESPONSE as "the
ending FIS has the status" and copies ATA_RESP_FIS_SIZE out of ts->buf,
which this function never filled, then derives err_mask from it.

So the bail reports SAS_INTERRUPTED, which is what this driver already
uses for a response that arrived but could not be used -- TI_BREAK,
TI_PROTO_ERR, TI_NAK, TI_ACK_NAK_TO, TF_SMP_XMIT_RCV_ERR and
TC_ATA_R_ERR_RECV all map to it. sas_end_task() turns it into
DID_NO_CONNECT, and sas_to_ata_err() into AC_ERR_ATA_BUS, so neither path
mistakes it for a completed command.

The lookup failure above it had the same problem and gets the same line.
Nothing changes on the path where the index is in range.

Whether the sequencer can really report zero in that field is not
something the tree answers. It is firmware loaded with
request_firmware(), status_block[] is an opaque four bytes in
aic94xx_sas.h whose layout is described only by a struct declared locally
in this function and again in aic94xx_tmf.c, and there is no
Documentation/ file or MAINTAINERS entry for this driver. This is not a
claim of a reachable out-of-bounds access. It makes the driver consistent
with itself: asd_bytes_dmaed_tasklet() in aic94xx_scb.c clamps a
device-supplied length a few lines after indexing seq->edb_arr[] with a
device-supplied index and no bound of its own.

As with the preceding patch, a response dropped here leaves its ESCB
uninvalidated and so not reposted, since the repost happens inside
asd_invalidate_edb(). The alternative is to index the array with a value
the driver cannot place.

Fixes: 2908d778ab3e ("[SCSI] aic94xx: new driver")
Signed-off-by: Yehyeong Lee <yhlee@isslab.korea.ac.kr>
---
 drivers/scsi/aic94xx/aic94xx_task.c | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/drivers/scsi/aic94xx/aic94xx_task.c b/drivers/scsi/aic94xx/aic94xx_task.c
index b26a468ddc981..bb5094ca5de15 100644
--- a/drivers/scsi/aic94xx/aic94xx_task.c
+++ b/drivers/scsi/aic94xx/aic94xx_task.c
@@ -166,6 +166,14 @@ static void asd_get_response_tasklet(struct asd_ascb *ascb,
 
 	if (!escb) {
 		ASD_DPRINTK("Uh-oh! No escb for this dl?!\n");
+		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);
+		ts->stat = SAS_INTERRUPTED;
 		return;
 	}
 
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH v2 3/5] scsi: aic94xx: validate the EDB index in asd_get_tmf_resp_tasklet()
  2026-10-15  8:19 [PATCH v2 0/5] scsi: aic94xx: validate the indices the sequencer supplies Yehyeong Lee
  2026-10-15  8:19 ` [PATCH v2 1/5] scsi: aic94xx: bail out of escb_tasklet_complete() on a bad EDB index Yehyeong Lee
  2026-10-15  8:19 ` [PATCH v2 2/5] scsi: aic94xx: validate the EDB index in asd_get_response_tasklet() Yehyeong Lee
@ 2026-10-15  8:19 ` Yehyeong Lee
  2026-10-15  8:19 ` [PATCH v2 4/5] scsi: aic94xx: bound the transaction context index from the sequencer Yehyeong Lee
  2026-10-15  8:19 ` [PATCH v2 5/5] scsi: aic94xx: check that an ESCB lookup returned an ESCB Yehyeong Lee
  4 siblings, 0 replies; 7+ messages in thread
From: Yehyeong Lee @ 2026-10-15  8:19 UTC (permalink / raw)
  To: Martin K . Petersen, James E . J . Bottomley
  Cc: linux-scsi, linux-kernel, stable, yhlee

asd_get_tmf_resp_tasklet() extracts the empty data buffer index from the
done list status block exactly as asd_get_response_tasklet() does:

	int  edb_id = ((resp_sb->flags & 0x70) >> 4)-1;

and indexes seq->edb_arr[] with it unchecked:

	edb = asd_ha->seq.edb_arr[edb_id + escb->edb_index];
	ascb->tag = *(__be16 *)(edb->vaddr+4);

The field is three bits, so the value runs from -1 to 6, and the first
ESCB has edb_index 0, which makes seq->edb_arr[-1] reachable arithmetic.
asd_invalidate_edb() at the end of the function uses the same value
again.

Apply the same check as the preceding patch and return res, which is
TMF_RESP_FUNC_FAILED at that point, the way the lookup failure above it
already does.

The caveats are the ones on that patch: the sequencer is firmware loaded
with request_firmware() and nothing in the tree describes what it may put
in this field, so this is not a claim of a reachable out-of-bounds
access, and a response dropped here leaves its ESCB uninvalidated and
unreposted.

Fixes: 2908d778ab3e ("[SCSI] aic94xx: new driver")
Signed-off-by: Yehyeong Lee <yhlee@isslab.korea.ac.kr>
---
 drivers/scsi/aic94xx/aic94xx_tmf.c | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/drivers/scsi/aic94xx/aic94xx_tmf.c b/drivers/scsi/aic94xx/aic94xx_tmf.c
index 28ac92b041fe3..1ee6009d83051 100644
--- a/drivers/scsi/aic94xx/aic94xx_tmf.c
+++ b/drivers/scsi/aic94xx/aic94xx_tmf.c
@@ -282,6 +282,12 @@ static int asd_get_tmf_resp_tasklet(struct asd_ascb *ascb,
 		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);
+		return res;
+	}
+
 	edb = asd_ha->seq.edb_arr[edb_id + escb->edb_index];
 	ascb->tag = *(__be16 *)(edb->vaddr+4);
 	fh = edb->vaddr + 16;
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH v2 4/5] scsi: aic94xx: bound the transaction context index from the sequencer
  2026-10-15  8:19 [PATCH v2 0/5] scsi: aic94xx: validate the indices the sequencer supplies Yehyeong Lee
                   ` (2 preceding siblings ...)
  2026-10-15  8:19 ` [PATCH v2 3/5] scsi: aic94xx: validate the EDB index in asd_get_tmf_resp_tasklet() Yehyeong Lee
@ 2026-10-15  8:19 ` Yehyeong Lee
  2026-10-15  8:19 ` [PATCH v2 5/5] scsi: aic94xx: check that an ESCB lookup returned an ESCB Yehyeong Lee
  4 siblings, 0 replies; 7+ messages in thread
From: Yehyeong Lee @ 2026-10-15  8:19 UTC (permalink / raw)
  To: Martin K . Petersen, James E . J . Bottomley
  Cc: linux-scsi, linux-kernel, stable, yhlee

Every aSCB the driver allocates takes a slot in one shared index array,
and the driver tells the sequencer which slot it used:

	/* asd_ascb_alloc() */
	ascb->tc_index = asd_tc_index_get(seq, ascb);
	...
	ascb->scb->header.index = cpu_to_le16((u16)ascb->tc_index);

The sequencer hands that number back in the done list, and the driver
looks the aSCB up again with it in three places: asd_dl_tasklet_handler()
uses dl->index, and asd_get_response_tasklet() and
asd_get_tmf_resp_tasklet() use the index_escb field of the status block.
All three go through

	static inline void *asd_tc_index_find(struct asd_seq_data *seq, int index)
	{
		return seq->tc_index_array[index];
	}

which does not look at index at all.

The array is kcalloc()ed with tc_index_bitmap_bits entries, which is
hw_prof.max_scbs, which is asd_get_cmdctx_size() divided by sizeof(struct
scb). The two values asd_get_cmdctx_size() can return are 32768 and
65536, and struct scb starts with an 11-byte scb_header, so max_scbs
cannot exceed 5957. Both numbers the sequencer supplies are __le16, so
either one can name a slot past the end of the array, and the pointer
read from there is then dereferenced.

Check the index where it is used. The three callers already handle a NULL
return -- "Uh-oh! No escb for this dl?!" in the two status block paths
and "BUG:sequencer:dl:no ascb?!" in the done list handler -- so none of
them needs to change.

This is not a claim of a reachable out-of-bounds access: the index
originates with the driver and comes back echoed, and whether the
sequencer can echo something else is not something the tree answers,
since it is firmware loaded with request_firmware() and there is no
Documentation/ file or MAINTAINERS entry for this driver. It is a bound
on a number that arrives from outside, in the one place all three users
share.

Fixes: 2908d778ab3e ("[SCSI] aic94xx: new driver")
Cc: stable@vger.kernel.org
Signed-off-by: Yehyeong Lee <yhlee@isslab.korea.ac.kr>
---
 drivers/scsi/aic94xx/aic94xx_hwi.h | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/drivers/scsi/aic94xx/aic94xx_hwi.h b/drivers/scsi/aic94xx/aic94xx_hwi.h
index 930e192b1cd41..841769a8130a9 100644
--- a/drivers/scsi/aic94xx/aic94xx_hwi.h
+++ b/drivers/scsi/aic94xx/aic94xx_hwi.h
@@ -306,6 +306,8 @@ static inline int asd_tc_index_get(struct asd_seq_data *seq, void *ptr)
  */
 static inline void *asd_tc_index_find(struct asd_seq_data *seq, int index)
 {
+	if (index < 0 || index >= seq->tc_index_bitmap_bits)
+		return NULL;
 	return seq->tc_index_array[index];
 }
 
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH v2 5/5] scsi: aic94xx: check that an ESCB lookup returned an ESCB
  2026-10-15  8:19 [PATCH v2 0/5] scsi: aic94xx: validate the indices the sequencer supplies Yehyeong Lee
                   ` (3 preceding siblings ...)
  2026-10-15  8:19 ` [PATCH v2 4/5] scsi: aic94xx: bound the transaction context index from the sequencer Yehyeong Lee
@ 2026-10-15  8:19 ` Yehyeong Lee
  2026-10-15  8:55   ` sashiko-bot
  4 siblings, 1 reply; 7+ messages in thread
From: Yehyeong Lee @ 2026-10-15  8:19 UTC (permalink / raw)
  To: Martin K . Petersen, James E . J . Bottomley
  Cc: linux-scsi, linux-kernel, stable, yhlee

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 <yhlee@isslab.korea.ac.kr>
---
 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


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* Re: [PATCH v2 5/5] scsi: aic94xx: check that an ESCB lookup returned an ESCB
  2026-10-15  8:19 ` [PATCH v2 5/5] scsi: aic94xx: check that an ESCB lookup returned an ESCB Yehyeong Lee
@ 2026-10-15  8:55   ` sashiko-bot
  0 siblings, 0 replies; 7+ messages in thread
From: sashiko-bot @ 2026-10-15  8:55 UTC (permalink / raw)
  To: Yehyeong Lee; +Cc: linux-scsi

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] scsi: aic94xx: UAF race condition due to lockless escb access
--

commit 61c788d589a8ac9597298b6297ce45bf72a70621
Author: Yehyeong Lee <yhlee@isslab.korea.ac.kr>

scsi: aic94xx: check that an ESCB lookup returned an ESCB

This commit adds checks to verify that an ESCB lookup actually returns an
Empty SCB by inspecting the opcode.

> diff --git a/drivers/scsi/aic94xx/aic94xx_task.c b/drivers/scsi/aic94xx/aic94xx_task.c
> --- 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) {

[Severity: High]
Does checking the escb->scb->header.opcode outside of the tc_index_lock
introduce a race condition?

If the hardware returns a bogus index corresponding to a live task, could
that task concurrently time out and be freed while we are evaluating the
opcode? This appears to introduce a use-after-free read when dereferencing
escb->scb since the tc_index_lock was already dropped earlier in
asd_get_response_tasklet().

> +		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;
> +	}
> +

[ ... ]

> diff --git a/drivers/scsi/aic94xx/aic94xx_tmf.c b/drivers/scsi/aic94xx/aic94xx_tmf.c
> --- 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) {

[Severity: High]
Can this lockless access to escb->scb in asd_get_tmf_resp_tasklet() also
risk a use-after-free if the escb is concurrently freed?

> +		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;
> +	}

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261015081935.4168771-1-yhlee@isslab.korea.ac.kr?part=5

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2026-10-15  8:55 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-15  8:19 [PATCH v2 0/5] scsi: aic94xx: validate the indices the sequencer supplies Yehyeong Lee
2026-10-15  8:19 ` [PATCH v2 1/5] scsi: aic94xx: bail out of escb_tasklet_complete() on a bad EDB index Yehyeong Lee
2026-10-15  8:19 ` [PATCH v2 2/5] scsi: aic94xx: validate the EDB index in asd_get_response_tasklet() Yehyeong Lee
2026-10-15  8:19 ` [PATCH v2 3/5] scsi: aic94xx: validate the EDB index in asd_get_tmf_resp_tasklet() Yehyeong Lee
2026-10-15  8:19 ` [PATCH v2 4/5] scsi: aic94xx: bound the transaction context index from the sequencer Yehyeong Lee
2026-10-15  8:19 ` [PATCH v2 5/5] scsi: aic94xx: check that an ESCB lookup returned an ESCB Yehyeong Lee
2026-10-15  8:55   ` sashiko-bot

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.