diff options
author | rwatson <rwatson@FreeBSD.org> | 2009-06-27 11:05:53 +0000 |
---|---|---|
committer | rwatson <rwatson@FreeBSD.org> | 2009-06-27 11:05:53 +0000 |
commit | 3b6551a921beb7f1408f05c3730aa5802bd6e79c (patch) | |
tree | f507e266469cd795237647e79f7ed84eb2c6afb7 /sys/netinet6 | |
parent | 5cfc09d074a45a6010e2cbbb541bd752a348d917 (diff) | |
download | FreeBSD-src-3b6551a921beb7f1408f05c3730aa5802bd6e79c.zip FreeBSD-src-3b6551a921beb7f1408f05c3730aa5802bd6e79c.tar.gz |
In in6_update_ifa(), jump to 'cleanup' rather than returning directly
in one additional case, avoiding an ifaddr reference leak.
Defer releasing the in6_ifaddr's in6_ifaddrhead reference until the
end of in6_unlink_ifa(), as callers are inconsistent regarding whether
or not they hold a reference across the call. This avoids using the
ifaddr after it may have been freed.
Reported by: tegge
Reviewed by: tegge
Approved by: re (blanket)
MFC after: 6 weeks
Diffstat (limited to 'sys/netinet6')
-rw-r--r-- | sys/netinet6/in6.c | 11 |
1 files changed, 7 insertions, 4 deletions
diff --git a/sys/netinet6/in6.c b/sys/netinet6/in6.c index f415877..645e107 100644 --- a/sys/netinet6/in6.c +++ b/sys/netinet6/in6.c @@ -970,8 +970,7 @@ in6_update_ifa(struct ifnet *ifp, struct in6_aliasreq *ifra, "%s on %s (errno=%d)\n", ip6_sprintf(ip6buf, &llsol), if_name(ifp), error)); - in6_purgeaddr((struct ifaddr *)ia); - return (error); + goto cleanup; } LIST_INSERT_HEAD(&ia->ia6_memberships, imm, i6mm_chain); @@ -1378,10 +1377,14 @@ in6_unlink_ifa(struct in6_ifaddr *ia, struct ifnet *ifp) IF_ADDR_UNLOCK(ifp); ifa_free(&ia->ia_ifa); /* if_addrhead */ + /* + * Defer the release of what might be the last reference to the + * in6_ifaddr so that it can't be freed before the remainder of the + * cleanup. + */ IN6_IFADDR_WLOCK(); TAILQ_REMOVE(&V_in6_ifaddrhead, ia, ia_link); IN6_IFADDR_WUNLOCK(); - ifa_free(&ia->ia_ifa); /* in6_ifaddrhead */ /* * Release the reference to the base prefix. There should be a @@ -1404,7 +1407,7 @@ in6_unlink_ifa(struct in6_ifaddr *ia, struct ifnet *ifp) if ((ia->ia6_flags & IN6_IFF_AUTOCONF)) { pfxlist_onlink_check(); } - + ifa_free(&ia->ia_ifa); /* in6_ifaddrhead */ splx(s); } |