Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 6 Aug 2019 17:11:17 +0000 (UTC)
From:      Gordon Tetlow <gordon@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-releng@freebsd.org
Subject:   svn commit: r350644 - in releng: 11.2/sys/netinet6 11.3/sys/netinet6 12.0/sys/netinet6
Message-ID:  <201908061711.x76HBHa6039007@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: gordon
Date: Tue Aug  6 17:11:17 2019
New Revision: 350644
URL: https://svnweb.freebsd.org/changeset/base/350644

Log:
  Fix ICMPv6 / MLDv2 out-of-bounds memory access.
  
  Approved by:	so
  Security:	FreeBSD-SA-19:19.mldv2
  Security:	CVE-2019-5608

Modified:
  releng/11.2/sys/netinet6/mld6.c
  releng/11.3/sys/netinet6/mld6.c
  releng/12.0/sys/netinet6/mld6.c

Modified: releng/11.2/sys/netinet6/mld6.c
==============================================================================
--- releng/11.2/sys/netinet6/mld6.c	Tue Aug  6 17:09:47 2019	(r350643)
+++ releng/11.2/sys/netinet6/mld6.c	Tue Aug  6 17:11:17 2019	(r350644)
@@ -137,14 +137,15 @@ static int	mld_v2_enqueue_group_record(struct mbufq *,
 		    struct in6_multi *, const int, const int, const int,
 		    const int);
 static int	mld_v2_input_query(struct ifnet *, const struct ip6_hdr *,
-		    struct mbuf *, const int, const int);
+		    struct mbuf *, struct mldv2_query *, const int, const int);
 static int	mld_v2_merge_state_changes(struct in6_multi *,
 		    struct mbufq *);
 static void	mld_v2_process_group_timers(struct mld_ifsoftc *,
 		    struct mbufq *, struct mbufq *,
 		    struct in6_multi *, const int);
 static int	mld_v2_process_group_query(struct in6_multi *,
-		    struct mld_ifsoftc *mli, int, struct mbuf *, const int);
+		    struct mld_ifsoftc *mli, int, struct mbuf *,
+		    struct mldv2_query *, const int);
 static int	sysctl_mld_gsr(SYSCTL_HANDLER_ARGS);
 static int	sysctl_mld_ifinfo(SYSCTL_HANDLER_ARGS);
 
@@ -794,16 +795,16 @@ mld_v1_update_group(struct in6_multi *inm, const int t
  * Process a received MLDv2 general, group-specific or
  * group-and-source-specific query.
  *
- * Assumes that the query header has been pulled up to sizeof(mldv2_query).
+ * Assumes that mld points to a struct mldv2_query which is stored in
+ * contiguous memory.
  *
  * Return 0 if successful, otherwise an appropriate error code is returned.
  */
 static int
 mld_v2_input_query(struct ifnet *ifp, const struct ip6_hdr *ip6,
-    struct mbuf *m, const int off, const int icmp6len)
+    struct mbuf *m, struct mldv2_query *mld, const int off, const int icmp6len)
 {
 	struct mld_ifsoftc	*mli;
-	struct mldv2_query	*mld;
 	struct in6_multi	*inm;
 	uint32_t		 maxdelay, nsrc, qqi;
 	int			 is_general_query;
@@ -828,8 +829,6 @@ mld_v2_input_query(struct ifnet *ifp, const struct ip6
 
 	CTR2(KTR_MLD, "input v2 query on ifp %p(%s)", ifp, if_name(ifp));
 
-	mld = (struct mldv2_query *)(mtod(m, uint8_t *) + off);
-
 	maxdelay = ntohs(mld->mld_maxdelay);	/* in 1/10ths of a second */
 	if (maxdelay >= 32768) {
 		maxdelay = (MLD_MRC_MANT(maxdelay) | 0x1000) <<
@@ -954,7 +953,7 @@ mld_v2_input_query(struct ifnet *ifp, const struct ip6
 		 * group-specific or group-and-source query.
 		 */
 		if (mli->mli_v2_timer == 0 || mli->mli_v2_timer >= timer)
-			mld_v2_process_group_query(inm, mli, timer, m, off);
+			mld_v2_process_group_query(inm, mli, timer, m, mld, off);
 
 		/* XXX Clear embedded scope ID as userland won't expect it. */
 		in6_clearscope(&mld->mld_addr);
@@ -975,9 +974,8 @@ out_locked:
  */
 static int
 mld_v2_process_group_query(struct in6_multi *inm, struct mld_ifsoftc *mli,
-    int timer, struct mbuf *m0, const int off)
+    int timer, struct mbuf *m0, struct mldv2_query *mld, const int off)
 {
-	struct mldv2_query	*mld;
 	int			 retval;
 	uint16_t		 nsrc;
 
@@ -985,7 +983,6 @@ mld_v2_process_group_query(struct in6_multi *inm, stru
 	MLD_LOCK_ASSERT();
 
 	retval = 0;
-	mld = (struct mldv2_query *)(mtod(m0, uint8_t *) + off);
 
 	switch (inm->in6m_state) {
 	case MLD_NOT_MEMBER:
@@ -1005,6 +1002,15 @@ mld_v2_process_group_query(struct in6_multi *inm, stru
 
 	nsrc = ntohs(mld->mld_numsrc);
 
+	/* Length should be checked by calling function. */
+	KASSERT((m0->m_flags & M_PKTHDR) == 0 ||
+	    m0->m_pkthdr.len >= off + sizeof(struct mldv2_query) +
+	    nsrc * sizeof(struct in6_addr),
+	    ("mldv2 packet is too short: (%d bytes < %zd bytes, m=%p)",
+	    m0->m_pkthdr.len, off + sizeof(struct mldv2_query) +
+	    nsrc * sizeof(struct in6_addr), m0));
+
+
 	/*
 	 * Deal with group-specific queries upfront.
 	 * If any group query is already pending, purge any recorded
@@ -1046,28 +1052,20 @@ mld_v2_process_group_query(struct in6_multi *inm, stru
 	 * report for those sources.
 	 */
 	if (inm->in6m_nsrc > 0) {
-		struct mbuf		*m;
-		uint8_t			*sp;
+		struct in6_addr		 srcaddr;
 		int			 i, nrecorded;
 		int			 soff;
 
-		m = m0;
 		soff = off + sizeof(struct mldv2_query);
 		nrecorded = 0;
 		for (i = 0; i < nsrc; i++) {
-			sp = mtod(m, uint8_t *) + soff;
-			retval = in6m_record_source(inm,
-			    (const struct in6_addr *)sp);
+			m_copydata(m0, soff, sizeof(struct in6_addr),
+			    (caddr_t)&srcaddr);
+			retval = in6m_record_source(inm, &srcaddr);
 			if (retval < 0)
 				break;
 			nrecorded += retval;
 			soff += sizeof(struct in6_addr);
-			if (soff >= m->m_len) {
-				soff = soff - m->m_len;
-				m = m->m_next;
-				if (m == NULL)
-					break;
-			}
 		}
 		if (nrecorded > 0) {
 			CTR1(KTR_MLD,
@@ -1276,8 +1274,8 @@ mld_input(struct mbuf *m, int off, int icmp6len)
 			if (mld_v1_input_query(ifp, ip6, mld) != 0)
 				return (0);
 		} else if (icmp6len >= sizeof(struct mldv2_query)) {
-			if (mld_v2_input_query(ifp, ip6, m, off,
-			    icmp6len) != 0)
+			if (mld_v2_input_query(ifp, ip6, m,
+			    (struct mldv2_query *)mld, off, icmp6len) != 0)
 				return (0);
 		}
 		break;

Modified: releng/11.3/sys/netinet6/mld6.c
==============================================================================
--- releng/11.3/sys/netinet6/mld6.c	Tue Aug  6 17:09:47 2019	(r350643)
+++ releng/11.3/sys/netinet6/mld6.c	Tue Aug  6 17:11:17 2019	(r350644)
@@ -137,14 +137,15 @@ static int	mld_v2_enqueue_group_record(struct mbufq *,
 		    struct in6_multi *, const int, const int, const int,
 		    const int);
 static int	mld_v2_input_query(struct ifnet *, const struct ip6_hdr *,
-		    struct mbuf *, const int, const int);
+		    struct mbuf *, struct mldv2_query *, const int, const int);
 static int	mld_v2_merge_state_changes(struct in6_multi *,
 		    struct mbufq *);
 static void	mld_v2_process_group_timers(struct mld_ifsoftc *,
 		    struct mbufq *, struct mbufq *,
 		    struct in6_multi *, const int);
 static int	mld_v2_process_group_query(struct in6_multi *,
-		    struct mld_ifsoftc *mli, int, struct mbuf *, const int);
+		    struct mld_ifsoftc *mli, int, struct mbuf *,
+		    struct mldv2_query *, const int);
 static int	sysctl_mld_gsr(SYSCTL_HANDLER_ARGS);
 static int	sysctl_mld_ifinfo(SYSCTL_HANDLER_ARGS);
 
@@ -794,16 +795,16 @@ mld_v1_update_group(struct in6_multi *inm, const int t
  * Process a received MLDv2 general, group-specific or
  * group-and-source-specific query.
  *
- * Assumes that the query header has been pulled up to sizeof(mldv2_query).
+ * Assumes that mld points to a struct mldv2_query which is stored in
+ * contiguous memory.
  *
  * Return 0 if successful, otherwise an appropriate error code is returned.
  */
 static int
 mld_v2_input_query(struct ifnet *ifp, const struct ip6_hdr *ip6,
-    struct mbuf *m, const int off, const int icmp6len)
+    struct mbuf *m, struct mldv2_query *mld, const int off, const int icmp6len)
 {
 	struct mld_ifsoftc	*mli;
-	struct mldv2_query	*mld;
 	struct in6_multi	*inm;
 	uint32_t		 maxdelay, nsrc, qqi;
 	int			 is_general_query;
@@ -828,8 +829,6 @@ mld_v2_input_query(struct ifnet *ifp, const struct ip6
 
 	CTR2(KTR_MLD, "input v2 query on ifp %p(%s)", ifp, if_name(ifp));
 
-	mld = (struct mldv2_query *)(mtod(m, uint8_t *) + off);
-
 	maxdelay = ntohs(mld->mld_maxdelay);	/* in 1/10ths of a second */
 	if (maxdelay >= 32768) {
 		maxdelay = (MLD_MRC_MANT(maxdelay) | 0x1000) <<
@@ -954,7 +953,7 @@ mld_v2_input_query(struct ifnet *ifp, const struct ip6
 		 * group-specific or group-and-source query.
 		 */
 		if (mli->mli_v2_timer == 0 || mli->mli_v2_timer >= timer)
-			mld_v2_process_group_query(inm, mli, timer, m, off);
+			mld_v2_process_group_query(inm, mli, timer, m, mld, off);
 
 		/* XXX Clear embedded scope ID as userland won't expect it. */
 		in6_clearscope(&mld->mld_addr);
@@ -975,9 +974,8 @@ out_locked:
  */
 static int
 mld_v2_process_group_query(struct in6_multi *inm, struct mld_ifsoftc *mli,
-    int timer, struct mbuf *m0, const int off)
+    int timer, struct mbuf *m0, struct mldv2_query *mld, const int off)
 {
-	struct mldv2_query	*mld;
 	int			 retval;
 	uint16_t		 nsrc;
 
@@ -985,7 +983,6 @@ mld_v2_process_group_query(struct in6_multi *inm, stru
 	MLD_LOCK_ASSERT();
 
 	retval = 0;
-	mld = (struct mldv2_query *)(mtod(m0, uint8_t *) + off);
 
 	switch (inm->in6m_state) {
 	case MLD_NOT_MEMBER:
@@ -1005,6 +1002,15 @@ mld_v2_process_group_query(struct in6_multi *inm, stru
 
 	nsrc = ntohs(mld->mld_numsrc);
 
+	/* Length should be checked by calling function. */
+	KASSERT((m0->m_flags & M_PKTHDR) == 0 ||
+	    m0->m_pkthdr.len >= off + sizeof(struct mldv2_query) +
+	    nsrc * sizeof(struct in6_addr),
+	    ("mldv2 packet is too short: (%d bytes < %zd bytes, m=%p)",
+	    m0->m_pkthdr.len, off + sizeof(struct mldv2_query) +
+	    nsrc * sizeof(struct in6_addr), m0));
+
+
 	/*
 	 * Deal with group-specific queries upfront.
 	 * If any group query is already pending, purge any recorded
@@ -1046,28 +1052,20 @@ mld_v2_process_group_query(struct in6_multi *inm, stru
 	 * report for those sources.
 	 */
 	if (inm->in6m_nsrc > 0) {
-		struct mbuf		*m;
-		uint8_t			*sp;
+		struct in6_addr		 srcaddr;
 		int			 i, nrecorded;
 		int			 soff;
 
-		m = m0;
 		soff = off + sizeof(struct mldv2_query);
 		nrecorded = 0;
 		for (i = 0; i < nsrc; i++) {
-			sp = mtod(m, uint8_t *) + soff;
-			retval = in6m_record_source(inm,
-			    (const struct in6_addr *)sp);
+			m_copydata(m0, soff, sizeof(struct in6_addr),
+			    (caddr_t)&srcaddr);
+			retval = in6m_record_source(inm, &srcaddr);
 			if (retval < 0)
 				break;
 			nrecorded += retval;
 			soff += sizeof(struct in6_addr);
-			if (soff >= m->m_len) {
-				soff = soff - m->m_len;
-				m = m->m_next;
-				if (m == NULL)
-					break;
-			}
 		}
 		if (nrecorded > 0) {
 			CTR1(KTR_MLD,
@@ -1276,8 +1274,8 @@ mld_input(struct mbuf *m, int off, int icmp6len)
 			if (mld_v1_input_query(ifp, ip6, mld) != 0)
 				return (0);
 		} else if (icmp6len >= sizeof(struct mldv2_query)) {
-			if (mld_v2_input_query(ifp, ip6, m, off,
-			    icmp6len) != 0)
+			if (mld_v2_input_query(ifp, ip6, m,
+			    (struct mldv2_query *)mld, off, icmp6len) != 0)
 				return (0);
 		}
 		break;

Modified: releng/12.0/sys/netinet6/mld6.c
==============================================================================
--- releng/12.0/sys/netinet6/mld6.c	Tue Aug  6 17:09:47 2019	(r350643)
+++ releng/12.0/sys/netinet6/mld6.c	Tue Aug  6 17:11:17 2019	(r350644)
@@ -139,14 +139,15 @@ static int	mld_v2_enqueue_group_record(struct mbufq *,
 		    struct in6_multi *, const int, const int, const int,
 		    const int);
 static int	mld_v2_input_query(struct ifnet *, const struct ip6_hdr *,
-		    struct mbuf *, const int, const int);
+		    struct mbuf *, struct mldv2_query *, const int, const int);
 static int	mld_v2_merge_state_changes(struct in6_multi *,
 		    struct mbufq *);
 static void	mld_v2_process_group_timers(struct in6_multi_head *,
 		    struct mbufq *, struct mbufq *,
 		    struct in6_multi *, const int);
 static int	mld_v2_process_group_query(struct in6_multi *,
-		    struct mld_ifsoftc *mli, int, struct mbuf *, const int);
+		    struct mld_ifsoftc *mli, int, struct mbuf *,
+		    struct mldv2_query *, const int);
 static int	sysctl_mld_gsr(SYSCTL_HANDLER_ARGS);
 static int	sysctl_mld_ifinfo(SYSCTL_HANDLER_ARGS);
 
@@ -797,16 +798,16 @@ mld_v1_update_group(struct in6_multi *inm, const int t
  * Process a received MLDv2 general, group-specific or
  * group-and-source-specific query.
  *
- * Assumes that the query header has been pulled up to sizeof(mldv2_query).
+ * Assumes that mld points to a struct mldv2_query which is stored in
+ * contiguous memory.
  *
  * Return 0 if successful, otherwise an appropriate error code is returned.
  */
 static int
 mld_v2_input_query(struct ifnet *ifp, const struct ip6_hdr *ip6,
-    struct mbuf *m, const int off, const int icmp6len)
+    struct mbuf *m, struct mldv2_query *mld, const int off, const int icmp6len)
 {
 	struct mld_ifsoftc	*mli;
-	struct mldv2_query	*mld;
 	struct in6_multi	*inm;
 	uint32_t		 maxdelay, nsrc, qqi;
 	int			 is_general_query;
@@ -831,8 +832,6 @@ mld_v2_input_query(struct ifnet *ifp, const struct ip6
 
 	CTR2(KTR_MLD, "input v2 query on ifp %p(%s)", ifp, if_name(ifp));
 
-	mld = (struct mldv2_query *)(mtod(m, uint8_t *) + off);
-
 	maxdelay = ntohs(mld->mld_maxdelay);	/* in 1/10ths of a second */
 	if (maxdelay >= 32768) {
 		maxdelay = (MLD_MRC_MANT(maxdelay) | 0x1000) <<
@@ -957,7 +956,7 @@ mld_v2_input_query(struct ifnet *ifp, const struct ip6
 		 * group-specific or group-and-source query.
 		 */
 		if (mli->mli_v2_timer == 0 || mli->mli_v2_timer >= timer)
-			mld_v2_process_group_query(inm, mli, timer, m, off);
+			mld_v2_process_group_query(inm, mli, timer, m, mld, off);
 
 		/* XXX Clear embedded scope ID as userland won't expect it. */
 		in6_clearscope(&mld->mld_addr);
@@ -978,9 +977,8 @@ out_locked:
  */
 static int
 mld_v2_process_group_query(struct in6_multi *inm, struct mld_ifsoftc *mli,
-    int timer, struct mbuf *m0, const int off)
+    int timer, struct mbuf *m0, struct mldv2_query *mld, const int off)
 {
-	struct mldv2_query	*mld;
 	int			 retval;
 	uint16_t		 nsrc;
 
@@ -988,7 +986,6 @@ mld_v2_process_group_query(struct in6_multi *inm, stru
 	MLD_LOCK_ASSERT();
 
 	retval = 0;
-	mld = (struct mldv2_query *)(mtod(m0, uint8_t *) + off);
 
 	switch (inm->in6m_state) {
 	case MLD_NOT_MEMBER:
@@ -1008,6 +1005,15 @@ mld_v2_process_group_query(struct in6_multi *inm, stru
 
 	nsrc = ntohs(mld->mld_numsrc);
 
+	/* Length should be checked by calling function. */
+	KASSERT((m0->m_flags & M_PKTHDR) == 0 ||
+	    m0->m_pkthdr.len >= off + sizeof(struct mldv2_query) +
+	    nsrc * sizeof(struct in6_addr),
+	    ("mldv2 packet is too short: (%d bytes < %zd bytes, m=%p)",
+	    m0->m_pkthdr.len, off + sizeof(struct mldv2_query) +
+	    nsrc * sizeof(struct in6_addr), m0));
+
+
 	/*
 	 * Deal with group-specific queries upfront.
 	 * If any group query is already pending, purge any recorded
@@ -1049,28 +1055,20 @@ mld_v2_process_group_query(struct in6_multi *inm, stru
 	 * report for those sources.
 	 */
 	if (inm->in6m_nsrc > 0) {
-		struct mbuf		*m;
-		uint8_t			*sp;
+		struct in6_addr		 srcaddr;
 		int			 i, nrecorded;
 		int			 soff;
 
-		m = m0;
 		soff = off + sizeof(struct mldv2_query);
 		nrecorded = 0;
 		for (i = 0; i < nsrc; i++) {
-			sp = mtod(m, uint8_t *) + soff;
-			retval = in6m_record_source(inm,
-			    (const struct in6_addr *)sp);
+			m_copydata(m0, soff, sizeof(struct in6_addr),
+			    (caddr_t)&srcaddr);
+			retval = in6m_record_source(inm, &srcaddr);
 			if (retval < 0)
 				break;
 			nrecorded += retval;
 			soff += sizeof(struct in6_addr);
-			if (soff >= m->m_len) {
-				soff = soff - m->m_len;
-				m = m->m_next;
-				if (m == NULL)
-					break;
-			}
 		}
 		if (nrecorded > 0) {
 			CTR1(KTR_MLD,
@@ -1279,8 +1277,8 @@ mld_input(struct mbuf *m, int off, int icmp6len)
 			if (mld_v1_input_query(ifp, ip6, mld) != 0)
 				return (0);
 		} else if (icmp6len >= sizeof(struct mldv2_query)) {
-			if (mld_v2_input_query(ifp, ip6, m, off,
-			    icmp6len) != 0)
+			if (mld_v2_input_query(ifp, ip6, m,
+			    (struct mldv2_query *)mld, off, icmp6len) != 0)
 				return (0);
 		}
 		break;



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201908061711.x76HBHa6039007>