DragonFlyBSD Kernel Audit
DF-2541 / fix.diff
← back to finding ↓ download raw
diff --git a/sys/net/ip_mroute/ip_mroute.c b/sys/net/ip_mroute/ip_mroute.c
@@ -1713,7 +1713,16 @@
     struct ip *ip = mtod(m, struct ip *);
     int hlen = ip->ip_hl << 2;
 
+    /*
+     * Serialize against del_vif()/X_ip_mrouter_done(), which bzero() viftable
+     * entries and reset the last_encap_vif/last_encap_src cache under this
+     * token.  Without the token a concurrent teardown can zero the cached vif
+     * between the validation walk and the last_encap_vif->v_ifp dereference
+     * below, producing a NULL function-pointer call in ip_input (DF-2541).
+     */
+    lwkt_gettoken(&mroute_token);
     if (!have_encap_tunnel) {
+	lwkt_reltoken(&mroute_token);
 	rip_input(mp, offp, proto);
 	return(IPPROTO_DONE);
     }
@@ -1729,6 +1738,7 @@
     if (!IN_MULTICAST(ntohl(((struct ip *)((char *)ip+hlen))->ip_dst.s_addr))) {
 	++mrtstat.mrts_bad_tunnel;
 	m_freem(m);
+	lwkt_reltoken(&mroute_token);
 	return(IPPROTO_DONE);
     }
     if (ip->ip_src.s_addr != last_encap_src) {
@@ -1752,6 +1762,7 @@
 	if (mrtdebug)
 	    log(LOG_DEBUG, "ip_mforward: no tunnel with %lx\n",
 		(u_long)ntohl(ip->ip_src.s_addr));
+	lwkt_reltoken(&mroute_token);
 	return(IPPROTO_DONE);
     }
 
@@ -1763,6 +1774,7 @@
     m->m_pkthdr.rcvif = last_encap_vif->v_ifp;
 
     netisr_queue(NETISR_IP, m);
+    lwkt_reltoken(&mroute_token);
     return(IPPROTO_DONE);
 }