Date: Thu, 23 Apr 2009 13:08:48 +0000 (UTC) From: Robert Watson <rwatson@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r191423 - head/sys/net Message-ID: <200904231308.n3ND8mpl088009@svn.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: rwatson Date: Thu Apr 23 13:08:47 2009 New Revision: 191423 URL: http://svn.freebsd.org/changeset/base/191423 Log: 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 Modified: head/sys/net/if.c head/sys/net/if_var.h Modified: head/sys/net/if.c ============================================================================== --- head/sys/net/if.c Thu Apr 23 12:09:49 2009 (r191422) +++ head/sys/net/if.c Thu Apr 23 13:08:47 2009 (r191423) @@ -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, c 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, c } #endif } + if_rele(ifp); return (error); } Modified: head/sys/net/if_var.h ============================================================================== --- head/sys/net/if_var.h Thu Apr 23 12:09:49 2009 (r191422) +++ head/sys/net/if_var.h Thu Apr 23 13:08:47 2009 (r191423) @@ -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 *);
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200904231308.n3ND8mpl088009>