From: Jia Jia <physicalmtea@gmail.com>
To: "Martin K . Petersen" <mkp@kernel.org>
Cc: Jan Engelhardt <jengelh@inai.de>, Hannes Reinecke <hare@suse.de>,
Paolo Bonzini <pbonzini@redhat.com>,
Akinobu Mita <akinobu.mita@gmail.com>,
James Bottomley <James.Bottomley@suse.de>,
linux-scsi@vger.kernel.org, target-devel@vger.kernel.org,
linux-kernel@vger.kernel.org, Jia Jia <physicalmtea@gmail.com>
Subject: [PATCH 4/8] scsi: target: copy a DIF insert tuple across prot sgs
Date: Tue, 6 Oct 2026 17:33:34 +0800 [thread overview]
Message-ID: <20261006093338.27342-5-physicalmtea@gmail.com> (raw)
In-Reply-To: <20261006093338.27342-1-physicalmtea@gmail.com>
sbc_dif_generate() walks each protection sg and stores an 8 byte
t10_pi_tuple while j < sg->length. The store is not limited to the
bytes that remain in that sg.
vhost-scsi maps a protection buffer with iov_iter_get_pages2() and
keeps each sg inside one page. Eight PI bytes that start at page
offset 4092 become two sg entries, 4 bytes at offset 4092 and 4 bytes
at offset 0. The first entry still takes the loop. For DIF Type 1
the ref_tag store is 4 bytes at paddr + 4, which is the next physical
page.
Software INSERT reaches this function when the fabric does not
advertise DIN_INSERT or DOUT_INSERT. vhost-scsi advertises only
DIN_PASS and DOUT_PASS. With fabric_prot_type 1, a READ is
TARGET_PROT_DIN_INSERT and the generated tuple is written into the
guest PI buffer.
Copy the 8 byte tuple across protection sg entries. Four bytes at the
end of one entry and four at the start of the next stay one tuple. A
tail with no following entry is left unwritten. The walk maps each
page with kmap_local_page().
KASAN reports:
BUG: KASAN: use-after-free in sbc_dif_generate+0x20c/0x640 [target_core_mod]
Write of size 4
sbc_dif_generate
target_complete_ok_work
process_one_work
worker_thread
kthread
ret_from_fork
ret_from_fork_asm
Fixes: 66a3d5bc47d2 ("target/sbc: Add sbc_dif_generate software emulation")
Signed-off-by: Jia Jia <physicalmtea@gmail.com>
---
drivers/target/target_core_sbc.c | 181 ++++++++++++++++++++++---------
1 file changed, 132 insertions(+), 49 deletions(-)
diff --git a/drivers/target/target_core_sbc.c b/drivers/target/target_core_sbc.c
index adef903652ac..12275ebad95c 100644
--- a/drivers/target/target_core_sbc.c
+++ b/drivers/target/target_core_sbc.c
@@ -1250,72 +1250,155 @@ sbc_execute_unmap(struct se_cmd *cmd)
return ret;
}
+/*
+ * Copy one 8 byte PI tuple to or from the prot sg. The data page sits
+ * above the prot map, so a prot sg change drops and restores it. A
+ * short tail releases both maps and returns false.
+ */
+static bool
+sbc_dif_prot_copy(struct scatterlist **psgp, void **paddrp,
+ unsigned int *poffp, struct scatterlist **dsgp,
+ void **daddrp, void *buf, bool to_prot)
+{
+ struct scatterlist *psg = *psgp;
+ struct scatterlist *dsg = *dsgp;
+ void *paddr = *paddrp;
+ void *daddr = *daddrp;
+ unsigned int poff = *poffp;
+ unsigned int len = sizeof(struct t10_pi_tuple);
+ u8 *p = buf;
+
+ while (len) {
+ unsigned int take;
+
+ if (!psg || poff >= psg->length) {
+ if (daddr)
+ kunmap_local(daddr - dsg->offset);
+ if (paddr)
+ kunmap_local(paddr - psg->offset);
+ daddr = NULL;
+ paddr = NULL;
+ psg = psg ? sg_next(psg) : NULL;
+ if (!psg) {
+ *psgp = NULL;
+ *paddrp = NULL;
+ *daddrp = NULL;
+ return false;
+ }
+ poff = 0;
+ if (!psg->length)
+ continue;
+ paddr = kmap_local_page(sg_page(psg)) + psg->offset;
+ daddr = kmap_local_page(sg_page(dsg)) + dsg->offset;
+ }
+
+ take = min_t(unsigned int, len, psg->length - poff);
+ if (to_prot)
+ memcpy(paddr + poff, p, take);
+ else
+ memcpy(p, paddr + poff, take);
+ p += take;
+ poff += take;
+ len -= take;
+ }
+
+ *psgp = psg;
+ *paddrp = paddr;
+ *poffp = poff;
+ *dsgp = dsg;
+ *daddrp = daddr;
+ return true;
+}
+
void
sbc_dif_generate(struct se_cmd *cmd)
{
struct se_device *dev = cmd->se_dev;
- struct t10_pi_tuple *sdt;
- struct scatterlist *dsg = cmd->t_data_sg, *psg;
+ struct scatterlist *dsg = cmd->t_data_sg;
+ struct scatterlist *psg = cmd->t_prot_sg;
sector_t sector = cmd->t_task_lba;
void *daddr, *paddr;
- int i, j, offset = 0;
+ unsigned int poff = 0;
+ int offset = 0;
unsigned int block_size = dev->dev_attrib.block_size;
- for_each_sg(cmd->t_prot_sg, psg, cmd->t_prot_nents, i) {
- paddr = kmap_atomic(sg_page(psg)) + psg->offset;
- daddr = kmap_atomic(sg_page(dsg)) + dsg->offset;
+ if (!psg || !dsg)
+ return;
- for (j = 0; j < psg->length;
- j += sizeof(*sdt)) {
- __u16 crc;
- unsigned int avail;
+ paddr = kmap_local_page(sg_page(psg)) + psg->offset;
+ daddr = kmap_local_page(sg_page(dsg)) + dsg->offset;
- if (offset >= dsg->length) {
- offset -= dsg->length;
- kunmap_atomic(daddr - dsg->offset);
- dsg = sg_next(dsg);
- if (!dsg) {
- kunmap_atomic(paddr - psg->offset);
- return;
- }
- daddr = kmap_atomic(sg_page(dsg)) + dsg->offset;
- }
+ for (;;) {
+ struct t10_pi_tuple sdt;
+ struct scatterlist *mark_psg;
+ unsigned int mark_off;
+ __u16 crc;
+ unsigned int avail;
- sdt = paddr + j;
- avail = min(block_size, dsg->length - offset);
- crc = crc_t10dif(daddr + offset, avail);
- if (avail < block_size) {
- kunmap_atomic(daddr - dsg->offset);
- dsg = sg_next(dsg);
- if (!dsg) {
- kunmap_atomic(paddr - psg->offset);
- return;
- }
- daddr = kmap_atomic(sg_page(dsg)) + dsg->offset;
- offset = block_size - avail;
- crc = crc_t10dif_update(crc, daddr, offset);
- } else {
- offset += block_size;
- }
+ if (poff >= psg->length && !sg_next(psg))
+ break;
- sdt->guard_tag = cpu_to_be16(crc);
- if (cmd->prot_type == TARGET_DIF_TYPE1_PROT)
- sdt->ref_tag = cpu_to_be32(sector & 0xffffffff);
- sdt->app_tag = 0;
+ mark_psg = psg;
+ mark_off = poff;
+ if (!sbc_dif_prot_copy(&psg, &paddr, &poff, &dsg, &daddr,
+ &sdt, false))
+ return;
+
+ if (offset >= dsg->length) {
+ offset -= dsg->length;
+ kunmap_local(daddr - dsg->offset);
+ dsg = sg_next(dsg);
+ if (!dsg) {
+ kunmap_local(paddr - psg->offset);
+ return;
+ }
+ daddr = kmap_local_page(sg_page(dsg)) + dsg->offset;
+ }
- pr_debug("DIF %s INSERT sector: %llu guard_tag: 0x%04x"
- " app_tag: 0x%04x ref_tag: %u\n",
- (cmd->data_direction == DMA_TO_DEVICE) ?
- "WRITE" : "READ", (unsigned long long)sector,
- sdt->guard_tag, sdt->app_tag,
- be32_to_cpu(sdt->ref_tag));
+ avail = min(block_size, dsg->length - offset);
+ crc = crc_t10dif(daddr + offset, avail);
+ if (avail < block_size) {
+ kunmap_local(daddr - dsg->offset);
+ dsg = sg_next(dsg);
+ if (!dsg) {
+ kunmap_local(paddr - psg->offset);
+ return;
+ }
+ daddr = kmap_local_page(sg_page(dsg)) + dsg->offset;
+ offset = block_size - avail;
+ crc = crc_t10dif_update(crc, daddr, offset);
+ } else {
+ offset += block_size;
+ }
- sector++;
+ sdt.guard_tag = cpu_to_be16(crc);
+ if (cmd->prot_type == TARGET_DIF_TYPE1_PROT)
+ sdt.ref_tag = cpu_to_be32(sector & 0xffffffff);
+ sdt.app_tag = 0;
+
+ pr_debug("DIF %s INSERT sector: %llu guard_tag: 0x%04x app_tag: 0x%04x ref_tag: %u\n",
+ (cmd->data_direction == DMA_TO_DEVICE) ?
+ "WRITE" : "READ", (unsigned long long)sector,
+ sdt.guard_tag, sdt.app_tag,
+ be32_to_cpu(sdt.ref_tag));
+
+ if (psg != mark_psg) {
+ kunmap_local(daddr - dsg->offset);
+ kunmap_local(paddr - psg->offset);
+ psg = mark_psg;
+ paddr = kmap_local_page(sg_page(psg)) + psg->offset;
+ daddr = kmap_local_page(sg_page(dsg)) + dsg->offset;
}
+ poff = mark_off;
+ if (!sbc_dif_prot_copy(&psg, &paddr, &poff, &dsg, &daddr,
+ &sdt, true))
+ return;
- kunmap_atomic(daddr - dsg->offset);
- kunmap_atomic(paddr - psg->offset);
+ sector++;
}
+
+ kunmap_local(daddr - dsg->offset);
+ kunmap_local(paddr - psg->offset);
}
static sense_reason_t
--
2.34.1
next prev parent reply other threads:[~2026-10-06 9:34 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-06 9:33 [PATCH 0/8] scsi: target: keep command bytes inside the sg Jia Jia
2026-10-06 9:33 ` [PATCH 1/8] scsi: target: take COMPARE AND WRITE data from the write half Jia Jia
2026-10-06 9:33 ` [PATCH 2/8] scsi: target: keep REPORT REFERRALS stores inside the buffer Jia Jia
2026-10-06 9:33 ` [PATCH 3/8] scsi: target: reject a short SET TARGET PORT GROUPS list Jia Jia
2026-10-06 9:33 ` Jia Jia [this message]
2026-10-06 9:33 ` [PATCH 5/8] scsi: target: copy a DIF verify tuple across prot sgs Jia Jia
2026-10-06 9:33 ` [PATCH 6/8] scsi: target: limit DIF block CRC to each data sg Jia Jia
2026-10-06 9:33 ` [PATCH 7/8] scsi: target: keep pscsi mode bytes inside the data sgs Jia Jia
2026-10-06 9:33 ` [PATCH 8/8] scsi: target: skip an escaped DIF block inside the data sg Jia Jia
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261006093338.27342-5-physicalmtea@gmail.com \
--to=physicalmtea@gmail.com \
--cc=James.Bottomley@suse.de \
--cc=akinobu.mita@gmail.com \
--cc=hare@suse.de \
--cc=jengelh@inai.de \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-scsi@vger.kernel.org \
--cc=mkp@kernel.org \
--cc=pbonzini@redhat.com \
--cc=target-devel@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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.