git: ec76d3f600b5 - main - nd6: Do not allocate an ifaddr for the interface ID in in6_ifadd()

From: R. Christian McDonald <rcm_at_FreeBSD.org>
Date: Thu, 08 Oct 2026 23:56:55 UTC
The branch main has been updated by rcm:

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

commit ec76d3f600b528df7c29d3305e44342f08789fbf
Author:     R. Christian McDonald <rcm@FreeBSD.org>
AuthorDate: 2026-10-08 23:08:19 +0000
Commit:     R. Christian McDonald <rcm@FreeBSD.org>
CommitDate: 2026-10-08 23:08:19 +0000

    nd6: Do not allocate an ifaddr for the interface ID in in6_ifadd()
    
    Without a suitable link-local address, in6_ifadd() obtains the
    interface identifier from in6_get_ifid().  It wrote the result into a
    freshly allocated struct in6_ifaddr, M_NOWAIT since the function runs
    in the network epoch, so SLAAC could fail under memory pressure over a
    16 byte scratch buffer.
    
    Write the identifier into the on-stack address the rest of the function
    uses instead.  Zero it first, in6_get_ifid() only fills in the low 64
    bits, as the M_ZERO allocation did.  Track which path provided the
    identifier with a flag instead of a pointer into the ifaddr, so each
    path releases its own reference.
    
    Reviewed by:            glebius
    Fixes:                  9e792f7ef729 ("sys/netinet6: Fix SLAAC for interfaces with no /64 LL address")
    Fixes:                  f9fc93690aef ("sys/netinet6: fix memory corruption in in6_ifadd")
    MFC after:              2 weeks
    Sponsored by:           Rubicon Communications, LLC ("Netgate")
    Differential Revision:  https://reviews.freebsd.org/D60488
---
 sys/netinet6/nd6_rtr.c | 50 +++++++++++++++++++++-----------------------------
 1 file changed, 21 insertions(+), 29 deletions(-)

diff --git a/sys/netinet6/nd6_rtr.c b/sys/netinet6/nd6_rtr.c
index 9d7cd3f832d9..ed31981ec9df 100644
--- a/sys/netinet6/nd6_rtr.c
+++ b/sys/netinet6/nd6_rtr.c
@@ -1215,7 +1215,7 @@ in6_ifadd(struct nd_prefixctl *pr, int mcast)
 	struct in6_aliasreq ifra;
 	struct in6_ifaddr *ia = NULL, *ib = NULL;
 	int error, plen0;
-	struct in6_addr *ifid_addr = NULL, mask, newaddr;
+	struct in6_addr mask, newaddr;
 	int prefixlen = pr->ndpr_plen;
 	int updateflags;
 	char ip6buf[INET6_ADDRSTRLEN];
@@ -1255,47 +1255,39 @@ in6_ifadd(struct nd_prefixctl *pr, int mcast)
 		if(!in6_get_stableifid(ifp, &newaddr, prefixlen))
 			return NULL;
 	} else {
+		bool have_ifid = false;
+
 		ifa = (struct ifaddr *)in6ifa_ifpforlinklocal(ifp, 0); /* 0 is OK? */
-		if (ifa) {
+		if (ifa != NULL) {
 			ib = (struct in6_ifaddr *)ifa;
-			ifid_addr = &ib->ia_addr.sin6_addr;
 
 			/* prefixlen + ifidlen must be equal to 128 */
 			plen0 = in6_mask2len(&ib->ia_prefixmask.sin6_addr, NULL);
-			if (prefixlen != plen0) {
-				ifa_free(ifa);
-				ifid_addr = NULL;
+			if (prefixlen == plen0) {
+				memcpy(&newaddr, &ib->ia_addr.sin6_addr,
+				    sizeof(newaddr));
+				have_ifid = true;
+			} else {
 				nd6log((LOG_DEBUG,
 				    "%s: wrong prefixlen for %s (prefix=%d ifid=%d)\n",
 				    __func__, if_name(ifp), prefixlen, 128 - plen0));
 			}
+			ifa_free(ifa);
 		}
 
-		/* No suitable LL address, get the ifid directly */
-		if (ifid_addr == NULL) {
-			ifa = ifa_alloc(sizeof(struct in6_ifaddr), M_NOWAIT);
-			if (ifa != NULL) {
-				ib = (struct in6_ifaddr *)ifa;
-				ifid_addr = &ib->ia_addr.sin6_addr;
-				if(in6_get_ifid(ifp, NULL, ifid_addr) != 0) {
-					nd6log((LOG_DEBUG,
-					    "%s: failed to get ifid for %s\n",
-					    __func__, if_name(ifp)));
-					ifa_free(ifa);
-					ifid_addr = NULL;
-				}
+		/*
+		 * No suitable link-local address, get the ifid directly.
+		 * Only the low 64 bits are filled in, zero the rest.
+		 */
+		if (!have_ifid) {
+			memset(&newaddr, 0, sizeof(newaddr));
+			if (in6_get_ifid(ifp, NULL, &newaddr) != 0) {
+				nd6log((LOG_INFO,
+				    "%s: could not determine ifid for %s\n",
+				    __func__, if_name(ifp)));
+				return (NULL);
 			}
 		}
-
-		if (ifid_addr == NULL) {
-			nd6log((LOG_INFO,
-			    "%s: could not determine ifid for %s\n",
-			    __func__, if_name(ifp)));
-			return NULL;
-		}
-
-		memcpy(&newaddr, &ib->ia_addr.sin6_addr, sizeof(ib->ia_addr.sin6_addr));
-		ifa_free(ifa);
 	}
 
 	IN6_MASK_ADDR(&ifra.ifra_addr.sin6_addr, &mask);