diff --git a/drivers/nvme/target/core.c b/drivers/nvme/target/core.c index 291d782ce544..2e4e066ef6ba 100644 --- a/drivers/nvme/target/core.c +++ b/drivers/nvme/target/core.c @@ -1093,6 +1093,9 @@ static u32 nvmet_io_cmd_transfer_len(struct nvmet_req *req) case nvme_cmd_dsm: return nvmet_dsm_len(req); case nvme_cmd_zone_mgmt_recv: + /* The handler rejects a NUMD whose byte count would wrap. */ + if (le32_to_cpu(req->cmd->zmr.numd) >= U32_MAX / 4) + return 0; return (le32_to_cpu(req->cmd->zmr.numd) + 1) << 2; default: return 0; diff --git a/drivers/nvme/target/zns.c b/drivers/nvme/target/zns.c index 78d82b6ff121..92f5c1435582 100644 --- a/drivers/nvme/target/zns.c +++ b/drivers/nvme/target/zns.c @@ -322,9 +322,17 @@ static void nvmet_bdev_zone_zmgmt_recv_work(struct work_struct *w) void nvmet_bdev_execute_zone_mgmt_recv(struct nvmet_req *req) { - u32 out_bufsize = (le32_to_cpu(req->cmd->zmr.numd) + 1) << 2; + u32 numd = le32_to_cpu(req->cmd->zmr.numd); - if (!nvmet_check_transfer_len(req, out_bufsize)) + /* From U32_MAX / 4 up, (numd + 1) << 2 wraps here and in the work. */ + if (numd >= U32_MAX / 4) { + req->error_loc = offsetof(struct nvme_zone_mgmt_recv_cmd, numd); + nvmet_req_complete(req, + NVME_SC_INVALID_FIELD | NVME_STATUS_DNR); + return; + } + + if (!nvmet_check_transfer_len(req, (numd + 1) << 2)) return; INIT_WORK(&req->z.zmgmt_work, nvmet_bdev_zone_zmgmt_recv_work);