svn commit: r305453 - in head/sys: dev/hyperv/netvsc net

Sepherosa Ziehau sephe at FreeBSD.org
Tue Sep 6 03:20:08 UTC 2016


Author: sephe
Date: Tue Sep  6 03:20:06 2016
New Revision: 305453
URL: https://svnweb.freebsd.org/changeset/base/305453

Log:
  hyperv/hn: Stringent RNDIS packet message length/offset check.
  
  While I'm here, use definition in net/rndis.h
  
  MFC after:	1 week
  Sponsored by:	Microsoft
  Differential Revision:	https://reviews.freebsd.org/D7782

Modified:
  head/sys/dev/hyperv/netvsc/hv_rndis_filter.c
  head/sys/net/rndis.h

Modified: head/sys/dev/hyperv/netvsc/hv_rndis_filter.c
==============================================================================
--- head/sys/dev/hyperv/netvsc/hv_rndis_filter.c	Tue Sep  6 01:10:51 2016	(r305452)
+++ head/sys/dev/hyperv/netvsc/hv_rndis_filter.c	Tue Sep  6 03:20:06 2016	(r305453)
@@ -159,39 +159,22 @@ hv_rf_receive_indicate_status(struct hn_
 }
 
 static int
-hv_rf_find_recvinfo(const rndis_packet *rpkt, struct hn_recvinfo *info)
+hn_rndis_rxinfo(const void *info_data, int info_dlen, struct hn_recvinfo *info)
 {
-	const struct rndis_pktinfo *pi;
-	uint32_t mask = 0, len;
+	const struct rndis_pktinfo *pi = info_data;
+	uint32_t mask = 0;
 
-	info->vlan_info = HN_NDIS_VLAN_INFO_INVALID;
-	info->csum_info = HN_NDIS_RXCSUM_INFO_INVALID;
-	info->hash_info = HN_NDIS_HASH_INFO_INVALID;
-
-	if (rpkt->per_pkt_info_offset == 0)
-		return (0);
-	if (__predict_false(rpkt->per_pkt_info_offset &
-	    (RNDIS_PKTINFO_ALIGN - 1)))
-		return (EINVAL);
-	if (__predict_false(rpkt->per_pkt_info_offset <
-	    RNDIS_PACKET_MSG_OFFSET_MIN))
-		return (EINVAL);
-
-	pi = (const struct rndis_pktinfo *)
-	    ((const uint8_t *)rpkt + rpkt->per_pkt_info_offset);
-	len = rpkt->per_pkt_info_length;
-
-	while (len != 0) {
+	while (info_dlen != 0) {
 		const void *data;
 		uint32_t dlen;
 
-		if (__predict_false(len < sizeof(*pi)))
+		if (__predict_false(info_dlen < sizeof(*pi)))
 			return (EINVAL);
-		if (__predict_false(len < pi->rm_size))
+		if (__predict_false(info_dlen < pi->rm_size))
 			return (EINVAL);
-		len -= pi->rm_size;
+		info_dlen -= pi->rm_size;
 
-		if (__predict_false(pi->rm_size & (RNDIS_PKTINFO_ALIGN - 1)))
+		if (__predict_false(pi->rm_size & RNDIS_PKTINFO_SIZE_ALIGNMASK))
 			return (EINVAL);
 		if (__predict_false(pi->rm_size < pi->rm_pktinfooffset))
 			return (EINVAL);
@@ -249,43 +232,183 @@ next:
 	return (0);
 }
 
+static __inline bool
+hn_rndis_check_overlap(int off, int len, int check_off, int check_len)
+{
+
+	if (off < check_off) {
+		if (__predict_true(off + len <= check_off))
+			return (false);
+	} else if (off > check_off) {
+		if (__predict_true(check_off + check_len <= off))
+			return (false);
+	}
+	return (true);
+}
+
 /*
  * RNDIS filter receive data
  */
 static void
 hv_rf_receive_data(struct hn_rx_ring *rxr, const void *data, int dlen)
 {
-	const rndis_msg *message = data;
-	const rndis_packet *rndis_pkt;
-	uint32_t data_offset;
+	const struct rndis_packet_msg *pkt;
 	struct hn_recvinfo info;
-
-	rndis_pkt = &message->msg.packet;
+	int data_off, pktinfo_off, data_len, pktinfo_len;
 
 	/*
-	 * Fixme:  Handle multiple rndis pkt msgs that may be enclosed in this
-	 * netvsc packet (ie tot_data_buf_len != message_length)
+	 * Check length.
 	 */
+	if (__predict_false(dlen < sizeof(*pkt))) {
+		if_printf(rxr->hn_ifp, "invalid RNDIS packet msg\n");
+		return;
+	}
+	pkt = data;
 
-	/* Remove rndis header, then pass data packet up the stack */
-	data_offset = RNDIS_HEADER_SIZE + rndis_pkt->data_offset;
+	if (__predict_false(dlen < pkt->rm_len)) {
+		if_printf(rxr->hn_ifp, "truncated RNDIS packet msg, "
+		    "dlen %d, msglen %u\n", dlen, pkt->rm_len);
+		return;
+	}
+	if (__predict_false(pkt->rm_len <
+	    pkt->rm_datalen + pkt->rm_oobdatalen + pkt->rm_pktinfolen)) {
+		if_printf(rxr->hn_ifp, "invalid RNDIS packet msglen, "
+		    "msglen %u, data %u, oob %u, pktinfo %u\n",
+		    pkt->rm_len, pkt->rm_datalen, pkt->rm_oobdatalen,
+		    pkt->rm_pktinfolen);
+		return;
+	}
+	if (__predict_false(pkt->rm_datalen == 0)) {
+		if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, no data\n");
+		return;
+	}
 
-	dlen -= data_offset;
-	if (dlen < rndis_pkt->data_length) {
-		if_printf(rxr->hn_ifp,
-		    "total length %u is less than data length %u\n",
-		    dlen, rndis_pkt->data_length);
+	/*
+	 * Check offests.
+	 */
+#define IS_OFFSET_INVALID(ofs)			\
+	((ofs) < RNDIS_PACKET_MSG_OFFSET_MIN ||	\
+	 ((ofs) & RNDIS_PACKET_MSG_OFFSET_ALIGNMASK))
+
+	/* XXX Hyper-V does not meet data offset alignment requirement */
+	if (__predict_false(pkt->rm_dataoffset < RNDIS_PACKET_MSG_OFFSET_MIN)) {
+		if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
+		    "data offset %u\n", pkt->rm_dataoffset);
 		return;
 	}
+	if (__predict_false(pkt->rm_oobdataoffset > 0 &&
+	    IS_OFFSET_INVALID(pkt->rm_oobdataoffset))) {
+		if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
+		    "oob offset %u\n", pkt->rm_oobdataoffset);
+		return;
+	}
+	if (__predict_true(pkt->rm_pktinfooffset > 0) &&
+	    __predict_false(IS_OFFSET_INVALID(pkt->rm_pktinfooffset))) {
+		if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
+		    "pktinfo offset %u\n", pkt->rm_pktinfooffset);
+		return;
+	}
+
+#undef IS_OFFSET_INVALID
+
+	data_off = RNDIS_PACKET_MSG_OFFSET_ABS(pkt->rm_dataoffset);
+	data_len = pkt->rm_datalen;
+	pktinfo_off = RNDIS_PACKET_MSG_OFFSET_ABS(pkt->rm_pktinfooffset);
+	pktinfo_len = pkt->rm_pktinfolen;
+
+	/*
+	 * Check OOB coverage.
+	 */
+	if (__predict_false(pkt->rm_oobdatalen != 0)) {
+		int oob_off, oob_len;
+
+		if_printf(rxr->hn_ifp, "got oobdata\n");
+		oob_off = RNDIS_PACKET_MSG_OFFSET_ABS(pkt->rm_oobdataoffset);
+		oob_len = pkt->rm_oobdatalen;
+
+		if (__predict_false(oob_off + oob_len > pkt->rm_len)) {
+			if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
+			    "oob overflow, msglen %u, oob abs %d len %d\n",
+			    pkt->rm_len, oob_off, oob_len);
+			return;
+		}
+
+		/*
+		 * Check against data.
+		 */
+		if (hn_rndis_check_overlap(oob_off, oob_len,
+		    data_off, data_len)) {
+			if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
+			    "oob overlaps data, oob abs %d len %d, "
+			    "data abs %d len %d\n",
+			    oob_off, oob_len, data_off, data_len);
+			return;
+		}
+
+		/*
+		 * Check against pktinfo.
+		 */
+		if (pktinfo_len != 0 &&
+		    hn_rndis_check_overlap(oob_off, oob_len,
+		    pktinfo_off, pktinfo_len)) {
+			if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
+			    "oob overlaps pktinfo, oob abs %d len %d, "
+			    "pktinfo abs %d len %d\n",
+			    oob_off, oob_len, pktinfo_off, pktinfo_len);
+			return;
+		}
+	}
 
-	dlen = rndis_pkt->data_length;
-	data = (const uint8_t *)data + data_offset;
+	/*
+	 * Check per-packet-info coverage and find useful per-packet-info.
+	 */
+	info.vlan_info = HN_NDIS_VLAN_INFO_INVALID;
+	info.csum_info = HN_NDIS_RXCSUM_INFO_INVALID;
+	info.hash_info = HN_NDIS_HASH_INFO_INVALID;
+	if (__predict_true(pktinfo_len != 0)) {
+		bool overlap;
+		int error;
+
+		if (__predict_false(pktinfo_off + pktinfo_len > pkt->rm_len)) {
+			if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
+			    "pktinfo overflow, msglen %u, "
+			    "pktinfo abs %d len %d\n",
+			    pkt->rm_len, pktinfo_off, pktinfo_len);
+			return;
+		}
+
+		/*
+		 * Check packet info coverage.
+		 */
+		overlap = hn_rndis_check_overlap(pktinfo_off, pktinfo_len,
+		    data_off, data_len);
+		if (__predict_false(overlap)) {
+			if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
+			    "pktinfo overlap data, pktinfo abs %d len %d, "
+			    "data abs %d len %d\n",
+			    pktinfo_off, pktinfo_len, data_off, data_len);
+			return;
+		}
+
+		/*
+		 * Find useful per-packet-info.
+		 */
+		error = hn_rndis_rxinfo(((const uint8_t *)pkt) + pktinfo_off,
+		    pktinfo_len, &info);
+		if (__predict_false(error)) {
+			if_printf(rxr->hn_ifp, "invalid RNDIS packet msg "
+			    "pktinfo\n");
+			return;
+		}
+	}
 
-	if (hv_rf_find_recvinfo(rndis_pkt, &info)) {
-		if_printf(rxr->hn_ifp, "recvinfo parsing failed\n");
+	if (__predict_false(data_off + data_len > pkt->rm_len)) {
+		if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
+		    "data overflow, msglen %u, data abs %d len %d\n",
+		    pkt->rm_len, data_off, data_len);
 		return;
 	}
-	netvsc_recv(rxr, data, dlen, &info);
+	netvsc_recv(rxr, ((const uint8_t *)pkt) + data_off, data_len, &info);
 }
 
 /*
@@ -565,7 +688,7 @@ hn_rndis_query(struct hn_softc *sc, uint
 	 * Check output data length and offset.
 	 */
 	/* ofs is the offset from the beginning of comp. */
-	ofs = RNDIS_QUERY_COMP_INFOBUFABS(comp->rm_infobufoffset);
+	ofs = RNDIS_QUERY_COMP_INFOBUFOFFSET_ABS(comp->rm_infobufoffset);
 	if (ofs < sizeof(*comp) || ofs + comp->rm_infobuflen > comp_len) {
 		if_printf(sc->hn_ifp, "RNDIS query invalid comp ib off/len, "
 		    "%u/%u\n", comp->rm_infobufoffset, comp->rm_infobuflen);

Modified: head/sys/net/rndis.h
==============================================================================
--- head/sys/net/rndis.h	Tue Sep  6 01:10:51 2016	(r305452)
+++ head/sys/net/rndis.h	Tue Sep  6 03:20:06 2016	(r305453)
@@ -127,6 +127,14 @@ struct rndis_packet_msg {
 	(sizeof(struct rndis_packet_msg) -	\
 	 __offsetof(struct rndis_packet_msg, rm_dataoffset))
 
+/* Offset from the beginning of rndis_packet_msg. */
+#define	RNDIS_PACKET_MSG_OFFSET_ABS(ofs)	\
+	((ofs) + __offsetof(struct rndis_packet_msg, rm_dataoffset))
+
+#define	RNDIS_PACKET_MSG_OFFSET_ALIGN		4
+#define	RNDIS_PACKET_MSG_OFFSET_ALIGNMASK	\
+	(RNDIS_PACKET_MSG_OFFSET_ALIGN - 1)
+
 /* Per-packet-info for RNDIS data message */
 struct rndis_pktinfo {
 	uint32_t rm_size;
@@ -137,7 +145,8 @@ struct rndis_pktinfo {
 
 #define	RNDIS_PKTINFO_OFFSET		\
 	__offsetof(struct rndis_pktinfo, rm_data[0])
-#define	RNDIS_PKTINFO_ALIGN		4
+#define	RNDIS_PKTINFO_SIZE_ALIGN	4
+#define	RNDIS_PKTINFO_SIZE_ALIGNMASK	(RNDIS_PKTINFO_SIZE_ALIGN - 1)
 
 #define	NDIS_PKTINFO_TYPE_CSUM		0
 #define	NDIS_PKTINFO_TYPE_IPSEC		1
@@ -236,7 +245,8 @@ struct rndis_query_comp {
 	uint32_t rm_infobufoffset;
 };
 
-#define	RNDIS_QUERY_COMP_INFOBUFABS(ofs)	\
+/* infobuf offset from the beginning of rndis_query_comp. */
+#define	RNDIS_QUERY_COMP_INFOBUFOFFSET_ABS(ofs)	\
 	((ofs) + __offsetof(struct rndis_query_req, rm_rid))
 
 /* Send a set object request. */


More information about the svn-src-all mailing list