--- /dev/null
+From 52a0a98549344ca20ad81a4176d68d28e3c05a5c Mon Sep 17 00:00:00 2001
+From: YunJe Shin <yjshin0438@gmail.com>
+Date: Wed, 28 Jan 2026 09:41:07 +0900
+Subject: nvmet-tcp: add bounds checks in nvmet_tcp_build_pdu_iovec
+
+From: YunJe Shin <yjshin0438@gmail.com>
+
+commit 52a0a98549344ca20ad81a4176d68d28e3c05a5c upstream.
+
+nvmet_tcp_build_pdu_iovec() could walk past cmd->req.sg when a PDU
+length or offset exceeds sg_cnt and then use bogus sg->length/offset
+values, leading to _copy_to_iter() GPF/KASAN. Guard sg_idx, remaining
+entries, and sg->length/offset before building the bvec.
+
+Fixes: 872d26a391da ("nvmet-tcp: add NVMe over TCP target driver")
+Signed-off-by: YunJe Shin <ioerts@kookmin.ac.kr>
+Reviewed-by: Sagi Grimberg <sagi@grimberg.me>
+Reviewed-by: Joonkyo Jung <joonkyoj@yonsei.ac.kr>
+Signed-off-by: Keith Busch <kbusch@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvme/target/tcp.c | 17 +++++++++++++++++
+ 1 file changed, 17 insertions(+)
+
+--- a/drivers/nvme/target/tcp.c
++++ b/drivers/nvme/target/tcp.c
+@@ -333,11 +333,14 @@ static void nvmet_tcp_free_cmd_buffers(s
+ cmd->req.sg = NULL;
+ }
+
++static void nvmet_tcp_fatal_error(struct nvmet_tcp_queue *queue);
++
+ static void nvmet_tcp_build_pdu_iovec(struct nvmet_tcp_cmd *cmd)
+ {
+ struct bio_vec *iov = cmd->iov;
+ struct scatterlist *sg;
+ u32 length, offset, sg_offset;
++ unsigned int sg_remaining;
+ int nr_pages;
+
+ length = cmd->pdu_len;
+@@ -345,9 +348,22 @@ static void nvmet_tcp_build_pdu_iovec(st
+ offset = cmd->rbytes_done;
+ cmd->sg_idx = offset / PAGE_SIZE;
+ sg_offset = offset % PAGE_SIZE;
++ if (!cmd->req.sg_cnt || cmd->sg_idx >= cmd->req.sg_cnt) {
++ nvmet_tcp_fatal_error(cmd->queue);
++ return;
++ }
+ sg = &cmd->req.sg[cmd->sg_idx];
++ sg_remaining = cmd->req.sg_cnt - cmd->sg_idx;
+
+ while (length) {
++ if (!sg_remaining) {
++ nvmet_tcp_fatal_error(cmd->queue);
++ return;
++ }
++ if (!sg->length || sg->length <= sg_offset) {
++ nvmet_tcp_fatal_error(cmd->queue);
++ return;
++ }
+ u32 iov_len = min_t(u32, length, sg->length - sg_offset);
+
+ bvec_set_page(iov, sg_page(sg), iov_len,
+@@ -355,6 +371,7 @@ static void nvmet_tcp_build_pdu_iovec(st
+
+ length -= iov_len;
+ sg = sg_next(sg);
++ sg_remaining--;
+ iov++;
+ sg_offset = 0;
+ }