=1781032611; a=rsa-sha256; cv=none; b=mNXi4HA3ouFYNCVg8B0qMnXoyvZTtkRfS+c16MBrEO/5b+hbRCkSMG1/gPUQbwcDC2gyUa KkRRt6MOl2IPGGanTDekW8afVAtLsx/Mt9ncjHd9j+JnYfLhWSfgp/SttsxPhsu/01qOtr yjIq0OndycWnYoU21og6LCu3VubW+CEB0ctFlBbgN2+C21Eo1eHGBXvJrI1G1wz7fa9jiZ dRhArWmHU4oCinZLZEUqaZWdNrHZ3PMPuD3Lrd7kV/zEOWjkAHdGOzhdzyNENxp8u8nA6r 0CBDt5qoQRC1gdCyOLs80JoLVUIV0PF9Yzn1F18RwrSvfhHDLC+XDAMvaWxsKQ== ARC-Authentication-Results: i=1; mx1.freebsd.org; none ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1781032611; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=Ta5FepWvmIimlGC8VCQkI9CbxV8ahnI9+asnf3SxRns=; b=I4CeyBOj3Cp+C9IeGD25/RzI4wLcMjUyXVeQp2OeuGy4DM9zjf0VnniFGtpu8mQf3Yc0xF RPHDmC9+lzxKYkVkkdl7SLLptn5BI+Ed6Wfi+8KqKgePk3uQFXogEq0Okx8NRZRuy84akU MRIIThHkYa1dB290sqHIIggb+hDnVoYI6PgL+JvGA3Kk/UYDk49W0tnA5ZDgFzKdaliaxY qOse3fO5xLo6SuU4UXAmFxJYJCJaIqPWVt3HrveC7Nd5tlqpAzk3JwvS7a0kkzyJQCZUR5 YUtz+e4KzC45PCTfLTXmI9kbfNAICjaheGUsDdkwkiKispvV7MqFtM2UzIJCKg== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) by mxrelay.nyi.freebsd.org (Postfix) with ESMTP id 4gZdv66gWlznvh for ; Tue, 09 Jun 2026 19:16:50 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from git (uid 1279) (envelope-from git@FreeBSD.org) id 3ea6a by gitrepo.freebsd.org (DragonFly Mail Agent v0.13+ on gitrepo.freebsd.org); Tue, 09 Jun 2026 19:16:50 +0000 To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org From: Mark Johnston Subject: git: 1bac7df1baeb - main - in6_mcast: Fix a race in in6p_set_source_filter() List-Id: Commit messages for all branches of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-all List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-BeenThere: dev-commits-src-all@freebsd.org Sender: owner-dev-commits-src-all@FreeBSD.org List-Id: List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: markj X-Git-Repository: src X-Git-Refname: refs/heads/main X-Git-Reftype: branch X-Git-Commit: 1bac7df1baebd233d31d086a973a48df18f99e73 Auto-Submitted: auto-generated Date: Tue, 09 Jun 2026 19:16:50 +0000 Message-Id: <6a2866a2.3ea6a.722e7f09@gitrepo.freebsd.org> The branch main has been updated by markj: URL: https://cgit.FreeBSD.org/src/commit/?id=1bac7df1baebd233d31d086a973a48df18f99e73 commit 1bac7df1baebd233d31d086a973a48df18f99e73 Author: Mark Johnston AuthorDate: 2026-05-29 20:12:24 +0000 Commit: Mark Johnston CommitDate: 2026-06-09 19:13:21 +0000 in6_mcast: Fix a race in in6p_set_source_filter() We drop the inpcb lock in order to copy in the source list, but this leaves a window where the multicast filter structure might be freed. This can be exploited to obtain root privileges. In the v4 code this race is mitigated by holding the global multicast lock across the gap. Restructure the code to copy in filters before doing anything else, so that there's no need to drop the inpcb lock and reason about the correctness of doing so. Do the same in the v4 code for consistency. Approved by: so Security: FreeBSD-SA-26:29.ip6_multicast Security: CVE-2026-49412 Reported by: Andrew Griffiths Reported by: Maik Münch Reviewed by: glebius Sponsored by: The FreeBSD Foundation Differential Revision: https://reviews.freebsd.org/D57347 --- sys/netinet/in_mcast.c | 40 +++++++++++++++++----------------------- sys/netinet6/in6_mcast.c | 41 +++++++++++++++++++---------------------- 2 files changed, 36 insertions(+), 45 deletions(-) diff --git a/sys/netinet/in_mcast.c b/sys/netinet/in_mcast.c index 1e7985ac01d7..340218cf5397 100644 --- a/sys/netinet/in_mcast.c +++ b/sys/netinet/in_mcast.c @@ -2505,6 +2505,7 @@ inp_set_source_filters(struct inpcb *inp, struct sockopt *sopt) { struct epoch_tracker et; struct __msfilterreq msfr; + struct sockaddr_storage *kss; sockunion_t *gsa; struct ifnet *ifp; struct in_mfilter *imf; @@ -2517,9 +2518,6 @@ inp_set_source_filters(struct inpcb *inp, struct sockopt *sopt) if (error) return (error); - if (msfr.msfr_nsrcs > in_mcast_maxsocksrc) - return (ENOBUFS); - if ((msfr.msfr_fmode != MCAST_EXCLUDE && msfr.msfr_fmode != MCAST_INCLUDE)) return (EINVAL); @@ -2532,13 +2530,24 @@ inp_set_source_filters(struct inpcb *inp, struct sockopt *sopt) if (!IN_MULTICAST(ntohl(gsa->sin.sin_addr.s_addr))) return (EINVAL); + if (msfr.msfr_nsrcs > in_mcast_maxsocksrc) + return (ENOBUFS); + kss = mallocarray(msfr.msfr_nsrcs, sizeof(struct sockaddr_storage), + M_TEMP, M_WAITOK); + error = copyin(msfr.msfr_srcs, kss, + sizeof(struct sockaddr_storage) * msfr.msfr_nsrcs); + if (error) + goto out_inp_unlocked; + gsa->sin.sin_port = 0; /* ignore port */ NET_EPOCH_ENTER(et); ifp = ifnet_byindex(msfr.msfr_ifindex); NET_EPOCH_EXIT(et); /* XXXGL: unsafe ifp */ - if (ifp == NULL) - return (EADDRNOTAVAIL); + if (ifp == NULL) { + error = EADDRNOTAVAIL; + goto out_inp_unlocked; + } IN_MULTI_LOCK(); @@ -2570,25 +2579,9 @@ inp_set_source_filters(struct inpcb *inp, struct sockopt *sopt) if (msfr.msfr_nsrcs > 0) { struct in_msource *lims; struct sockaddr_in *psin; - struct sockaddr_storage *kss, *pkss; + struct sockaddr_storage *pkss; int i; - INP_WUNLOCK(inp); - - CTR2(KTR_IGMPV3, "%s: loading %lu source list entries", - __func__, (unsigned long)msfr.msfr_nsrcs); - kss = malloc(sizeof(struct sockaddr_storage) * msfr.msfr_nsrcs, - M_TEMP, M_WAITOK); - error = copyin(msfr.msfr_srcs, kss, - sizeof(struct sockaddr_storage) * msfr.msfr_nsrcs); - if (error) { - IN_MULTI_UNLOCK(); - free(kss, M_TEMP); - return (error); - } - - INP_WLOCK(inp); - /* * Mark all source filters as UNDEFINED at t1. * Restore new group filter mode, as imf_leave() @@ -2623,7 +2616,6 @@ inp_set_source_filters(struct inpcb *inp, struct sockopt *sopt) break; lims->imsl_st[1] = imf->imf_st[1]; } - free(kss, M_TEMP); } if (error) @@ -2660,6 +2652,8 @@ out_imf_rollback: out_inp_locked: INP_WUNLOCK(inp); IN_MULTI_UNLOCK(); +out_inp_unlocked: + free(kss, M_TEMP); return (error); } diff --git a/sys/netinet6/in6_mcast.c b/sys/netinet6/in6_mcast.c index 7942faefd568..fad47cb0e69b 100644 --- a/sys/netinet6/in6_mcast.c +++ b/sys/netinet6/in6_mcast.c @@ -2491,6 +2491,7 @@ in6p_set_source_filters(struct inpcb *inp, struct sockopt *sopt) { struct __msfilterreq msfr; struct epoch_tracker et; + struct sockaddr_storage *kss; sockunion_t *gsa; struct ifnet *ifp; struct in6_mfilter *imf; @@ -2503,9 +2504,6 @@ in6p_set_source_filters(struct inpcb *inp, struct sockopt *sopt) if (error) return (error); - if (msfr.msfr_nsrcs > in6_mcast_maxsocksrc) - return (ENOBUFS); - if (msfr.msfr_fmode != MCAST_EXCLUDE && msfr.msfr_fmode != MCAST_INCLUDE) return (EINVAL); @@ -2518,19 +2516,31 @@ in6p_set_source_filters(struct inpcb *inp, struct sockopt *sopt) if (!IN6_IS_ADDR_MULTICAST(&gsa->sin6.sin6_addr)) return (EINVAL); + if (msfr.msfr_nsrcs > in6_mcast_maxsocksrc) + return (ENOBUFS); + kss = mallocarray(msfr.msfr_nsrcs, sizeof(struct sockaddr_storage), + M_TEMP, M_WAITOK); + error = copyin(msfr.msfr_srcs, kss, + sizeof(struct sockaddr_storage) * msfr.msfr_nsrcs); + if (error) + goto out_in6p_unlocked; + gsa->sin6.sin6_port = 0; /* ignore port */ NET_EPOCH_ENTER(et); ifp = ifnet_byindex(msfr.msfr_ifindex); NET_EPOCH_EXIT(et); - if (ifp == NULL) - return (EADDRNOTAVAIL); + if (ifp == NULL) { + error = EADDRNOTAVAIL; + goto out_in6p_unlocked; + } (void)in6_setscope(&gsa->sin6.sin6_addr, ifp, NULL); /* * Take the INP write lock. * Check if this socket is a member of this group. */ + IN6_MULTI_LOCK(); imo = in6p_findmoptions(inp); imf = im6o_match_group(imo, ifp, &gsa->sa); if (imf == NULL) { @@ -2555,24 +2565,9 @@ in6p_set_source_filters(struct inpcb *inp, struct sockopt *sopt) if (msfr.msfr_nsrcs > 0) { struct in6_msource *lims; struct sockaddr_in6 *psin; - struct sockaddr_storage *kss, *pkss; + struct sockaddr_storage *pkss; int i; - INP_WUNLOCK(inp); - - CTR2(KTR_MLD, "%s: loading %lu source list entries", - __func__, (unsigned long)msfr.msfr_nsrcs); - kss = malloc(sizeof(struct sockaddr_storage) * msfr.msfr_nsrcs, - M_TEMP, M_WAITOK); - error = copyin(msfr.msfr_srcs, kss, - sizeof(struct sockaddr_storage) * msfr.msfr_nsrcs); - if (error) { - free(kss, M_TEMP); - return (error); - } - - INP_WLOCK(inp); - /* * Mark all source filters as UNDEFINED at t1. * Restore new group filter mode, as im6f_leave() @@ -2617,7 +2612,6 @@ in6p_set_source_filters(struct inpcb *inp, struct sockopt *sopt) break; lims->im6sl_st[1] = imf->im6f_st[1]; } - free(kss, M_TEMP); } if (error) @@ -2652,6 +2646,9 @@ out_im6f_rollback: out_in6p_locked: INP_WUNLOCK(inp); + IN6_MULTI_UNLOCK(); +out_in6p_unlocked: + free(kss, M_TEMP); return (error); }