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
This commit is contained in:
Robert Watson 2009-04-23 13:08:47 +00:00
parent 111c6b617b
commit 6064c5d362
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=191423
2 changed files with 29 additions and 6 deletions

View File

@ -1788,9 +1788,26 @@ 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)
{
@ -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);
}

View File

@ -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 *);