⬢ DragonFlyBSD Kernel Audit
DF-3298 / fix.diff
← back to finding ↓ download raw
--- a/sys/netinet/ip_carp.c
+++ b/sys/netinet/ip_carp.c
@@ -719,6 +719,16 @@
 	bpfdetach(ifp);
 	if_detach(ifp);
 
+	/*
+	 * The carp(4) ifnet is embedded in the softc that is about to be
+	 * freed below.  Purge any IP fragments still sitting in the
+	 * reassembly queues that were received through this ifnet: the
+	 * fragment mbufs carry m_pkthdr.rcvif pointing at this ifnet and
+	 * a datagram completing reassembly after the free would dispatch
+	 * into carp_proto_input() with a dangling rcvif (use-after-free).
+	 */
+	ip_reass_purge_ifnet(ifp);
+
 	KASSERT(sc->sc_naddrs == 0, ("certain inet address is still active"));
 	kfree(sc, M_CARP);
 
--- a/sys/netinet/ip_input.c
+++ b/sys/netinet/ip_input.c
@@ -309,6 +309,7 @@
 static void		ipfrag_timeo_dispatch(netmsg_t);
 static void		ipfrag_timeo(void *);
 static void		ipfrag_drain_dispatch(netmsg_t);
+static void		ip_reass_purge_ifnet_dispatch(netmsg_t);
 
 /*
  * IP initialization: fill in IP protocol switch table.
@@ -1513,6 +1514,64 @@
 	in_rtqdrain();
 }
 
+struct netmsg_purge_ifnet {
+	struct netmsg_base	base;
+	struct ifnet		*ifp;
+};
+
+static void
+ip_reass_purge_ifnet_dispatch(netmsg_t nmsg)
+{
+	struct netmsg_purge_ifnet *msg = (struct netmsg_purge_ifnet *)nmsg;
+	struct ifnet *ifp = msg->ifp;
+	struct ipfrag_queue *fragq = &ipfrag_queue_pcpu[mycpuid];
+	struct ipq *fp, *fpnext;
+	struct mbuf *m;
+	int i;
+
+	for (i = 0; i < IPREASS_NHASH; i++) {
+		TAILQ_FOREACH_MUTABLE(fp, &fragq->ipq[i], ipq_list, fpnext) {
+			for (m = fp->ipq_frags; m != NULL; m = m->m_nextpkt) {
+				if (m->m_pkthdr.rcvif == ifp) {
+					ipstat.ips_fragdropped +=
+					    fp->ipq_nfrags;
+					ip_freef(fragq, &fragq->ipq[i], fp);
+					break;
+				}
+			}
+		}
+	}
+
+	crit_enter();
+	lwkt_replymsg(&nmsg->lmsg, 0);
+	crit_exit();
+}
+
+/*
+ * Synchronously purge, on all protocol threads, every IP reassembly
+ * queue holding fragments that were received via the given ifnet.
+ *
+ * This must be called before an ifnet whose softc embeds it (e.g. a
+ * carp(4) interface) is freed: fragment mbufs reference the ifnet via
+ * m_pkthdr.rcvif, and a datagram whose reassembly completes after the
+ * free would be dispatched up the transport stack with a dangling
+ * rcvif, causing a use-after-free in the protocol input path
+ * (e.g. carp_proto_input()).
+ */
+void
+ip_reass_purge_ifnet(struct ifnet *ifp)
+{
+	struct netmsg_purge_ifnet msg;
+	int cpu;
+
+	for (cpu = 0; cpu < netisr_ncpus; ++cpu) {
+		netmsg_init(&msg.base, NULL, &curthread->td_msgport, 0,
+			    ip_reass_purge_ifnet_dispatch);
+		msg.ifp = ifp;
+		netisr_domsg(&msg.base, cpu);
+	}
+}
+
 /*
  * Do option processing on a datagram,
  * possibly discarding it if bad options are encountered,
--- a/sys/netinet/ip_var.h
+++ b/sys/netinet/ip_var.h
@@ -186,6 +186,7 @@
 
 void	 ip_ctloutput(union netmsg *);
 void	 ip_drain(void);
+void	 ip_reass_purge_ifnet(struct ifnet *);
 int	 ip_fragment(struct ip *ip, struct mbuf **m_frag, int mtu,
 	    u_long if_hwassist_flags, int sw_csum);
 struct mbuf *