On Sat, Sep 01, 2007 at 05:54:10PM -0400, Peter Avalos wrote:
> On Wed, Aug 29, 2007 at 09:50:27PM -0700, Matthew Dillon wrote:
> > 
> >     So we need to audit all the code that manipulates the in_ifaddrhead 
> >     list and check for structures that might get freed without being removed
> >     from the list.
> > 
> 
> I'm having no luck.  Got any pointers/ideas?
> 

Looking around FreeBSD source I saw this, and was wondering if it could
help?

http://www.theshell.com/cgi-bin/gitweb.cgi?p=freebsd-src.git;a=commitdiff;h=16f96b132d344c7682df05b3bf74278150a5b3b1

commit 16f96b132d344c7682df05b3bf74278150a5b3b1
Author: peadar <peadar>
Date:   Wed May 25 13:52:03 2005 +0000

    Separate out address-detaching part of if_detach into if_purgeaddrs,
    so if_tap doesn't need to rely on locally-rolled code to do same.
    
    The observable symptom of if_tap's bzero'ing the address details
    was a crash in "ifconfig tap0" after an if_tap device was closed.
    
    Reported By: Matti Saarinen (mjsaarin at cc dot helsinki dot fi)

diff --git a/sys/net/if.c b/sys/net/if.c
index 8aff428..f056329 100644
--- a/sys/net/if.c
+++ b/sys/net/if.c
@@ -27,7 +27,7 @@
  * SUCH DAMAGE.
  *
  *     @(#)if.c        8.5 (Berkeley) 1/9/95
- * $FreeBSD: src/sys/net/if.c,v 1.227 2005/04/20 09:30:54 glebius Exp $
+ * $FreeBSD: src/sys/net/if.c,v 1.228 2005/05/25 13:52:03 peadar Exp $
  */
 
 #include "opt_compat.h"
@@ -530,13 +530,52 @@ if_attachdomain1(struct ifnet *ifp)
 }
 
 /*
+ * Remove any network addresses from an interface.
+ */
+
+void
+if_purgeaddrs(struct ifnet *ifp)
+{
+       struct ifaddr *ifa, *next;
+
+       TAILQ_FOREACH_SAFE(ifa, &ifp->if_addrhead, ifa_link, next) {
+
+               if (ifa->ifa_addr->sa_family == AF_LINK)
+                       continue;
+#ifdef INET
+               /* XXX: Ugly!! ad hoc just for INET */
+               if (ifa->ifa_addr && ifa->ifa_addr->sa_family == AF_INET) {
+                       struct ifaliasreq ifr;
+
+                       bzero(&ifr, sizeof(ifr));
+                       ifr.ifra_addr = *ifa->ifa_addr;
+                       if (ifa->ifa_dstaddr)
+                               ifr.ifra_broadaddr = *ifa->ifa_dstaddr;
+                       if (in_control(NULL, SIOCDIFADDR, (caddr_t)&ifr, ifp,
+                           NULL) == 0)
+                               continue;
+               }
+#endif /* INET */
+#ifdef INET6
+               if (ifa->ifa_addr && ifa->ifa_addr->sa_family == AF_INET6) {
+                       in6_purgeaddr(ifa);
+                       /* ifp_addrhead is already updated */
+                       continue;
+               }
+#endif /* INET6 */
+               TAILQ_REMOVE(&ifp->if_addrhead, ifa, ifa_link);
+               IFAFREE(ifa);
+       }
+}
+
+/*
  * Detach an interface, removing it from the
  * list of "active" interfaces.
  */
 void
 if_detach(struct ifnet *ifp)
 {
-       struct ifaddr *ifa, *next;
+       struct ifaddr *ifa;
        struct radix_node_head  *rnh;
        int s;
        int i;
@@ -568,35 +607,7 @@ if_detach(struct ifnet *ifp)
                altq_detach(&ifp->if_snd);
 #endif
 
-       for (ifa = TAILQ_FIRST(&ifp->if_addrhead); ifa; ifa = next) {
-               next = TAILQ_NEXT(ifa, ifa_link);
-
-               if (ifa->ifa_addr->sa_family == AF_LINK)
-                       continue;
-#ifdef INET
-               /* XXX: Ugly!! ad hoc just for INET */
-               if (ifa->ifa_addr && ifa->ifa_addr->sa_family == AF_INET) {
-                       struct ifaliasreq ifr;
-
-                       bzero(&ifr, sizeof(ifr));
-                       ifr.ifra_addr = *ifa->ifa_addr;
-                       if (ifa->ifa_dstaddr)
-                               ifr.ifra_broadaddr = *ifa->ifa_dstaddr;
-                       if (in_control(NULL, SIOCDIFADDR, (caddr_t)&ifr, ifp,
-                           NULL) == 0)
-                               continue;
-               }
-#endif /* INET */
-#ifdef INET6
-               if (ifa->ifa_addr && ifa->ifa_addr->sa_family == AF_INET6) {
-                       in6_purgeaddr(ifa);
-                       /* ifp_addrhead is already updated */
-                       continue;
-               }
-#endif /* INET6 */
-               TAILQ_REMOVE(&ifp->if_addrhead, ifa, ifa_link);
-               IFAFREE(ifa);
-       }
+       if_purgeaddrs(ifp);
 
 #ifdef INET6
        /*
diff --git a/sys/net/if_tap.c b/sys/net/if_tap.c
index 232c96d..a807bd5 100644
--- a/sys/net/if_tap.c
+++ b/sys/net/if_tap.c
@@ -31,7 +31,7 @@
  */
 
 /*
- * $FreeBSD: src/sys/net/if_tap.c,v 1.53 2005/05/04 18:55:02 emax Exp $
+ * $FreeBSD: src/sys/net/if_tap.c,v 1.54 2005/05/25 13:52:03 peadar Exp $
  * $Id: if_tap.c,v 0.21 2000/07/23 21:46:02 max Exp $
  */
 
@@ -408,6 +408,7 @@ tapclose(dev, foo, bar, td)
        int              bar;
        struct thread   *td;
 {
+       struct ifaddr *ifa;
        struct tap_softc        *tp = dev->si_drv1;
        struct ifnet            *ifp = &tp->tap_if;
        int                     s;
@@ -426,24 +427,10 @@ tapclose(dev, foo, bar, td)
                s = splimp();
                if_down(ifp);
                if (ifp->if_flags & IFF_RUNNING) {
-                       /* find internet addresses and delete routes */
-                       struct ifaddr   *ifa = NULL;
-
-                       /* In desparate need of ifaddr locking. */
                        TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
-                               if (ifa->ifa_addr->sa_family == AF_INET) {
-                                       rtinit(ifa, (int)RTM_DELETE, 0);
-
-                                       /* remove address from interface */
-                                       bzero(ifa->ifa_addr,
-                                                  sizeof(*(ifa->ifa_addr)));
-                                       bzero(ifa->ifa_dstaddr,
-                                                  sizeof(*(ifa->ifa_dstaddr)));
-                                       bzero(ifa->ifa_netmask,
-                                                  sizeof(*(ifa->ifa_netmask)));
-                               }
+                               rtinit(ifa, (int)RTM_DELETE, 0);
                        }
-
+                       if_purgeaddrs(ifp);
                        ifp->if_flags &= ~IFF_RUNNING;
                }
                splx(s);
diff --git a/sys/net/if_var.h b/sys/net/if_var.h
index ab8d389..6d9cbed 100644
--- a/sys/net/if_var.h
+++ b/sys/net/if_var.h
@@ -27,7 +27,7 @@
  * SUCH DAMAGE.
  *
  *     From: @(#)if.h  8.1 (Berkeley) 6/10/93
- * $FreeBSD: src/sys/net/if_var.h,v 1.95 2005/04/20 09:30:54 glebius Exp $
+ * $FreeBSD: src/sys/net/if_var.h,v 1.96 2005/05/25 13:52:03 peadar Exp $
  */
 
 #ifndef        _NET_IF_VAR_H_
@@ -629,6 +629,7 @@ int if_allmulti(struct ifnet *, int);
 void   if_attach(struct ifnet *);
 int    if_delmulti(struct ifnet *, struct sockaddr *);
 void   if_detach(struct ifnet *);
+void   if_purgeaddrs(struct ifnet *);
 void   if_down(struct ifnet *);
 void   if_initname(struct ifnet *, const char *, int);
 void   if_link_state_change(struct ifnet *, int);

Attachment: pgpWcWWq7jp54.pgp
Description: PGP signature

Reply via email to