git: 68004e56fdc2 - main - net: Fix handling of unmapped user pages in if_getgroup()

From: Mark Johnston <markj_at_FreeBSD.org>
Date: Mon, 01 Jun 2026 18:30:14 UTC
The branch main has been updated by markj:

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

commit 68004e56fdc22c11b4ec680e83309b4ea2bfe13a
Author:     Mark Johnston <markj@FreeBSD.org>
AuthorDate: 2026-06-01 16:44:15 +0000
Commit:     Mark Johnston <markj@FreeBSD.org>
CommitDate: 2026-06-01 18:22:57 +0000

    net: Fix handling of unmapped user pages in if_getgroup()
    
    We cannot call copyout() while in a net epoch section, unless the user
    memory is wired.  Use the global ifnet lock to synchronize the accesses
    instead.
    
    Reported by:    emaste
    Reviewed by:    zlei
    MFC after:      2 weeks
    Differential Revision:  https://reviews.freebsd.org/D57154
---
 sys/net/if.c         | 56 +++++++++++++++++++++-------------------------------
 sys/net/if_private.h |  2 +-
 2 files changed, 24 insertions(+), 34 deletions(-)

diff --git a/sys/net/if.c b/sys/net/if.c
index e421dd3bd494..440b40102d53 100644
--- a/sys/net/if.c
+++ b/sys/net/if.c
@@ -1375,11 +1375,8 @@ if_addgroup(struct ifnet *ifp, const char *groupname)
 	ifgl->ifgl_group = ifg;
 	ifgm->ifgm_ifp = ifp;
 
-	IF_ADDR_WLOCK(ifp);
 	CK_STAILQ_INSERT_TAIL(&ifg->ifg_members, ifgm, ifgm_next);
 	CK_STAILQ_INSERT_TAIL(&ifp->if_groups, ifgl, ifgl_next);
-	IF_ADDR_WUNLOCK(ifp);
-
 	IFNET_WUNLOCK();
 
 	if (new)
@@ -1402,9 +1399,7 @@ _if_delgroup_locked(struct ifnet *ifp, struct ifg_list *ifgl,
 
 	IFNET_WLOCK_ASSERT();
 
-	IF_ADDR_WLOCK(ifp);
 	CK_STAILQ_REMOVE(&ifp->if_groups, ifgl, ifg_list, ifgl_next);
-	IF_ADDR_WUNLOCK(ifp);
 
 	CK_STAILQ_FOREACH(ifgm, &ifgl->ifgl_group->ifg_members, ifgm_next) {
 		if (ifgm->ifgm_ifp == ifp) {
@@ -1479,34 +1474,35 @@ if_delgroups(struct ifnet *ifp)
 static int
 if_getgroup(struct ifgroupreq *ifgr, struct ifnet *ifp)
 {
-	int			 len, error;
-	struct ifg_list		*ifgl;
-	struct ifg_req		 ifgrq, *ifgp;
-
-	NET_EPOCH_ASSERT();
+	struct ifg_list *ifgl;
+	struct ifg_req ifgrq, *ifgp;
+	int len, error;
 
+	IFNET_RLOCK();
 	if (ifgr->ifgr_len == 0) {
 		CK_STAILQ_FOREACH(ifgl, &ifp->if_groups, ifgl_next)
 			ifgr->ifgr_len += sizeof(struct ifg_req);
-		return (0);
-	}
-
-	len = ifgr->ifgr_len;
-	ifgp = ifgr->ifgr_groups;
-	/* XXX: wire */
-	CK_STAILQ_FOREACH(ifgl, &ifp->if_groups, ifgl_next) {
-		if (len < sizeof(ifgrq))
-			return (EINVAL);
-		bzero(&ifgrq, sizeof ifgrq);
-		strlcpy(ifgrq.ifgrq_group, ifgl->ifgl_group->ifg_group,
-		    sizeof(ifgrq.ifgrq_group));
-		if ((error = copyout(&ifgrq, ifgp, sizeof(struct ifg_req))))
-			return (error);
-		len -= sizeof(ifgrq);
-		ifgp++;
+		error = 0;
+	} else {
+		len = ifgr->ifgr_len;
+		ifgp = ifgr->ifgr_groups;
+		CK_STAILQ_FOREACH(ifgl, &ifp->if_groups, ifgl_next) {
+			if (len < sizeof(ifgrq)) {
+				error = EINVAL;
+				break;
+			}
+			bzero(&ifgrq, sizeof ifgrq);
+			strlcpy(ifgrq.ifgrq_group, ifgl->ifgl_group->ifg_group,
+			    sizeof(ifgrq.ifgrq_group));
+			if ((error = copyout(&ifgrq, ifgp, sizeof(struct ifg_req))))
+				break;
+			len -= sizeof(ifgrq);
+			ifgp++;
+		}
 	}
+	IFNET_RUNLOCK();
 
-	return (0);
+	return (error);
 }
 
 /*
@@ -2756,14 +2752,8 @@ ifhwioctl(u_long cmd, struct ifnet *ifp, caddr_t data, struct thread *td)
 		break;
 	}
 	case SIOCGIFGROUP:
-	{
-		struct epoch_tracker et;
-
-		NET_EPOCH_ENTER(et);
 		error = if_getgroup((struct ifgroupreq *)data, ifp);
-		NET_EPOCH_EXIT(et);
 		break;
-	}
 
 	case SIOCDIFGROUP:
 	{
diff --git a/sys/net/if_private.h b/sys/net/if_private.h
index c4ade7308df4..5a76fd788c1b 100644
--- a/sys/net/if_private.h
+++ b/sys/net/if_private.h
@@ -41,7 +41,7 @@ struct ifnet {
 	CK_STAILQ_ENTRY(ifnet) if_link; 	/* all struct ifnets are chained (CK_) */
 	LIST_ENTRY(ifnet) if_clones;	/* interfaces of a cloner */
 	CK_STAILQ_HEAD(, ifg_list) if_groups; /* linked list of groups per if (CK_) */
-					/* protected by if_addr_lock */
+					/* protected by ifnet_sxlock */
 	u_char	if_alloctype;		/* if_type at time of allocation */
 	uint8_t	if_numa_domain;		/* NUMA domain of device */
 	/* Driver and protocol specific information that remains stable. */