DF-3450 / fix.diff
--- 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; |