diff options
author | Ryan Stone <rstone@FreeBSD.org> | 2021-02-11 16:17:58 +0000 |
---|---|---|
committer | Ryan Stone <rstone@FreeBSD.org> | 2022-01-06 20:04:24 +0000 |
commit | 5adea417d494b9c0e186cf2d06f98873d02d1834 (patch) | |
tree | 6c4a10726aef176f6319bbb8d64c3ac65b6c0945 /sys/net | |
parent | 32a95656b51ebefcdf3e0b02c110825f59abd26f (diff) | |
download | src-5adea417d494b9c0e186cf2d06f98873d02d1834.tar.gz src-5adea417d494b9c0e186cf2d06f98873d02d1834.zip |
Fix ifa refcount leak in ifa_ifwithnet()
In 4f6c66cc9c75c8, ifa_ifwithnet() was changed to no longer
ifa_ref() the returned ifaddr, and instead the caller was required
to stay in the net_epoch for as long as they wanted the ifaddr
to remain valid. However, this missed the case where an AF_LINK
lookup would call ifaddr_byindex(), which still does ifa_ref()
the ifaddr. This would cause a refcount leak.
Fix this by inlining the relevant parts of ifaddr_byindex() here,
with the ifa_ref() call removed. This also avoids an unnecessary
entry and exit from the net_epoch for this case.
I've audited all in-tree consumers of ifa_ifwithnet() that could
possibly perform an AF_LINK lookup and confirmed that none of them
will expect the ifaddr to have a reference that they need to
release.
MFC after: 2 months
Sponsored by: Dell Inc
Differential Revision: https://reviews.freebsd.org/D28705
Reviewed by: melifaro
Diffstat (limited to 'sys/net')
-rw-r--r-- | sys/net/if.c | 12 |
1 files changed, 9 insertions, 3 deletions
diff --git a/sys/net/if.c b/sys/net/if.c index 84436c0074c8..8700485ff6a5 100644 --- a/sys/net/if.c +++ b/sys/net/if.c @@ -1953,6 +1953,7 @@ ifa_ifwithnet(const struct sockaddr *addr, int ignore_ptp, int fibnum) struct ifaddr *ifa_maybe = NULL; u_int af = addr->sa_family; const char *addr_data = addr->sa_data, *cplim; + const struct sockaddr_dl *sdl; NET_EPOCH_ASSERT(); /* @@ -1960,9 +1961,14 @@ ifa_ifwithnet(const struct sockaddr *addr, int ignore_ptp, int fibnum) * so do that if we can. */ if (af == AF_LINK) { - const struct sockaddr_dl *sdl = (const struct sockaddr_dl *)addr; - if (sdl->sdl_index && sdl->sdl_index <= V_if_index) - return (ifaddr_byindex(sdl->sdl_index)); + sdl = (const struct sockaddr_dl *)addr; + if (sdl->sdl_index && sdl->sdl_index <= V_if_index) { + ifp = ifnet_byindex(sdl->sdl_index); + if (ifp == NULL) + return (NULL); + + return (ifp->if_addr); + } } /* |