From: YunJe Shin <yjshin0438@gmail.com> stable inclusion from stable-v5.10.250 commit 043b4307a99f902697349128fde93b2ddde4686c category: bugfix bugzilla: https://atomgit.com/src-openeuler/kernel/issues/13660 CVE: CVE-2026-23112 Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=... -------------------------------- [ Upstream commit 52a0a98549344ca20ad81a4176d68d28e3c05a5c ] 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: Sasha Levin <sashal@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Conflicts: drivers/nvme/target/tcp.c [Due to commit 5bfaba275ae6 ("nvmet-tcp: don't map pages which can't come from HIGHMEM") not been merged, which is not related to this patch.] Signed-off-by: Zizhi Wo <wozizhi@huawei.com> --- drivers/nvme/target/tcp.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/drivers/nvme/target/tcp.c b/drivers/nvme/target/tcp.c index da2f9c9e0a6d..a470ffb93f66 100644 --- a/drivers/nvme/target/tcp.c +++ b/drivers/nvme/target/tcp.c @@ -286,6 +286,8 @@ static int nvmet_tcp_check_ddgst(struct nvmet_tcp_queue *queue, void *pdu) return 0; } +static void nvmet_tcp_fatal_error(struct nvmet_tcp_queue *queue); + static void nvmet_tcp_unmap_pdu_iovec(struct nvmet_tcp_cmd *cmd) { struct scatterlist *sg; @@ -302,22 +304,38 @@ static void nvmet_tcp_map_pdu_iovec(struct nvmet_tcp_cmd *cmd) struct kvec *iov = cmd->iov; struct scatterlist *sg; u32 length, offset, sg_offset; + unsigned int sg_remaining; length = cmd->pdu_len; cmd->nr_mapped = DIV_ROUND_UP(length, PAGE_SIZE); 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) { u32 iov_len = min_t(u32, length, sg->length - sg_offset); + if (!sg_remaining) { + nvmet_tcp_fatal_error(cmd->queue); + return; + } + if (!sg->length || sg->length <= sg_offset) { + nvmet_tcp_fatal_error(cmd->queue); + return; + } + iov->iov_base = kmap(sg_page(sg)) + sg->offset + sg_offset; iov->iov_len = iov_len; length -= iov_len; sg = sg_next(sg); + sg_remaining--; iov++; sg_offset = 0; } -- 2.39.2