Skip site navigation (1)Skip section navigation (2)
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>