diff options
author | rwatson <rwatson@FreeBSD.org> | 2009-04-19 22:16:19 +0000 |
---|---|---|
committer | rwatson <rwatson@FreeBSD.org> | 2009-04-19 22:16:19 +0000 |
commit | 38c104950f32d836c5f70ed62933baf00b7cfeaf (patch) | |
tree | 5e9c214883179a99f30b2df323de46dcdbaef62f | |
parent | 80e4437a3b63e37efc7987db0e7f044546e89418 (diff) | |
download | FreeBSD-src-38c104950f32d836c5f70ed62933baf00b7cfeaf.zip FreeBSD-src-38c104950f32d836c5f70ed62933baf00b7cfeaf.tar.gz |
Protect against some writer-writer races in in_control() by acquiring
the interface address list lock around interface address list
modifications. More to do here.
MFC after: 2 weeks
-rw-r--r-- | sys/netinet/in.c | 10 |
1 files changed, 7 insertions, 3 deletions
diff --git a/sys/netinet/in.c b/sys/netinet/in.c index 1423e81..bb3973c 100644 --- a/sys/netinet/in.c +++ b/sys/netinet/in.c @@ -330,14 +330,12 @@ in_control(struct socket *so, u_long cmd, caddr_t data, struct ifnet *ifp, * Protect from ipintr() traversing address list * while we're modifying it. */ - s = splnet(); ifa = &ia->ia_ifa; IFA_LOCK_INIT(ifa); ifa->ifa_addr = (struct sockaddr *)&ia->ia_addr; ifa->ifa_dstaddr = (struct sockaddr *)&ia->ia_dstaddr; ifa->ifa_netmask = (struct sockaddr *)&ia->ia_sockmask; ifa->ifa_refcnt = 1; - TAILQ_INSERT_TAIL(&ifp->if_addrhead, ifa, ifa_link); ia->ia_sockmask.sin_len = 8; ia->ia_sockmask.sin_family = AF_INET; @@ -347,6 +345,10 @@ in_control(struct socket *so, u_long cmd, caddr_t data, struct ifnet *ifp, } ia->ia_ifp = ifp; + IF_ADDR_LOCK(ifp); + TAILQ_INSERT_TAIL(&ifp->if_addrhead, ifa, ifa_link); + IF_ADDR_UNLOCK(ifp); + s = splnet(); TAILQ_INSERT_TAIL(&V_in_ifaddrhead, ia, ia_link); splx(s); iaIsNew = 1; @@ -512,8 +514,10 @@ in_control(struct socket *so, u_long cmd, caddr_t data, struct ifnet *ifp, * Protect from ipintr() traversing address list while we're modifying * it. */ - s = splnet(); + IF_ADDR_LOCK(ifp); TAILQ_REMOVE(&ifp->if_addrhead, &ia->ia_ifa, ifa_link); + IF_ADDR_UNLOCK(ifp); + s = splnet(); TAILQ_REMOVE(&V_in_ifaddrhead, ia, ia_link); if (ia->ia_addr.sin_family == AF_INET) { LIST_REMOVE(ia, ia_hash); |