[PATCH 8/8] scsi: target: skip an escaped DIF block inside the data sg
From: Jia Jia
Date: Tue Oct 06 2026 - 05:39:38 EST
sbc_dif_verify() treats an application tag of 0xffff as one skipped
logical block and adds block_size to the data cursor. The next tuple
folds that cursor across a single sg entry. When the block spans more
entries, dsg_off remains past dsg->length and the following CRC reads a
full block from that offset.
A 512 byte block split 256 + 100 + the remainder, with the 100 byte
entry ending on a page and the first tuple escaped, reads the next
block from the next physical page.
Walk every data sg the skipped block covers. Those data pages are
mapped with kmap_local_page(), including the page the cursor moves to.
KASAN reports:
BUG: KASAN: use-after-free in crc_t10dif_update+0x91/0xf0
Read of size 1
crc_t10dif_update
sbc_dif_verify
target_execute_cmd
vhost_scsi_write_pending
transport_generic_new_cmd
__target_submit
target_queued_submit_work
process_one_work
worker_thread
kthread
ret_from_fork
ret_from_fork_asm
Fixes: 18213afbd8ce ("target: handle odd SG mapping for data transfer memory")
Signed-off-by: Jia Jia <physicalmtea@xxxxxxxxx>
---
drivers/target/target_core_sbc.c | 46 +++++++++++++++++++++++++++++++-
1 file changed, 45 insertions(+), 1 deletion(-)
diff --git a/drivers/target/target_core_sbc.c b/drivers/target/target_core_sbc.c
index 76dbc986e887..7c4f66c2607a 100644
--- a/drivers/target/target_core_sbc.c
+++ b/drivers/target/target_core_sbc.c
@@ -1521,6 +1521,46 @@ void sbc_dif_copy_prot(struct se_cmd *cmd, unsigned int sectors, bool read,
}
EXPORT_SYMBOL(sbc_dif_copy_prot);
+/*
+ * Drop @len bytes of the data sg. An escaped PI tuple still covers one
+ * logical block, which may cross more than one sg entry.
+ */
+static bool
+sbc_dif_consume(struct scatterlist **dsgp, void **daddrp, int *offp,
+ unsigned int len)
+{
+ struct scatterlist *dsg = *dsgp;
+ void *daddr = *daddrp;
+ int off = *offp;
+
+ while (len) {
+ unsigned int take;
+
+ if (off >= dsg->length) {
+ kunmap_local(daddr - dsg->offset);
+ dsg = sg_next(dsg);
+ if (!dsg)
+ return false;
+
+ daddr = kmap_local_page(sg_page(dsg)) + dsg->offset;
+ off = 0;
+ if (!dsg->length) {
+ kunmap_local(daddr - dsg->offset);
+ return false;
+ }
+ }
+
+ take = min_t(unsigned int, len, dsg->length - off);
+ off += take;
+ len -= take;
+ }
+
+ *dsgp = dsg;
+ *daddrp = daddr;
+ *offp = off;
+ return true;
+}
+
sense_reason_t
sbc_dif_verify(struct se_cmd *cmd, sector_t start, unsigned int sectors,
unsigned int ei_lba, struct scatterlist *psg, int psg_off)
@@ -1559,7 +1599,11 @@ sbc_dif_verify(struct se_cmd *cmd, sector_t start, unsigned int sectors,
sdt.app_tag, be32_to_cpu(sdt.ref_tag));
if (sdt.app_tag == T10_PI_APP_ESCAPE) {
- dsg_off += block_size;
+ if (!sbc_dif_consume(&dsg, &daddr, &dsg_off,
+ block_size)) {
+ kunmap_local(paddr - psg->offset);
+ return 0;
+ }
goto next;
}
--
2.34.1