Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 6 Apr 2011 17:56:19 +0000 (UTC)
From:      Gleb Smirnoff <glebius@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-8@freebsd.org
Subject:   svn commit: r220393 - stable/8/sys/netinet
Message-ID:  <201104061756.p36HuJVd008071@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: glebius
Date: Wed Apr  6 17:56:18 2011
New Revision: 220393
URL: http://svn.freebsd.org/changeset/base/220393

Log:
  MFhead 215790:
  
    Redo r166423. It is important not only skip freeing multicast
    entires when underlying interface is detached, but also purge
    pointers to them, to avoid double-free in future.

Modified:
  stable/8/sys/netinet/ip_carp.c

Modified: stable/8/sys/netinet/ip_carp.c
==============================================================================
--- stable/8/sys/netinet/ip_carp.c	Wed Apr  6 17:54:12 2011	(r220392)
+++ stable/8/sys/netinet/ip_carp.c	Wed Apr  6 17:56:18 2011	(r220393)
@@ -218,7 +218,7 @@ static void	carp_set_state(struct carp_s
 static int	carp_addrcount(struct carp_if *, struct in_ifaddr *, int);
 enum	{ CARP_COUNT_MASTER, CARP_COUNT_RUNNING };
 
-static void	carp_multicast_cleanup(struct carp_softc *);
+static void	carp_multicast_cleanup(struct carp_softc *, int dofree);
 static int	carp_set_addr(struct carp_softc *, struct sockaddr_in *);
 static int	carp_del_addr(struct carp_softc *, struct sockaddr_in *);
 static void	carp_carpdev_state_locked(struct carp_if *);
@@ -227,7 +227,7 @@ static void	carp_sc_state_locked(struct 
 static void	carp_send_na(struct carp_softc *);
 static int	carp_set_addr6(struct carp_softc *, struct sockaddr_in6 *);
 static int	carp_del_addr6(struct carp_softc *, struct sockaddr_in6 *);
-static void	carp_multicast6_cleanup(struct carp_softc *);
+static void	carp_multicast6_cleanup(struct carp_softc *, int dofree);
 #endif
 
 static LIST_HEAD(, carp_softc) carpif_list;
@@ -466,9 +466,11 @@ carp_clone_destroy(struct ifnet *ifp)
 /*
  * This function can be called on CARP interface destroy path,
  * and in case of the removal of the underlying interface as
- * well. We differentiate these two cases. In the latter case
- * we do not cleanup our multicast memberships, since they
- * are already freed. Also, in the latter case we do not
+ * well. We differentiate these two cases: in case of destruction
+ * of the underlying interface, we do not cleanup our multicast
+ * memberships, since they are already freed. But we purge pointers
+ * to multicast structures, since they are no longer valid, to
+ * avoid panic in future calls to carpdetach(). Also, we do not
  * release the lock on return, because the function will be
  * called once more, for another CARP instance on the same
  * interface.
@@ -493,10 +495,9 @@ carpdetach(struct carp_softc *sc, int un
 	carp_set_state(sc, INIT);
 	SC2IFP(sc)->if_flags &= ~IFF_UP;
 	carp_setrun(sc, 0);
-	if (unlock)
-		carp_multicast_cleanup(sc);
+	carp_multicast_cleanup(sc, unlock);
 #ifdef INET6
-	carp_multicast6_cleanup(sc);
+	carp_multicast6_cleanup(sc, unlock);
 #endif
 
 	if (sc->sc_carpdev != NULL) {
@@ -1444,7 +1445,7 @@ carp_setrun(struct carp_softc *sc, sa_fa
 }
 
 static void
-carp_multicast_cleanup(struct carp_softc *sc)
+carp_multicast_cleanup(struct carp_softc *sc, int dofree)
 {
 	struct ip_moptions *imo = &sc->sc_imo;
 	u_int16_t n = imo->imo_num_memberships;
@@ -1452,7 +1453,8 @@ carp_multicast_cleanup(struct carp_softc
 	/* Clean up our own multicast memberships */
 	while (n-- > 0) {
 		if (imo->imo_membership[n] != NULL) {
-			in_delmulti(imo->imo_membership[n]);
+			if (dofree)
+				in_delmulti(imo->imo_membership[n]);
 			imo->imo_membership[n] = NULL;
 		}
 	}
@@ -1464,14 +1466,15 @@ carp_multicast_cleanup(struct carp_softc
 
 #ifdef INET6
 static void
-carp_multicast6_cleanup(struct carp_softc *sc)
+carp_multicast6_cleanup(struct carp_softc *sc, int dofree)
 {
 	struct ip6_moptions *im6o = &sc->sc_im6o;
 	u_int16_t n = im6o->im6o_num_memberships;
 
 	while (n-- > 0) {
 		if (im6o->im6o_membership[n] != NULL) {
-			in6_mc_leave(im6o->im6o_membership[n], NULL);
+			if (dofree)
+				in6_mc_leave(im6o->im6o_membership[n], NULL);
 			im6o->im6o_membership[n] = NULL;
 		}
 	}
@@ -1831,7 +1834,7 @@ carp_set_addr6(struct carp_softc *sc, st
 
 cleanup:
 	if (!sc->sc_naddrs6)
-		carp_multicast6_cleanup(sc);
+		carp_multicast6_cleanup(sc, 1);
 	ifa_free(&ia->ia_ifa);
 	return (error);
 }
@@ -1849,7 +1852,7 @@ carp_del_addr6(struct carp_softc *sc, st
 		SC2IFP(sc)->if_flags &= ~IFF_UP;
 		SC2IFP(sc)->if_drv_flags &= ~IFF_DRV_RUNNING;
 		sc->sc_vhid = -1;
-		carp_multicast6_cleanup(sc);
+		carp_multicast6_cleanup(sc, 1);
 		TAILQ_REMOVE(&cif->vhif_vrs, sc, sc_list);
 		if (!--cif->vhif_nvrs) {
 			CARP_LOCK_DESTROY(cif);



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201104061756.p36HuJVd008071>