svn commit: r356489 - in stable/12/sys: netinet netinet6
Bjoern A. Zeeb
bz at FreeBSD.org
Wed Jan 8 15:41:05 UTC 2020
Author: bz
Date: Wed Jan 8 15:41:04 2020
New Revision: 356489
URL: https://svnweb.freebsd.org/changeset/base/356489
Log:
MFC r353635 (by hselasky):
Fix panic in network stack due to use after free when receiving
partial fragmented packets before a network interface is detached.
When sending IPv4 or IPv6 fragmented packets and a fragment is lost
before the network device is freed, the mbuf making up the fragment
will remain in the temporary hashed fragment list and cause a panic
when it times out due to accessing a freed network interface
structure.
1) Make sure the m_pkthdr.rcvif always points to a valid network
interface. Else the rcvif field should be set to NULL.
2) Use the rcvif of the last received fragment as m_pkthdr.rcvif for
the fully defragged packet, instead of the first received fragment.
Modified:
stable/12/sys/netinet/ip_reass.c
stable/12/sys/netinet6/frag6.c
Directory Properties:
stable/12/ (props changed)
Modified: stable/12/sys/netinet/ip_reass.c
==============================================================================
--- stable/12/sys/netinet/ip_reass.c Wed Jan 8 14:01:15 2020 (r356488)
+++ stable/12/sys/netinet/ip_reass.c Wed Jan 8 15:41:04 2020 (r356489)
@@ -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>
@@ -180,6 +183,7 @@ ip_reass(struct mbuf *m)
struct ip *ip;
struct mbuf *p, *q, *nq, *t;
struct ipq *fp;
+ struct ifnet *srcifp;
struct ipqhead *head;
int i, hlen, next, tmpmax;
u_int8_t ecn, ecn0;
@@ -240,6 +244,11 @@ ip_reass(struct mbuf *m)
}
/*
+ * Store receive network interface pointer for later.
+ */
+ srcifp = m->m_pkthdr.rcvif;
+
+ /*
* Attempt reassembly; if it succeeds, proceed.
* ip_reass() will return a different mbuf.
*/
@@ -489,8 +498,11 @@ ip_reass(struct mbuf *m)
m->m_len += (ip->ip_hl << 2);
m->m_data -= (ip->ip_hl << 2);
/* some debugging cruft by sklower, below, will go away soon */
- if (m->m_flags & M_PKTHDR) /* XXX this should be done elsewhere */
+ if (m->m_flags & M_PKTHDR) { /* XXX this should be done elsewhere */
m_fixhdr(m);
+ /* set valid receive interface pointer */
+ m->m_pkthdr.rcvif = srcifp;
+ }
IPSTAT_INC(ips_reassembled);
IPQ_UNLOCK(hash);
@@ -606,6 +618,43 @@ ipreass_drain(void)
}
}
+/*
+ * 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__));
+
+ /*
+ * Skip processing if IPv4 reassembly is not initialised or
+ * torn down by ipreass_destroy().
+ */
+ if (V_ipq_zone == NULL)
+ return;
+
+ 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) {
+ /* clear no longer valid rcvif pointer */
+ if (m->m_pkthdr.rcvif == ifp)
+ m->m_pkthdr.rcvif = NULL;
+ }
+ }
+ IPQ_UNLOCK(i);
+ }
+ CURVNET_RESTORE();
+}
+EVENTHANDLER_DEFINE(ifnet_departure_event, ipreass_cleanup, NULL, 0);
+
#ifdef VIMAGE
/*
* Destroy IP reassembly structures.
@@ -616,6 +665,7 @@ ipreass_destroy(void)
ipreass_drain();
uma_zdestroy(V_ipq_zone);
+ V_ipq_zone = NULL;
for (int i = 0; i < IPREASS_NHASH; i++)
mtx_destroy(&V_ipq[i].lock);
}
Modified: stable/12/sys/netinet6/frag6.c
==============================================================================
--- stable/12/sys/netinet6/frag6.c Wed Jan 8 14:01:15 2020 (r356488)
+++ stable/12/sys/netinet6/frag6.c Wed Jan 8 15:41:04 2020 (r356489)
@@ -234,7 +234,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 && m->m_pkthdr.rcvif != NULL) {
/* Adjust pointer. */
ip6 = mtod(m, struct ip6_hdr *);
@@ -260,6 +260,43 @@ frag6_freef(struct ip6q *q6, uint32_t bucket)
}
/*
+ * 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++) {
+ IP6QB_LOCK(i);
+ head = IP6QB_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);
+
+ /* clear no longer valid rcvif pointer */
+ if (m->m_pkthdr.rcvif == ifp)
+ m->m_pkthdr.rcvif = NULL;
+ }
+ }
+ IP6QB_UNLOCK(i);
+ }
+ CURVNET_RESTORE();
+}
+EVENTHANDLER_DEFINE(ifnet_departure_event, frag6_cleanup, NULL, 0);
+
+/*
* Like in RFC2460, in RFC8200, fragment and reassembly rules do not agree with
* each other, in terms of next header field handling in fragment header.
* While the sender will use the same value for all of the fragmented packets,
@@ -295,6 +332,7 @@ int
frag6_input(struct mbuf **mp, int *offp, int proto)
{
struct ifnet *dstifp;
+ struct ifnet *srcifp;
struct in6_ifaddr *ia6;
struct ip6_hdr *ip6;
struct ip6_frag *ip6f;
@@ -326,6 +364,11 @@ frag6_input(struct mbuf **mp, int *offp, int proto)
return (IPPROTO_DONE);
#endif
+ /*
+ * Store receive network interface pointer for later.
+ */
+ srcifp = m->m_pkthdr.rcvif;
+
dstifp = NULL;
/* Find the destination interface of the packet. */
ia6 = in6ifa_ifwithaddr(&ip6->ip6_dst, 0 /* XXX */);
@@ -523,6 +566,9 @@ frag6_input(struct mbuf **mp, int *offp, int proto)
frag6_deq(af6, bucket);
free(af6, M_FRAG6);
+ /* Set a valid receive interface pointer. */
+ merr->m_pkthdr.rcvif = srcifp;
+
/* Adjust pointer. */
ip6err = mtod(merr, struct ip6_hdr *);
@@ -709,6 +755,8 @@ insert:
for (t = m; t; t = t->m_next)
plen += t->m_len;
m->m_pkthdr.len = plen;
+ /* Set a valid receive interface pointer. */
+ m->m_pkthdr.rcvif = srcifp;
}
#ifdef RSS
More information about the svn-src-stable-12
mailing list