mirror of
https://github.com/freebsd/freebsd-src.git
synced 2026-11-07 04:36:52 -10:00
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:9e792f7ef7("sys/netinet6: Fix SLAAC for interfaces with no /64 LL address") Fixes:f9fc93690a("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
This commit is contained in:
1 file changed
+21
-29
+21
-29
@@ -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);
|
||||
|
||||
Reference in new issue
Block a user