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 *);
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to