⬢ DragonFlyBSD Kernel Audit
DF-3450 / fix.diff
← back to finding ↓ download raw
--- a/sys/netinet/ip_encap.c
+++ b/sys/netinet/ip_encap.c
@@ -228,10 +228,23 @@
 	}
 
 	/* for backward compatibility */
-	if (proto == IPPROTO_IPV4 && ipip_input) {
-		*mp = m;
-		ipip_input(mp, offp, proto);
-		return(IPPROTO_DONE);
+	if (proto == IPPROTO_IPV4) {
+		/*
+		 * DF-3450: snapshot the hook ONCE.  The old
+		 * test-then-call re-read the global and could call
+		 * NULL after ip_mroute's MOD_UNLOAD cleared it between
+		 * the two reads.  With a single read the call target is
+		 * guaranteed still mapped: the unload path NULLs the
+		 * pointer, drains netisr (netmsg_service_sync), and
+		 * only then lets the linker unmap the module text.
+		 */
+		int (*ipip_fn)(struct mbuf **, int *, int) = ipip_input;
+
+		if (ipip_fn != NULL) {
+			*mp = m;
+			ipip_fn(mp, offp, proto);
+			return(IPPROTO_DONE);
+		}
 	}
 
 	/* last resort: inject to raw socket */
--- a/sys/net/ip_mroute/ip_mroute.c
+++ b/sys/net/ip_mroute/ip_mroute.c
@@ -3287,6 +3287,16 @@
 	mrt_ioctl = NULL;
 	rsvp_input_p = NULL;
 	lwkt_reltoken(&mroute_token);
+
+	/*
+	 * DF-3450: encap4_input() reads and calls the ipip_input hook
+	 * with no lock while running on netisr0.  The pointer clears
+	 * above under mroute_token, but any dispatch already past the
+	 * read (or already executing X_ipip_input) must finish before
+	 * the linker unmaps the module text.  Drain all netisr work --
+	 * the same discipline the dummynet module unload uses.
+	 */
+	netmsg_service_sync();
 	break;
     }
     return 0;