diff options
author | rstone <rstone@FreeBSD.org> | 2016-11-26 01:16:33 +0000 |
---|---|---|
committer | Luiz Otavio O Souza <luiz@netgate.com> | 2017-02-16 14:59:03 -0600 |
commit | 74fc727e9fd412f2db88b11ff1caccec4e00bd95 (patch) | |
tree | d1d5307159ea4a20c69b3fb179c357e2cfcae072 | |
parent | 42a5f2897e93d1e42833eac551c64c1373119ff9 (diff) | |
download | FreeBSD-src-74fc727e9fd412f2db88b11ff1caccec4e00bd95.zip FreeBSD-src-74fc727e9fd412f2db88b11ff1caccec4e00bd95.tar.gz |
MFC r308580:
Don't read if_counters with if_addr_lock held
Calling into an ifnet implementation with the if_addr_lock already
held can cause a LOR and potentially a deadlock, as ifnet
implementations typically can take the if_addr_lock after their
own locks during configuration. Refactor a sysctl handler that
was violating this to read if_counter data in a temporary buffer
before the if_addr_lock is taken, and then copying the data
in its final location later, when the if_addr_lock is held.
PR: 194109
Reported by: Jean-Sebastien Pedron
MFC after: 2 weeks
Differential Revision: https://reviews.freebsd.org/D8498
Reviewed by: sbruno
(cherry picked from commit 6622d4d3a9bcae4eefea44361d8b9d96d81a3073)
-rw-r--r-- | sys/net/rtsock.c | 21 |
1 files changed, 13 insertions, 8 deletions
diff --git a/sys/net/rtsock.c b/sys/net/rtsock.c index 91158b0..ca89002 100644 --- a/sys/net/rtsock.c +++ b/sys/net/rtsock.c @@ -1566,8 +1566,8 @@ sysctl_dumpentry(struct radix_node *rn, void *vw) } static int -sysctl_iflist_ifml(struct ifnet *ifp, struct rt_addrinfo *info, - struct walkarg *w, int len) +sysctl_iflist_ifml(struct ifnet *ifp, const struct if_data *src_ifd, + struct rt_addrinfo *info, struct walkarg *w, int len) { struct if_msghdrl *ifm; struct if_data *ifd; @@ -1598,14 +1598,14 @@ sysctl_iflist_ifml(struct ifnet *ifp, struct rt_addrinfo *info, ifd = &ifm->ifm_data; } - if_data_copy(ifp, ifd); + memcpy(ifd, src_ifd, sizeof(*ifd)); return (SYSCTL_OUT(w->w_req, (caddr_t)ifm, len)); } static int -sysctl_iflist_ifm(struct ifnet *ifp, struct rt_addrinfo *info, - struct walkarg *w, int len) +sysctl_iflist_ifm(struct ifnet *ifp, const struct if_data *src_ifd, + struct rt_addrinfo *info, struct walkarg *w, int len) { struct if_msghdr *ifm; struct if_data *ifd; @@ -1630,7 +1630,7 @@ sysctl_iflist_ifm(struct ifnet *ifp, struct rt_addrinfo *info, ifd = &ifm->ifm_data; } - if_data_copy(ifp, ifd); + memcpy(ifd, src_ifd, sizeof(*ifd)); return (SYSCTL_OUT(w->w_req, (caddr_t)ifm, len)); } @@ -1705,15 +1705,18 @@ sysctl_iflist(int af, struct walkarg *w) { struct ifnet *ifp; struct ifaddr *ifa; + struct if_data ifd; struct rt_addrinfo info; int len, error = 0; struct sockaddr_storage ss; bzero((caddr_t)&info, sizeof(info)); + bzero(&ifd, sizeof(ifd)); IFNET_RLOCK_NOSLEEP(); TAILQ_FOREACH(ifp, &V_ifnet, if_link) { if (w->w_arg && w->w_arg != ifp->if_index) continue; + if_data_copy(ifp, &ifd); IF_ADDR_RLOCK(ifp); ifa = ifp->if_addr; info.rti_info[RTAX_IFP] = ifa->ifa_addr; @@ -1723,9 +1726,11 @@ sysctl_iflist(int af, struct walkarg *w) info.rti_info[RTAX_IFP] = NULL; if (w->w_req && w->w_tmem) { if (w->w_op == NET_RT_IFLISTL) - error = sysctl_iflist_ifml(ifp, &info, w, len); + error = sysctl_iflist_ifml(ifp, &ifd, &info, w, + len); else - error = sysctl_iflist_ifm(ifp, &info, w, len); + error = sysctl_iflist_ifm(ifp, &ifd, &info, w, + len); if (error) goto done; } |