git: 2f363712480e - stable/13 - CTL: Add length validation for incoming HA messages.

From: Alexander Motin <mav_at_FreeBSD.org>
Date: Tue, 29 Mar 2022 01:51:03 UTC
The branch stable/13 has been updated by mav:

URL: https://cgit.FreeBSD.org/src/commit/?id=2f363712480ed320188f3c55008eaafa18cf68fa

commit 2f363712480ed320188f3c55008eaafa18cf68fa
Author:     Alexander Motin <mav@FreeBSD.org>
AuthorDate: 2022-02-24 21:17:34 +0000
Commit:     Alexander Motin <mav@FreeBSD.org>
CommitDate: 2022-03-29 01:30:48 +0000

    CTL: Add length validation for incoming HA messages.
    
    This should fix uninitialized memory reads when working with broken
    HA peer, like one fixed in 1a8d8a3a909.  Instead print error message
    and kill the HA link.
    
    MFC after:      3 days
    Sponsored by:   iXsystems, Inc.
    
    (cherry picked from commit 530d274c15e5b3f69088b4b53f8dc5b2b849a916)
---
 sys/cam/ctl/ctl.c | 82 ++++++++++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 78 insertions(+), 4 deletions(-)

diff --git a/sys/cam/ctl/ctl.c b/sys/cam/ctl/ctl.c
index c89c9a7863fe..8bdd4736254f 100644
--- a/sys/cam/ctl/ctl.c
+++ b/sys/cam/ctl/ctl.c
@@ -1101,7 +1101,14 @@ static void
 ctl_isc_ua(struct ctl_softc *softc, union ctl_ha_msg *msg, int len)
 {
 	struct ctl_lun *lun;
-	uint32_t iid = ctl_get_initindex(&msg->hdr.nexus);
+	uint32_t iid;
+
+	if (len < sizeof(msg->ua)) {
+		printf("%s: Received truncated message %d < %lu\n",
+		    __func__, len, sizeof(msg->ua));
+		ctl_ha_msg_abort(CTL_HA_CHAN_CTL);
+		return;
+	}
 
 	mtx_lock(&softc->ctl_lock);
 	if (msg->hdr.nexus.targ_mapped_lun >= ctl_max_luns ||
@@ -1113,6 +1120,7 @@ ctl_isc_ua(struct ctl_softc *softc, union ctl_ha_msg *msg, int len)
 	mtx_unlock(&softc->ctl_lock);
 	if (msg->ua.ua_type == CTL_UA_THIN_PROV_THRES && msg->ua.ua_set)
 		memcpy(lun->ua_tpt_info, msg->ua.ua_info, 8);
+	iid = ctl_get_initindex(&msg->hdr.nexus);
 	if (msg->ua.ua_all) {
 		if (msg->ua.ua_set)
 			ctl_est_ua_all(lun, iid, msg->ua.ua_type);
@@ -1136,6 +1144,20 @@ ctl_isc_lun_sync(struct ctl_softc *softc, union ctl_ha_msg *msg, int len)
 	ctl_lun_flags oflags;
 	uint32_t targ_lun;
 
+	if (len < offsetof(struct ctl_ha_msg_lun, data[0])) {
+		printf("%s: Received truncated message %d < %lu\n",
+		    __func__, len, offsetof(struct ctl_ha_msg_lun, data[0]));
+		ctl_ha_msg_abort(CTL_HA_CHAN_CTL);
+		return;
+	}
+	i = msg->lun.lun_devid_len + msg->lun.pr_key_count * sizeof(pr_key);
+	if (len < offsetof(struct ctl_ha_msg_lun, data[i])) {
+		printf("%s: Received truncated message data %d < %lu\n",
+		    __func__, len, offsetof(struct ctl_ha_msg_lun, data[i]));
+		ctl_ha_msg_abort(CTL_HA_CHAN_CTL);
+		return;
+	}
+
 	targ_lun = msg->hdr.nexus.targ_mapped_lun;
 	mtx_lock(&softc->ctl_lock);
 	if (targ_lun >= ctl_max_luns ||
@@ -1206,6 +1228,22 @@ ctl_isc_port_sync(struct ctl_softc *softc, union ctl_ha_msg *msg, int len)
 	struct ctl_lun *lun;
 	int i, new;
 
+	if (len < offsetof(struct ctl_ha_msg_port, data[0])) {
+		printf("%s: Received truncated message %d < %lu\n",
+		    __func__, len, offsetof(struct ctl_ha_msg_port, data[0]));
+		ctl_ha_msg_abort(CTL_HA_CHAN_CTL);
+		return;
+	}
+	i = msg->port.name_len + msg->port.lun_map_len +
+	    msg->port.port_devid_len + msg->port.target_devid_len +
+	    msg->port.init_devid_len;
+	if (len < offsetof(struct ctl_ha_msg_port, data[i])) {
+		printf("%s: Received truncated message data %d < %lu\n",
+		    __func__, len, offsetof(struct ctl_ha_msg_port, data[i]));
+		ctl_ha_msg_abort(CTL_HA_CHAN_CTL);
+		return;
+	}
+
 	port = softc->ctl_ports[msg->hdr.nexus.targ_port];
 	if (port == NULL) {
 		CTL_DEBUG_PRINT(("%s: New port %d\n", __func__,
@@ -1317,7 +1355,21 @@ static void
 ctl_isc_iid_sync(struct ctl_softc *softc, union ctl_ha_msg *msg, int len)
 {
 	struct ctl_port *port;
-	int iid;
+	int i, iid;
+
+	if (len < offsetof(struct ctl_ha_msg_iid, data[0])) {
+		printf("%s: Received truncated message %d < %lu\n",
+		    __func__, len, offsetof(struct ctl_ha_msg_iid, data[0]));
+		ctl_ha_msg_abort(CTL_HA_CHAN_CTL);
+		return;
+	}
+	i = msg->iid.name_len;
+	if (len < offsetof(struct ctl_ha_msg_iid, data[i])) {
+		printf("%s: Received truncated message data %d < %lu\n",
+		    __func__, len, offsetof(struct ctl_ha_msg_iid, data[i]));
+		ctl_ha_msg_abort(CTL_HA_CHAN_CTL);
+		return;
+	}
 
 	port = softc->ctl_ports[msg->hdr.nexus.targ_port];
 	if (port == NULL) {
@@ -1343,6 +1395,13 @@ static void
 ctl_isc_login(struct ctl_softc *softc, union ctl_ha_msg *msg, int len)
 {
 
+	if (len < sizeof(msg->login)) {
+		printf("%s: Received truncated message %d < %lu\n",
+		    __func__, len, sizeof(msg->login));
+		ctl_ha_msg_abort(CTL_HA_CHAN_CTL);
+		return;
+	}
+
 	if (msg->login.version != CTL_HA_VERSION) {
 		printf("CTL HA peers have different versions %d != %d\n",
 		    msg->login.version, CTL_HA_VERSION);
@@ -1376,6 +1435,20 @@ ctl_isc_mode_sync(struct ctl_softc *softc, union ctl_ha_msg *msg, int len)
 	u_int i;
 	uint32_t initidx, targ_lun;
 
+	if (len < offsetof(struct ctl_ha_msg_mode, data[0])) {
+		printf("%s: Received truncated message %d < %lu\n",
+		    __func__, len, offsetof(struct ctl_ha_msg_mode, data[0]));
+		ctl_ha_msg_abort(CTL_HA_CHAN_CTL);
+		return;
+	}
+	i = msg->mode.page_len;
+	if (len < offsetof(struct ctl_ha_msg_mode, data[i])) {
+		printf("%s: Received truncated message data %d < %lu\n",
+		    __func__, len, offsetof(struct ctl_ha_msg_mode, data[i]));
+		ctl_ha_msg_abort(CTL_HA_CHAN_CTL);
+		return;
+	}
+
 	targ_lun = msg->hdr.nexus.targ_mapped_lun;
 	mtx_lock(&softc->ctl_lock);
 	if (targ_lun >= ctl_max_luns ||
@@ -1400,7 +1473,7 @@ ctl_isc_mode_sync(struct ctl_softc *softc, union ctl_ha_msg *msg, int len)
 		return;
 	}
 	memcpy(lun->mode_pages.index[i].page_data, msg->mode.data,
-	    lun->mode_pages.index[i].page_len);
+	    min(lun->mode_pages.index[i].page_len, msg->mode.page_len));
 	initidx = ctl_get_initindex(&msg->hdr.nexus);
 	if (initidx != -1)
 		ctl_est_ua_all(lun, initidx, CTL_UA_MODE_CHANGE);
@@ -1437,7 +1510,8 @@ ctl_isc_event_handler(ctl_ha_channel channel, ctl_ha_event event, int param)
 			return;
 		}
 
-		CTL_DEBUG_PRINT(("CTL: msg_type %d\n", msg->hdr.msg_type));
+		CTL_DEBUG_PRINT(("CTL: msg_type %d len %d\n",
+		    msg->hdr.msg_type, param));
 		switch (msg->hdr.msg_type) {
 		case CTL_MSG_SERIALIZE:
 			io = ctl_alloc_io(softc->othersc_pool);