Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 30 Jul 2026 18:11:52 +0000
From:      Pouria Mousavizadeh Tehrani <pouria@FreeBSD.org>
To:        src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org
Subject:   git: a0d2e5ebaa2e - main - if_gre(4): Fix races by changing initialization order and locks
Message-ID:  <6a6b93e8.3ddca.39012bf@gitrepo.freebsd.org>

index | next in thread | raw e-mail

The branch main has been updated by pouria:

URL: https://cgit.FreeBSD.org/src/commit/?id=a0d2e5ebaa2e7a8da83f593ed5459926423afa20

commit a0d2e5ebaa2e7a8da83f593ed5459926423afa20
Author:     Pouria Mousavizadeh Tehrani <pouria@FreeBSD.org>
AuthorDate: 2026-07-30 17:48:47 +0000
Commit:     Pouria Mousavizadeh Tehrani <pouria@FreeBSD.org>
CommitDate: 2026-07-30 17:51:11 +0000

    if_gre(4): Fix races by changing initialization order and locks
    
    Treat if_gre like any other network drivers during module
    initialization by using SI_SUB_PROTO_IF.
    Also, destroy cloned interfaces via a prison removal callback for
    gre over udp.
    
    PR:             275474
    Reviewed by:    markj
    Discussed with: glebius
    Differential Revision: https://reviews.freebsd.org/D57669
---
 sys/net/if_gre.c | 96 ++++++++++++++++++++++++++++++++++++--------------------
 sys/net/if_gre.h |  3 --
 2 files changed, 62 insertions(+), 37 deletions(-)

diff --git a/sys/net/if_gre.c b/sys/net/if_gre.c
index 563478eb67f8..1dc82238c6ab 100644
--- a/sys/net/if_gre.c
+++ b/sys/net/if_gre.c
@@ -40,11 +40,13 @@
 #include "opt_rss.h"
 
 #include <sys/param.h>
+#include <sys/jail.h>
 #include <sys/kernel.h>
 #include <sys/lock.h>
 #include <sys/malloc.h>
 #include <sys/module.h>
 #include <sys/mbuf.h>
+#include <sys/osd.h>
 #include <sys/priv.h>
 #include <sys/proc.h>
 #include <sys/socket.h>
@@ -208,14 +210,16 @@ vnet_gre_init(const void *unused __unused)
 	in6_gre_init();
 #endif
 }
-VNET_SYSINIT(vnet_gre_init, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY,
-    vnet_gre_init, NULL);
+VNET_SYSINIT(vnet_gre_init, SI_SUB_PROTO_IF, SI_ORDER_ANY, vnet_gre_init, NULL);
 
 static void
 vnet_gre_uninit(const void *unused __unused)
 {
 
-	ifc_detach_cloner(V_gre_cloner);
+	if (V_gre_cloner != NULL) {
+		ifc_detach_cloner(V_gre_cloner);
+		V_gre_cloner = NULL;
+	}
 #ifdef INET
 	in_gre_uninit();
 #endif
@@ -224,8 +228,8 @@ vnet_gre_uninit(const void *unused __unused)
 #endif
 	/* XXX: epoch_call drain */
 }
-VNET_SYSUNINIT(vnet_gre_uninit, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY,
-    vnet_gre_uninit, NULL);
+VNET_SYSUNINIT(vnet_gre_uninit, SI_SUB_PROTO_IF, SI_ORDER_ANY, vnet_gre_uninit,
+    NULL);
 
 static int
 gre_clone_create_nl(struct if_clone *ifc, char *name, size_t len,
@@ -248,7 +252,7 @@ gre_clone_modify_nl(struct ifnet *ifp, struct ifc_data_nl *ifd)
 	struct nl_parsed_gre params;
 	struct nlattr *attrs = lattrs->ifla_idata;
 	struct nlattr_bmask bm;
-	int error = 0;
+	int error;
 
 	if ((attrs == NULL) ||
 	    (nl_has_attr(ifd->bm, IFLA_LINKINFO) == 0)) {
@@ -267,6 +271,11 @@ gre_clone_modify_nl(struct ifnet *ifp, struct ifc_data_nl *ifd)
 	if ((error = nl_parse_nested(attrs, &gre_modify_parser, npt, &params)) != 0)
 		return (error);
 
+	sx_xlock(&gre_ioctl_sx);
+	sc = ifp->if_softc;
+	if (sc == NULL)
+		goto generic;
+
 	if (nl_has_attr(&bm, IFLA_GRE_LOCAL) && nl_has_attr(&bm, IFLA_GRE_REMOTE))
 		error = gre_set_addr_nl(sc, npt, params.ifla_local, params.ifla_remote);
 	else if (nl_has_attr(&bm, IFLA_GRE_LOCAL) || nl_has_attr(&bm, IFLA_GRE_REMOTE)) {
@@ -286,6 +295,9 @@ gre_clone_modify_nl(struct ifnet *ifp, struct ifc_data_nl *ifd)
 	if (error == 0 && nl_has_attr(&bm, IFLA_GRE_ENCAP_SPORT))
 		error = gre_set_udp_sport_nl(sc, npt, params.ifla_encap_sport);
 
+generic:
+	sx_xunlock(&gre_ioctl_sx);
+
 	if (error == 0)
 		error = nl_modify_ifp_generic(ifp, ifd->lattrs, ifd->bm, ifd->npt);
 
@@ -295,7 +307,6 @@ gre_clone_modify_nl(struct ifnet *ifp, struct ifc_data_nl *ifd)
 static void
 gre_clone_dump_nl(struct ifnet *ifp, struct nl_writer *nw)
 {
-	GRE_RLOCK_TRACKER;
 	struct gre_softc *sc;
 
 	nlattr_add_u32(nw, IFLA_LINK, ifp->if_index);
@@ -312,8 +323,10 @@ gre_clone_dump_nl(struct ifnet *ifp, struct nl_writer *nw)
 		return;
 	}
 
+	sx_slock(&gre_ioctl_sx);
 	sc = ifp->if_softc;
-	GRE_RLOCK();
+	if (sc == NULL)
+		goto ret;
 
 	if (sc->gre_family == AF_INET) {
 #ifdef INET
@@ -343,10 +356,11 @@ gre_clone_dump_nl(struct ifnet *ifp, struct nl_writer *nw)
 	    sc->gre_options & GRE_UDPENCAP ? IFLA_TUNNEL_GRE_UDP : IFLA_TUNNEL_NONE);
 	nlattr_add_u16(nw, IFLA_GRE_ENCAP_SPORT, sc->gre_port);
 
+ret:
 	nlattr_set_len(nw, off2);
 	nlattr_set_len(nw, off);
 
-	GRE_RUNLOCK();
+	sx_sunlock(&gre_ioctl_sx);
 }
 
 static int
@@ -391,6 +405,7 @@ gre_reassign(struct ifnet *ifp, struct vnet *new_vnet __unused,
 	if (sc != NULL)
 		gre_delete_tunnel(sc);
 	sx_xunlock(&gre_ioctl_sx);
+	if_link_state_change(ifp, LINK_STATE_DOWN);
 }
 #endif /* VIMAGE */
 
@@ -402,10 +417,11 @@ gre_clone_destroy(struct if_clone *ifc, struct ifnet *ifp, uint32_t flags)
 	sx_xlock(&gre_ioctl_sx);
 	sc = ifp->if_softc;
 	gre_delete_tunnel(sc);
-	bpfdetach(ifp);
-	if_detach(ifp);
 	ifp->if_softc = NULL;
 	sx_xunlock(&gre_ioctl_sx);
+	if_link_state_change(GRE2IFP(sc), LINK_STATE_DOWN);
+	bpfdetach(ifp);
+	if_detach(ifp);
 
 	GRE_WAIT();
 	if_free(ifp);
@@ -627,7 +643,7 @@ gre_delete_tunnel(struct gre_softc *sc)
 {
 	struct gre_socket *gs;
 
-	sx_assert(&gre_ioctl_sx, SA_XLOCKED);
+	GRE_LOCK_ASSERT();
 	if (sc->gre_family != 0) {
 		CK_LIST_REMOVE(sc, chain);
 		CK_LIST_REMOVE(sc, srchash);
@@ -646,7 +662,6 @@ gre_delete_tunnel(struct gre_softc *sc)
 		sc->gre_so = NULL;
 	}
 	GRE2IFP(sc)->if_drv_flags &= ~IFF_DRV_RUNNING;
-	if_link_state_change(GRE2IFP(sc), LINK_STATE_DOWN);
 }
 
 struct gre_list *
@@ -911,7 +926,6 @@ gre_flowid(struct gre_softc *sc, struct mbuf *m, uint32_t af)
 static int
 gre_transmit(struct ifnet *ifp, struct mbuf *m)
 {
-	GRE_RLOCK_TRACKER;
 	struct gre_softc *sc;
 	struct grehdr *gh;
 	struct udphdr *uh;
@@ -919,8 +933,8 @@ gre_transmit(struct ifnet *ifp, struct mbuf *m)
 	int error, len;
 	uint16_t proto;
 
+	NET_EPOCH_ASSERT();
 	len = 0;
-	GRE_RLOCK();
 #ifdef MAC
 	error = mac_ifnet_check_transmit(ifp, m);
 	if (error) {
@@ -1028,7 +1042,6 @@ drop:
 		if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1);
 		if_inc_counter(ifp, IFCOUNTER_OBYTES, len);
 	}
-	GRE_RUNLOCK();
 	return (error);
 }
 
@@ -1061,18 +1074,14 @@ gre_set_addr_nl(struct gre_softc *sc, struct nl_pstate *npt,
 	else if (src->sa_family == AF_INET) {
 		memcpy(&aliasreq.in.ifra_addr, src, sizeof(struct sockaddr_in));
 		memcpy(&aliasreq.in.ifra_dstaddr, dst, sizeof(struct sockaddr_in));
-		sx_xlock(&gre_ioctl_sx);
 		error = in_gre_ioctl(sc, SIOCSIFPHYADDR, (caddr_t)&aliasreq.in);
-		sx_xunlock(&gre_ioctl_sx);
 	}
 #endif
 #ifdef INET6
 	else if (src->sa_family == AF_INET6) {
 		memcpy(&aliasreq.in6.ifra_addr, src, sizeof(struct sockaddr_in6));
 		memcpy(&aliasreq.in6.ifra_dstaddr, dst, sizeof(struct sockaddr_in6));
-		sx_xlock(&gre_ioctl_sx);
 		error = in6_gre_ioctl(sc, SIOCSIFPHYADDR_IN6, (caddr_t)&aliasreq.in6);
-		sx_xunlock(&gre_ioctl_sx);
 	}
 #endif
 	else
@@ -1091,11 +1100,9 @@ gre_set_addr_nl(struct gre_softc *sc, struct nl_pstate *npt,
 static int
 gre_set_flags_nl(struct gre_softc *sc, struct nl_pstate *npt, uint32_t opt)
 {
-	int error = 0;
+	int error;
 
-	sx_xlock(&gre_ioctl_sx);
 	error = gre_set_flags(sc, opt);
-	sx_xunlock(&gre_ioctl_sx);
 
 	if (error == EINVAL)
 		nlmsg_report_err_msg(npt, "gre flags are invalid");
@@ -1106,11 +1113,9 @@ gre_set_flags_nl(struct gre_softc *sc, struct nl_pstate *npt, uint32_t opt)
 static int
 gre_set_key_nl(struct gre_softc *sc, struct nl_pstate *npt, uint32_t key)
 {
-	int error = 0;
+	int error;
 
-	sx_xlock(&gre_ioctl_sx);
 	error = gre_set_key(sc, key);
-	sx_xunlock(&gre_ioctl_sx);
 
 	if (error == EINVAL)
 		nlmsg_report_err_msg(npt, "gre key is invalid: %u", key);
@@ -1122,16 +1127,15 @@ static int
 gre_set_encap_nl(struct gre_softc *sc, struct nl_pstate *npt, uint32_t type)
 {
 	uint32_t opt;
-	int error = 0;
+	int error;
 
-	sx_xlock(&gre_ioctl_sx);
 	opt = sc->gre_options;
 	if (type & IFLA_TUNNEL_GRE_UDP)
 		opt |= GRE_UDPENCAP;
 	else
 		opt &= ~GRE_UDPENCAP;
+
 	error = gre_set_flags(sc, opt);
-	sx_xunlock(&gre_ioctl_sx);
 
 	if (error == EEXIST)
 		nlmsg_report_err_msg(npt, "same gre tunnel exist");
@@ -1143,11 +1147,9 @@ gre_set_encap_nl(struct gre_softc *sc, struct nl_pstate *npt, uint32_t type)
 static int
 gre_set_udp_sport_nl(struct gre_softc *sc, struct nl_pstate *npt, uint16_t port)
 {
-	int error = 0;
+	int error;
 
-	sx_xlock(&gre_ioctl_sx);
 	error = gre_set_udp_sport(sc, port);
-	sx_xunlock(&gre_ioctl_sx);
 
 	if (error == EINVAL)
 		nlmsg_report_err_msg(npt, "source port is invalid: %u", port);
@@ -1156,15 +1158,41 @@ gre_set_udp_sport_nl(struct gre_softc *sc, struct nl_pstate *npt, uint16_t port)
 }
 
 
+static int
+gre_prison_remove(void *obj, void *data __unused)
+{
+#ifdef VIMAGE
+	struct prison *pr;
+
+	pr = obj;
+	if (prison_owns_vnet(pr)) {
+		CURVNET_SET(pr->pr_vnet);
+		if (V_gre_cloner != NULL)
+			vnet_gre_uninit(NULL);
+		CURVNET_RESTORE();
+	}
+#endif
+	return (0);
+}
+
+
 static int
 gremodevent(module_t mod, int type, void *data)
 {
+	static int gre_osd_jail_slot;
 
 	switch (type) {
-	case MOD_LOAD:
+	case MOD_LOAD: {
+		osd_method_t methods[PR_MAXMETHOD] = {
+			[PR_METHOD_REMOVE] = gre_prison_remove,
+		};
+		gre_osd_jail_slot = osd_jail_register(NULL, methods);
 		NL_VERIFY_PARSERS(all_parsers);
 		break;
+	}
 	case MOD_UNLOAD:
+		if (gre_osd_jail_slot != 0)
+			osd_jail_deregister(gre_osd_jail_slot);
 		break;
 	default:
 		return (EOPNOTSUPP);
@@ -1178,5 +1206,5 @@ static moduledata_t gre_mod = {
 	0
 };
 
-DECLARE_MODULE(if_gre, gre_mod, SI_SUB_PSEUDO, SI_ORDER_ANY);
+DECLARE_MODULE(if_gre, gre_mod, SI_SUB_PROTO_IF, SI_ORDER_ANY);
 MODULE_VERSION(if_gre, 1);
diff --git a/sys/net/if_gre.h b/sys/net/if_gre.h
index ae6d18385dfb..61c8ef54523e 100644
--- a/sys/net/if_gre.h
+++ b/sys/net/if_gre.h
@@ -117,9 +117,6 @@ MALLOC_DECLARE(M_GRE);
 #endif
 
 #define	GRE2IFP(sc)		((sc)->gre_ifp)
-#define	GRE_RLOCK_TRACKER	struct epoch_tracker gre_et
-#define	GRE_RLOCK()		epoch_enter_preempt(net_epoch_preempt, &gre_et)
-#define	GRE_RUNLOCK()		epoch_exit_preempt(net_epoch_preempt, &gre_et)
 #define	GRE_WAIT()		epoch_wait_preempt(net_epoch_preempt)
 
 #define	gre_hdr			gre_uhdr.hdr


home | help

Want to link to this message? Use this
URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?6a6b93e8.3ddca.39012bf>