Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 03 Sep 2019 14:07:38 -0000
From:      Hans Petter Selasky <hselasky@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r346530 - in head/sys: netinet netinet6
Message-ID:  <201904220727.x3M7ROpR009729@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: hselasky
Date: Mon Apr 22 07:27:24 2019
New Revision: 346530
URL: https://svnweb.freebsd.org/changeset/base/346530

Log:
  Fix panic in network stack due to memory use after free in relation to
  fragmented packets.
  
  When sending IPv4 and IPv6 fragmented packets and a fragment is lost,
  the mbuf making up the fragment will remain in the temporary hashed
  fragment list for a while. If the network interface departs before the
  so-called slow timeout clears the packet, the fragment causes a panic
  when the timeout kicks in due to accessing a freed network interface
  structure.
  
  Make sure that when a network device is departing, all hashed IPv4 and
  IPv6 fragments belonging to it, get freed.
  
  Backtrace:
  panic()
  icmp6_reflect()
  
  hlim = ND_IFINFO(m->m_pkthdr.rcvif)->chlim;
  ^^^^ rcvif->if_afdata[AF_INET6] is NULL.
  
  icmp6_error()
  frag6_freef()
  frag6_slowtimo()
  pfslowtimo()
  softclock_call_cc()
  softclock()
  ithread_loop()
  
  Differential Revision:	https://reviews.freebsd.org/D19622
  Reviewed by:		bz (network), adrian
  MFC after:		1 week
  Sponsored by:		Mellanox Technologies

Modified:
  head/sys/netinet/ip_reass.c
  head/sys/netinet6/frag6.c

Modified: head/sys/netinet/ip_reass.c
==============================================================================
--- head/sys/netinet/ip_reass.c	Mon Apr 22 07:17:10 2019	(r346529)
+++ head/sys/netinet/ip_reass.c	Mon Apr 22 07:27:24 2019	(r346530)
@@ -46,7 +46,10 @@ __FBSDID("$FreeBSD$");
 #include <sys/lock.h>
 #include <sys/mutex.h>
 #include <sys/sysctl.h>
+#include <sys/socket.h>
 
+#include <net/if.h>
+#include <net/if_var.h>
 #include <net/rss_config.h>
 #include <net/netisr.h>
 #include <net/vnet.h>
@@ -605,6 +608,37 @@ ipreass_drain(void)
 		IPQ_UNLOCK(i);
 	}
 }
+
+/*
+ * Drain off all datagram fragments belonging to
+ * the given network interface.
+ */
+static void
+ipreass_cleanup(void *arg __unused, struct ifnet *ifp)
+{
+	struct ipq *fp, *temp;
+	struct mbuf *m;
+	int i;
+
+	KASSERT(ifp != NULL, ("%s: ifp is NULL", __func__));
+
+	CURVNET_SET_QUIET(ifp->if_vnet);
+	for (i = 0; i < IPREASS_NHASH; i++) {
+		IPQ_LOCK(i);
+		/* Scan fragment list. */
+		TAILQ_FOREACH_SAFE(fp, &V_ipq[i].head, ipq_list, temp) {
+			for (m = fp->ipq_frags; m != NULL; m = m->m_nextpkt) {
+				if (m->m_pkthdr.rcvif == ifp) {
+					ipq_drop(&V_ipq[i], fp);
+					break;
+				}
+			}
+		}
+		IPQ_UNLOCK(i);
+	}
+	CURVNET_RESTORE();
+}
+EVENTHANDLER_DEFINE(ifnet_departure_event, ipreass_cleanup, NULL, 0);
 
 #ifdef VIMAGE
 /*

Modified: head/sys/netinet6/frag6.c
==============================================================================
--- head/sys/netinet6/frag6.c	Mon Apr 22 07:17:10 2019	(r346529)
+++ head/sys/netinet6/frag6.c	Mon Apr 22 07:27:24 2019	(r346530)
@@ -81,7 +81,7 @@ static void frag6_deq(struct ip6asfrag *, uint32_t buc
 static void frag6_insque_head(struct ip6q *, struct ip6q *,
     uint32_t bucket);
 static void frag6_remque(struct ip6q *, uint32_t bucket);
-static void frag6_freef(struct ip6q *, uint32_t bucket);
+static void frag6_freef(struct ip6q *, uint32_t bucket, bool send_icmp);
 
 struct ip6qbucket {
 	struct ip6q	ip6q;
@@ -594,7 +594,7 @@ insert:
 		if (af6->ip6af_off != next) {
 			if (q6->ip6q_nfrag > V_ip6_maxfragsperpacket) {
 				IP6STAT_ADD(ip6s_fragdropped, q6->ip6q_nfrag);
-				frag6_freef(q6, hash);
+				frag6_freef(q6, hash, true);
 			}
 			IP6Q_UNLOCK(hash);
 			return IPPROTO_DONE;
@@ -604,7 +604,7 @@ insert:
 	if (af6->ip6af_up->ip6af_mff) {
 		if (q6->ip6q_nfrag > V_ip6_maxfragsperpacket) {
 			IP6STAT_ADD(ip6s_fragdropped, q6->ip6q_nfrag);
-			frag6_freef(q6, hash);
+			frag6_freef(q6, hash, true);
 		}
 		IP6Q_UNLOCK(hash);
 		return IPPROTO_DONE;
@@ -731,7 +731,7 @@ insert:
  * associated datagrams.
  */
 static void
-frag6_freef(struct ip6q *q6, uint32_t bucket)
+frag6_freef(struct ip6q *q6, uint32_t bucket, bool send_icmp)
 {
 	struct ip6asfrag *af6, *down6;
 
@@ -748,7 +748,7 @@ frag6_freef(struct ip6q *q6, uint32_t bucket)
 		 * Return ICMP time exceeded error for the 1st fragment.
 		 * Just free other fragments.
 		 */
-		if (af6->ip6af_off == 0) {
+		if (af6->ip6af_off == 0 && send_icmp != false) {
 			struct ip6_hdr *ip6;
 
 			/* adjust pointer */
@@ -864,7 +864,7 @@ frag6_slowtimo(void)
 					IP6STAT_ADD(ip6s_fragtimeout,
 						q6->ip6q_prev->ip6q_nfrag);
 					/* XXX in6_ifstat_inc(ifp, ifs6_reass_fail) */
-					frag6_freef(q6->ip6q_prev, i);
+					frag6_freef(q6->ip6q_prev, i, true);
 				}
 			}
 			/*
@@ -883,7 +883,7 @@ frag6_slowtimo(void)
 				IP6STAT_ADD(ip6s_fragoverflow,
 					q6->ip6q_prev->ip6q_nfrag);
 				/* XXX in6_ifstat_inc(ifp, ifs6_reass_fail) */
-				frag6_freef(head->ip6q_prev, i);
+				frag6_freef(head->ip6q_prev, i, true);
 			}
 			IP6Q_UNLOCK(i);
 		}
@@ -901,7 +901,7 @@ frag6_slowtimo(void)
 				IP6STAT_ADD(ip6s_fragoverflow,
 					q6->ip6q_prev->ip6q_nfrag);
 				/* XXX in6_ifstat_inc(ifp, ifs6_reass_fail) */
-				frag6_freef(head->ip6q_prev, i);
+				frag6_freef(head->ip6q_prev, i, true);
 			}
 			IP6Q_UNLOCK(i);
 			i = (i + 1) % IP6REASS_NHASH;
@@ -931,7 +931,7 @@ frag6_drain(void)
 			while (head->ip6q_next != head) {
 				IP6STAT_INC(ip6s_fragdropped);
 				/* XXX in6_ifstat_inc(ifp, ifs6_reass_fail) */
-				frag6_freef(head->ip6q_next, i);
+				frag6_freef(head->ip6q_next, i, true);
 			}
 			IP6Q_UNLOCK(i);
 		}
@@ -939,6 +939,45 @@ frag6_drain(void)
 	}
 	VNET_LIST_RUNLOCK_NOSLEEP();
 }
+
+/*
+ * Drain off all datagram fragments belonging to
+ * the given network interface.
+ */
+static void
+frag6_cleanup(void *arg __unused, struct ifnet *ifp)
+{
+	struct ip6q *q6, *q6n, *head;
+	struct ip6asfrag *af6;
+	struct mbuf *m;
+	int i;
+
+	KASSERT(ifp != NULL, ("%s: ifp is NULL", __func__));
+
+	CURVNET_SET_QUIET(ifp->if_vnet);
+	for (i = 0; i < IP6REASS_NHASH; i++) {
+		IP6Q_LOCK(i);
+		head = IP6Q_HEAD(i);
+		/* Scan fragment list. */
+		for (q6 = head->ip6q_next; q6 != head; q6 = q6n) {
+			q6n = q6->ip6q_next;
+
+			for (af6 = q6->ip6q_down; af6 != (struct ip6asfrag *)q6;
+			     af6 = af6->ip6af_down) {
+				m = IP6_REASS_MBUF(af6);
+
+				if (m->m_pkthdr.rcvif == ifp) {
+					IP6STAT_INC(ip6s_fragdropped);
+					frag6_freef(q6, i, false);
+					break;
+				}
+			}
+		}
+		IP6Q_UNLOCK(i);
+	}
+	CURVNET_RESTORE();
+}
+EVENTHANDLER_DEFINE(ifnet_departure_event, frag6_cleanup, NULL, 0);
 
 int
 ip6_deletefraghdr(struct mbuf *m, int offset, int wait)





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