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
This commit is contained in:
Robert Watson 2009-06-27 11:05:53 +00:00
parent 395cbe82d2
commit f291b9cd38
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=195102

View File

@ -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);
}