summaryrefslogtreecommitdiffstats
path: root/sys/net
diff options
context:
space:
mode:
authorrwatson <rwatson@FreeBSD.org>2009-04-23 13:08:47 +0000
committerrwatson <rwatson@FreeBSD.org>2009-04-23 13:08:47 +0000
commit471539dc8f7f8952bce906c2f71708c614007fcb (patch)
tree07704a61b3ff63c25404fa737cb3b0b0ec78c6af /sys/net
parentccc05d4c7fc358ca3cc8339274b58835f1ba153b (diff)
downloadFreeBSD-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.c34
-rw-r--r--sys/net/if_var.h1
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 *);
OpenPOWER on IntegriCloud