Commit ad95bab0 authored by Maurizio Lombardi's avatar Maurizio Lombardi Committed by Keith Busch
Browse files

nvme-tcp: fix potential memory corruption in nvme_tcp_recv_pdu()



nvme_tcp_recv_pdu() doesn't check the validity of the header length.
When header digests are enabled, a target might send a packet with an
invalid header length (e.g. 255), causing nvme_tcp_verify_hdgst()
to access memory outside the allocated area and cause memory corruptions
by overwriting it with the calculated digest.

Fix this by rejecting packets with an unexpected header length.

Fixes: 3f2304f8 ("nvme-tcp: add NVMe over TCP host driver")
Signed-off-by: default avatarMaurizio Lombardi <mlombard@redhat.com>
Reviewed-by: default avatarSagi Grimberg <sagi@grimberg.me>
Signed-off-by: default avatarKeith Busch <kbusch@kernel.org>
parent afb41b08
Loading
Loading
Loading
Loading
+29 −3
Original line number Diff line number Diff line
@@ -217,6 +217,19 @@ static inline int nvme_tcp_queue_id(struct nvme_tcp_queue *queue)
	return queue - queue->ctrl->queues;
}

static inline bool nvme_tcp_recv_pdu_supported(enum nvme_tcp_pdu_type type)
{
	switch (type) {
	case nvme_tcp_c2h_term:
	case nvme_tcp_c2h_data:
	case nvme_tcp_r2t:
	case nvme_tcp_rsp:
		return true;
	default:
		return false;
	}
}

/*
 * Check if the queue is TLS encrypted
 */
@@ -818,6 +831,16 @@ static int nvme_tcp_recv_pdu(struct nvme_tcp_queue *queue, struct sk_buff *skb,
		return 0;

	hdr = queue->pdu;
	if (unlikely(hdr->hlen != sizeof(struct nvme_tcp_rsp_pdu))) {
		if (!nvme_tcp_recv_pdu_supported(hdr->type))
			goto unsupported_pdu;

		dev_err(queue->ctrl->ctrl.device,
			"pdu type %d has unexpected header length (%d)\n",
			hdr->type, hdr->hlen);
		return -EPROTO;
	}

	if (unlikely(hdr->type == nvme_tcp_c2h_term)) {
		/*
		 * C2HTermReq never includes Header or Data digests.
@@ -850,11 +873,14 @@ static int nvme_tcp_recv_pdu(struct nvme_tcp_queue *queue, struct sk_buff *skb,
		nvme_tcp_init_recv_ctx(queue);
		return nvme_tcp_handle_r2t(queue, (void *)queue->pdu);
	default:
		goto unsupported_pdu;
	}

unsupported_pdu:
	dev_err(queue->ctrl->ctrl.device,
		"unsupported pdu type (%d)\n", hdr->type);
	return -EINVAL;
}
}

static inline void nvme_tcp_end_request(struct request *rq, u16 status)
{