diff options
author | rwatson <rwatson@FreeBSD.org> | 2009-04-23 13:08:47 +0000 |
---|---|---|
committer | rwatson <rwatson@FreeBSD.org> | 2009-04-23 13:08:47 +0000 |
commit | 471539dc8f7f8952bce906c2f71708c614007fcb (patch) | |
tree | 07704a61b3ff63c25404fa737cb3b0b0ec78c6af /sys/net | |
parent | ccc05d4c7fc358ca3cc8339274b58835f1ba153b (diff) | |
download | FreeBSD-src-471539dc8f7f8952bce906c2f71708c614007fcb.zip FreeBSD-src-471539dc8f7f8952bce906c2f71708c614007fcb.tar.gz |
Add ifunit_ref(), a version of ifunit(), that returns not just an
interface pointer, but also a reference to it.
Modify ifioctl() to use ifunit_ref(), holding the reference until
all ioctls, etc, have completed.
This closes a class of reader-writer races in which interfaces
could be removed during long-running ioctls, leading to crashes.
Many other consumers of ifunit() should now use ifunit_ref() to
avoid similar races.
MFC after: 3 weeks
Diffstat (limited to 'sys/net')
-rw-r--r-- | sys/net/if.c | 34 | ||||
-rw-r--r-- | sys/net/if_var.h | 1 |
2 files changed, 29 insertions, 6 deletions
diff --git a/sys/net/if.c b/sys/net/if.c index f116f55..991f880 100644 --- a/sys/net/if.c +++ b/sys/net/if.c @@ -1788,10 +1788,27 @@ if_slowtimo(void *arg) } /* - * Map interface name to - * interface structure pointer. + * Map interface name to interface structure pointer, with or without + * returning a reference. */ struct ifnet * +ifunit_ref(const char *name) +{ + INIT_VNET_NET(curvnet); + struct ifnet *ifp; + + IFNET_RLOCK(); + TAILQ_FOREACH(ifp, &V_ifnet, if_link) { + if (strncmp(name, ifp->if_xname, IFNAMSIZ) == 0) + break; + } + if (ifp != NULL) + if_ref(ifp); + IFNET_RUNLOCK(); + return (ifp); +} + +struct ifnet * ifunit(const char *name) { INIT_VNET_NET(curvnet); @@ -2167,17 +2184,21 @@ ifioctl(struct socket *so, u_long cmd, caddr_t data, struct thread *td) return (if_getgroupmembers((struct ifgroupreq *)data)); } - ifp = ifunit(ifr->ifr_name); - if (ifp == 0) + ifp = ifunit_ref(ifr->ifr_name); + if (ifp == NULL) return (ENXIO); error = ifhwioctl(cmd, ifp, data, td); - if (error != ENOIOCTL) + if (error != ENOIOCTL) { + if_rele(ifp); return (error); + } oif_flags = ifp->if_flags; - if (so->so_proto == 0) + if (so->so_proto == NULL) { + if_rele(ifp); return (EOPNOTSUPP); + } #ifndef COMPAT_43 error = ((*so->so_proto->pr_usrreqs->pru_control)(so, cmd, data, @@ -2250,6 +2271,7 @@ ifioctl(struct socket *so, u_long cmd, caddr_t data, struct thread *td) } #endif } + if_rele(ifp); return (error); } diff --git a/sys/net/if_var.h b/sys/net/if_var.h index 4078f8f..eb4986e 100644 --- a/sys/net/if_var.h +++ b/sys/net/if_var.h @@ -776,6 +776,7 @@ void if_up(struct ifnet *); int ifioctl(struct socket *, u_long, caddr_t, struct thread *); int ifpromisc(struct ifnet *, int); struct ifnet *ifunit(const char *); +struct ifnet *ifunit_ref(const char *); void ifq_attach(struct ifaltq *, struct ifnet *ifp); void ifq_detach(struct ifaltq *); |