Module Name:    src
Committed By:   ozaki-r
Date:           Fri Dec 15 04:03:46 UTC 2017

Modified Files:
        src/sys/net: if.c if.h if_vlan.c
        src/sys/netinet: in.c in_pcb.c ip_output.c
        src/sys/netinet6: in6.c in6_pcb.c ip6_output.c nd6.c nd6_rtr.c

Log Message:
Ensure to call if_mcast_op with holding IFNET_LOCK

Note that CARP doesn't deal with IFNET_LOCK yet.


To generate a diff of this commit:
cvs rdiff -u -r1.414 -r1.415 src/sys/net/if.c
cvs rdiff -u -r1.253 -r1.254 src/sys/net/if.h
cvs rdiff -u -r1.119 -r1.120 src/sys/net/if_vlan.c
cvs rdiff -u -r1.210 -r1.211 src/sys/netinet/in.c
cvs rdiff -u -r1.179 -r1.180 src/sys/netinet/in_pcb.c
cvs rdiff -u -r1.286 -r1.287 src/sys/netinet/ip_output.c
cvs rdiff -u -r1.254 -r1.255 src/sys/netinet6/in6.c
cvs rdiff -u -r1.161 -r1.162 src/sys/netinet6/in6_pcb.c
cvs rdiff -u -r1.195 -r1.196 src/sys/netinet6/ip6_output.c
cvs rdiff -u -r1.239 -r1.240 src/sys/netinet6/nd6.c
cvs rdiff -u -r1.135 -r1.136 src/sys/netinet6/nd6_rtr.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/net/if.c
diff -u src/sys/net/if.c:1.414 src/sys/net/if.c:1.415
--- src/sys/net/if.c:1.414	Thu Dec 14 05:46:54 2017
+++ src/sys/net/if.c	Fri Dec 15 04:03:46 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: if.c,v 1.414 2017/12/14 05:46:54 ozaki-r Exp $	*/
+/*	$NetBSD: if.c,v 1.415 2017/12/15 04:03:46 ozaki-r Exp $	*/
 
 /*-
  * Copyright (c) 1999, 2000, 2001, 2008 The NetBSD Foundation, Inc.
@@ -90,7 +90,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: if.c,v 1.414 2017/12/14 05:46:54 ozaki-r Exp $");
+__KERNEL_RCSID(0, "$NetBSD: if.c,v 1.415 2017/12/15 04:03:46 ozaki-r Exp $");
 
 #if defined(_KERNEL_OPT)
 #include "opt_inet.h"
@@ -2721,12 +2721,27 @@ if_put(const struct ifnet *ifp, struct p
 	psref_release(psref, &ifp->if_psref, ifnet_psref_class);
 }
 
+/*
+ * Return ifp having idx. Return NULL if not found.  Normally if_byindex
+ * should be used.
+ */
+ifnet_t *
+_if_byindex(u_int idx)
+{
+
+	return (__predict_true(idx < if_indexlim)) ? ifindex2ifnet[idx] : NULL;
+}
+
+/*
+ * Return ifp having idx. Return NULL if not found or the found ifp is
+ * already deactivated.
+ */
 ifnet_t *
 if_byindex(u_int idx)
 {
 	ifnet_t *ifp;
 
-	ifp = (__predict_true(idx < if_indexlim)) ? ifindex2ifnet[idx] : NULL;
+	ifp = _if_byindex(idx);
 	if (ifp != NULL && if_is_deactivated(ifp))
 		ifp = NULL;
 	return ifp;
@@ -3570,6 +3585,10 @@ if_mcast_op(ifnet_t *ifp, const unsigned
 	int rc;
 	struct ifreq ifr;
 
+	/* CARP still doesn't deal with the lock yet */
+#if !defined(NCARP) || (NCARP == 0)
+	KASSERT(IFNET_LOCKED(ifp));
+#endif
 	if (ifp->if_mcastop != NULL)
 		rc = (*ifp->if_mcastop)(ifp, cmd, sa);
 	else {

Index: src/sys/net/if.h
diff -u src/sys/net/if.h:1.253 src/sys/net/if.h:1.254
--- src/sys/net/if.h:1.253	Mon Dec 11 03:29:20 2017
+++ src/sys/net/if.h	Fri Dec 15 04:03:46 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: if.h,v 1.253 2017/12/11 03:29:20 ozaki-r Exp $	*/
+/*	$NetBSD: if.h,v 1.254 2017/12/15 04:03:46 ozaki-r Exp $	*/
 
 /*-
  * Copyright (c) 1999, 2000, 2001 The NetBSD Foundation, Inc.
@@ -1026,6 +1026,7 @@ int	if_clone_list(int, char *, int *);
 struct	ifnet *ifunit(const char *);
 struct	ifnet *if_get(const char *, struct psref *);
 ifnet_t *if_byindex(u_int);
+ifnet_t *_if_byindex(u_int);
 ifnet_t *if_get_byindex(u_int, struct psref *);
 ifnet_t *if_get_bylla(const void *, unsigned char, struct psref *);
 void	if_put(const struct ifnet *, struct psref *);

Index: src/sys/net/if_vlan.c
diff -u src/sys/net/if_vlan.c:1.119 src/sys/net/if_vlan.c:1.120
--- src/sys/net/if_vlan.c:1.119	Mon Dec 11 03:29:20 2017
+++ src/sys/net/if_vlan.c	Fri Dec 15 04:03:46 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: if_vlan.c,v 1.119 2017/12/11 03:29:20 ozaki-r Exp $	*/
+/*	$NetBSD: if_vlan.c,v 1.120 2017/12/15 04:03:46 ozaki-r Exp $	*/
 
 /*-
  * Copyright (c) 2000, 2001 The NetBSD Foundation, Inc.
@@ -78,7 +78,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: if_vlan.c,v 1.119 2017/12/11 03:29:20 ozaki-r Exp $");
+__KERNEL_RCSID(0, "$NetBSD: if_vlan.c,v 1.120 2017/12/15 04:03:46 ozaki-r Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_inet.h"
@@ -1160,7 +1160,9 @@ vlan_ether_addmulti(struct ifvlan *ifv, 
 	mib = ifv->ifv_mib;
 
 	KERNEL_LOCK_UNLESS_IFP_MPSAFE(mib->ifvm_p);
+	IFNET_LOCK(mib->ifvm_p);
 	error = if_mcast_op(mib->ifvm_p, SIOCADDMULTI, sa);
+	IFNET_UNLOCK(mib->ifvm_p);
 	KERNEL_UNLOCK_UNLESS_IFP_MPSAFE(mib->ifvm_p);
 
 	if (error != 0)
@@ -1201,7 +1203,9 @@ vlan_ether_delmulti(struct ifvlan *ifv, 
 
 	/* We no longer use this multicast address.  Tell parent so. */
 	mib = ifv->ifv_mib;
+	IFNET_LOCK(mib->ifvm_p);
 	error = if_mcast_op(mib->ifvm_p, SIOCDELMULTI, sa);
+	IFNET_UNLOCK(mib->ifvm_p);
 
 	if (error == 0) {
 		/* And forget about this address. */
@@ -1236,8 +1240,10 @@ vlan_ether_purgemulti(struct ifvlan *ifv
 	}
 
 	while ((mc = LIST_FIRST(&ifv->ifv_mc_listhead)) != NULL) {
+		IFNET_LOCK(mib->ifvm_p);
 		(void)if_mcast_op(mib->ifvm_p, SIOCDELMULTI,
 		    (const struct sockaddr *)&mc->mc_addr);
+		IFNET_UNLOCK(mib->ifvm_p);
 		LIST_REMOVE(mc, mc_entries);
 		free(mc, M_DEVBUF);
 	}

Index: src/sys/netinet/in.c
diff -u src/sys/netinet/in.c:1.210 src/sys/netinet/in.c:1.211
--- src/sys/netinet/in.c:1.210	Fri Nov 17 07:37:12 2017
+++ src/sys/netinet/in.c	Fri Dec 15 04:03:46 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: in.c,v 1.210 2017/11/17 07:37:12 ozaki-r Exp $	*/
+/*	$NetBSD: in.c,v 1.211 2017/12/15 04:03:46 ozaki-r Exp $	*/
 
 /*
  * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
@@ -91,7 +91,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: in.c,v 1.210 2017/11/17 07:37:12 ozaki-r Exp $");
+__KERNEL_RCSID(0, "$NetBSD: in.c,v 1.211 2017/12/15 04:03:46 ozaki-r Exp $");
 
 #include "arp.h"
 
@@ -912,11 +912,14 @@ in_addrhash_remove(struct in_ifaddr *ia)
 void
 in_purgeif(struct ifnet *ifp)		/* MUST be called at splsoftnet() */
 {
+
+	IFNET_LOCK(ifp);
 	if_purgeaddrs(ifp, AF_INET, in_purgeaddr);
 	igmp_purgeif(ifp);		/* manipulates pools */
 #ifdef MROUTING
 	ip_mrouter_detach(ifp);
 #endif
+	IFNET_UNLOCK(ifp);
 }
 
 /*

Index: src/sys/netinet/in_pcb.c
diff -u src/sys/netinet/in_pcb.c:1.179 src/sys/netinet/in_pcb.c:1.180
--- src/sys/netinet/in_pcb.c:1.179	Thu Aug 10 04:31:58 2017
+++ src/sys/netinet/in_pcb.c	Fri Dec 15 04:03:46 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: in_pcb.c,v 1.179 2017/08/10 04:31:58 ryo Exp $	*/
+/*	$NetBSD: in_pcb.c,v 1.180 2017/12/15 04:03:46 ozaki-r Exp $	*/
 
 /*
  * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
@@ -93,7 +93,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: in_pcb.c,v 1.179 2017/08/10 04:31:58 ryo Exp $");
+__KERNEL_RCSID(0, "$NetBSD: in_pcb.c,v 1.180 2017/12/15 04:03:46 ozaki-r Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_inet.h"
@@ -777,7 +777,10 @@ in_pcbpurgeif0(struct inpcbtable *table,
 			need_unlock = true;
 		}
 
+		/* IFNET_LOCK must be taken after solock */
+		IFNET_LOCK(ifp);
 		in_purgeifmcast(inp->inp_moptions, ifp);
+		IFNET_UNLOCK(ifp);
 
 		if (need_unlock)
 			inp_unlock(inp);

Index: src/sys/netinet/ip_output.c
diff -u src/sys/netinet/ip_output.c:1.286 src/sys/netinet/ip_output.c:1.287
--- src/sys/netinet/ip_output.c:1.286	Mon Dec 11 05:47:18 2017
+++ src/sys/netinet/ip_output.c	Fri Dec 15 04:03:46 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: ip_output.c,v 1.286 2017/12/11 05:47:18 ryo Exp $	*/
+/*	$NetBSD: ip_output.c,v 1.287 2017/12/15 04:03:46 ozaki-r Exp $	*/
 
 /*
  * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
@@ -91,7 +91,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: ip_output.c,v 1.286 2017/12/11 05:47:18 ryo Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ip_output.c,v 1.287 2017/12/15 04:03:46 ozaki-r Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_inet.h"
@@ -1771,7 +1771,10 @@ ip_add_membership(struct ip_moptions *im
 	 * Everything looks good; add a new record to the multicast
 	 * address list for the given interface.
 	 */
-	if ((imo->imo_membership[i] = in_addmulti(&ia, ifp)) == NULL) {
+	IFNET_LOCK(ifp);
+	imo->imo_membership[i] = in_addmulti(&ia, ifp);
+	IFNET_UNLOCK(ifp);
+	if (imo->imo_membership[i] == NULL) {
 		error = ENOBUFS;
 		goto out;
 	}
@@ -1830,7 +1833,9 @@ ip_drop_membership(struct ip_moptions *i
 	 * Give up the multicast address record to which the
 	 * membership points.
 	 */
+	IFNET_LOCK(ifp);
 	in_delmulti(imo->imo_membership[i]);
+	IFNET_UNLOCK(ifp);
 
 	/*
 	 * Remove the gap in the membership array.
@@ -2023,8 +2028,15 @@ ip_freemoptions(struct ip_moptions *imo)
 	/* The owner of imo (inp) should be protected by solock */
 
 	if (imo != NULL) {
-		for (i = 0; i < imo->imo_num_memberships; ++i)
-			in_delmulti(imo->imo_membership[i]);
+		for (i = 0; i < imo->imo_num_memberships; ++i) {
+			struct in_multi *inm = imo->imo_membership[i];
+			struct ifnet *ifp = inm->inm_ifp;
+			IFNET_LOCK(ifp);
+			in_delmulti(inm);
+			/* ifp should not leave thanks to solock */
+			IFNET_UNLOCK(ifp);
+		}
+
 		kmem_intr_free(imo, sizeof(*imo));
 	}
 }

Index: src/sys/netinet6/in6.c
diff -u src/sys/netinet6/in6.c:1.254 src/sys/netinet6/in6.c:1.255
--- src/sys/netinet6/in6.c:1.254	Thu Nov 23 07:09:20 2017
+++ src/sys/netinet6/in6.c	Fri Dec 15 04:03:46 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: in6.c,v 1.254 2017/11/23 07:09:20 ozaki-r Exp $	*/
+/*	$NetBSD: in6.c,v 1.255 2017/12/15 04:03:46 ozaki-r Exp $	*/
 /*	$KAME: in6.c,v 1.198 2001/07/18 09:12:38 itojun Exp $	*/
 
 /*
@@ -62,7 +62,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: in6.c,v 1.254 2017/11/23 07:09:20 ozaki-r Exp $");
+__KERNEL_RCSID(0, "$NetBSD: in6.c,v 1.255 2017/12/15 04:03:46 ozaki-r Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_inet.h"
@@ -1385,6 +1385,7 @@ in6_purgeaddr(struct ifaddr *ifa)
 	struct in6_multi_mship *imm;
 
 	KASSERT(!ifa_held(ifa));
+	KASSERT(IFNET_LOCKED(ifp));
 
 	ifa->ifa_flags |= IFA_DESTROYING;
 
@@ -1400,12 +1401,14 @@ in6_purgeaddr(struct ifaddr *ifa)
 	/*
 	 * leave from multicast groups we have joined for the interface
 	 */
+    again:
 	mutex_enter(&in6_ifaddr_lock);
 	while ((imm = LIST_FIRST(&ia->ia6_memberships)) != NULL) {
 		LIST_REMOVE(imm, i6mm_chain);
 		mutex_exit(&in6_ifaddr_lock);
+		KASSERT(imm->i6mm_maddr->in6m_ifp == ifp);
 		in6_leavegroup(imm);
-		mutex_enter(&in6_ifaddr_lock);
+		goto again;
 	}
 	mutex_exit(&in6_ifaddr_lock);
 
@@ -1456,7 +1459,9 @@ void
 in6_purgeif(struct ifnet *ifp)
 {
 
+	IFNET_LOCK(ifp);
 	in6_ifdetach(ifp);
+	IFNET_UNLOCK(ifp);
 }
 
 void

Index: src/sys/netinet6/in6_pcb.c
diff -u src/sys/netinet6/in6_pcb.c:1.161 src/sys/netinet6/in6_pcb.c:1.162
--- src/sys/netinet6/in6_pcb.c:1.161	Tue Apr 25 05:44:11 2017
+++ src/sys/netinet6/in6_pcb.c	Fri Dec 15 04:03:46 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: in6_pcb.c,v 1.161 2017/04/25 05:44:11 ozaki-r Exp $	*/
+/*	$NetBSD: in6_pcb.c,v 1.162 2017/12/15 04:03:46 ozaki-r Exp $	*/
 /*	$KAME: in6_pcb.c,v 1.84 2001/02/08 18:02:08 itojun Exp $	*/
 
 /*
@@ -62,7 +62,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: in6_pcb.c,v 1.161 2017/04/25 05:44:11 ozaki-r Exp $");
+__KERNEL_RCSID(0, "$NetBSD: in6_pcb.c,v 1.162 2017/12/15 04:03:46 ozaki-r Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_inet.h"
@@ -875,11 +875,18 @@ in6_pcbpurgeif0(struct inpcbtable *table
 			    i6mm_chain, nimm) {
 				if (imm->i6mm_maddr->in6m_ifp == ifp) {
 					LIST_REMOVE(imm, i6mm_chain);
+					IFNET_LOCK(ifp);
 					in6_leavegroup(imm);
+					IFNET_UNLOCK(ifp);
 				}
 			}
 		}
+
+		/* IFNET_LOCK must be taken after solock */
+		IFNET_LOCK(ifp);
 		in_purgeifmcast(in6p->in6p_v4moptions, ifp);
+		IFNET_UNLOCK(ifp);
+
 		if (need_unlock)
 			in6p_unlock(in6p);
 	}

Index: src/sys/netinet6/ip6_output.c
diff -u src/sys/netinet6/ip6_output.c:1.195 src/sys/netinet6/ip6_output.c:1.196
--- src/sys/netinet6/ip6_output.c:1.195	Sat Nov 25 13:18:02 2017
+++ src/sys/netinet6/ip6_output.c	Fri Dec 15 04:03:46 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: ip6_output.c,v 1.195 2017/11/25 13:18:02 kre Exp $	*/
+/*	$NetBSD: ip6_output.c,v 1.196 2017/12/15 04:03:46 ozaki-r Exp $	*/
 /*	$KAME: ip6_output.c,v 1.172 2001/03/25 09:55:56 itojun Exp $	*/
 
 /*
@@ -62,7 +62,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: ip6_output.c,v 1.195 2017/11/25 13:18:02 kre Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ip6_output.c,v 1.196 2017/12/15 04:03:46 ozaki-r Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_inet.h"
@@ -2606,7 +2606,9 @@ ip6_setmoptions(const struct sockopt *so
 		 * Everything looks good; add a new record to the multicast
 		 * address list for the given interface.
 		 */
+		IFNET_LOCK(ifp);
 		imm = in6_joingroup(ifp, &ia, &error, 0);
+		IFNET_UNLOCK(ifp);
 		if (imm == NULL)
 			goto put_break;
 		LIST_INSERT_HEAD(&im6o->im6o_memberships, imm, i6mm_chain);
@@ -2616,7 +2618,8 @@ ip6_setmoptions(const struct sockopt *so
 		break;
 	    }
 
-	case IPV6_LEAVE_GROUP:
+	case IPV6_LEAVE_GROUP: {
+		struct ifnet *in6m_ifp;
 		/*
 		 * Drop a multicast group membership.
 		 * Group must be a valid IP6 multicast address.
@@ -2698,8 +2701,13 @@ ip6_setmoptions(const struct sockopt *so
 		 * membership points.
 		 */
 		LIST_REMOVE(imm, i6mm_chain);
+		in6m_ifp = imm->i6mm_maddr->in6m_ifp;
+		IFNET_LOCK(in6m_ifp);
 		in6_leavegroup(imm);
+		/* in6m_ifp should not leave thanks to in6p_lock */
+		IFNET_UNLOCK(in6m_ifp);
 		break;
+	    }
 
 	default:
 		error = EOPNOTSUPP;
@@ -2778,8 +2786,15 @@ ip6_freemoptions(struct ip6_moptions *im
 
 	/* The owner of im6o (in6p) should be protected by solock */
 	LIST_FOREACH_SAFE(imm, &im6o->im6o_memberships, i6mm_chain, nimm) {
+		struct ifnet *ifp;
+
 		LIST_REMOVE(imm, i6mm_chain);
+
+		ifp = imm->i6mm_maddr->in6m_ifp;
+		IFNET_LOCK(ifp);
 		in6_leavegroup(imm);
+		/* ifp should not leave thanks to solock */
+		IFNET_UNLOCK(ifp);
 	}
 	free(im6o, M_IPMOPTS);
 }

Index: src/sys/netinet6/nd6.c
diff -u src/sys/netinet6/nd6.c:1.239 src/sys/netinet6/nd6.c:1.240
--- src/sys/netinet6/nd6.c:1.239	Fri Nov 17 07:37:12 2017
+++ src/sys/netinet6/nd6.c	Fri Dec 15 04:03:46 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: nd6.c,v 1.239 2017/11/17 07:37:12 ozaki-r Exp $	*/
+/*	$NetBSD: nd6.c,v 1.240 2017/12/15 04:03:46 ozaki-r Exp $	*/
 /*	$KAME: nd6.c,v 1.279 2002/06/08 11:16:51 itojun Exp $	*/
 
 /*
@@ -31,7 +31,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: nd6.c,v 1.239 2017/11/17 07:37:12 ozaki-r Exp $");
+__KERNEL_RCSID(0, "$NetBSD: nd6.c,v 1.240 2017/12/15 04:03:46 ozaki-r Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_net_mpsafe.h"
@@ -614,6 +614,7 @@ nd6_timer_work(struct work *wk, void *ar
 		/* check address lifetime */
 		if (IFA6_IS_INVALID(ia6)) {
 			int regen = 0;
+			struct ifnet *ifp;
 
 			/*
 			 * If the expiring address is temporary, try
@@ -627,13 +628,30 @@ nd6_timer_work(struct work *wk, void *ar
 			 */
 			if (ip6_use_tempaddr &&
 			    (ia6->ia6_flags & IN6_IFF_TEMPORARY) != 0) {
+				IFNET_LOCK(ia6->ia_ifa.ifa_ifp);
 				if (regen_tmpaddr(ia6) == 0)
 					regen = 1;
+				IFNET_UNLOCK(ia6->ia_ifa.ifa_ifp);
 			}
 
-			ia6_release(ia6, &psref);
- 			in6_purgeaddr(&ia6->ia_ifa);
+			ifp = ia6->ia_ifa.ifa_ifp;
+			IFNET_LOCK(ifp);
+			/*
+			 * Need to take the lock first to prevent if_detach
+			 * from running in6_purgeaddr concurrently.
+			 */
+			if (!if_is_deactivated(ifp)) {
+				ia6_release(ia6, &psref);
+				in6_purgeaddr(&ia6->ia_ifa);
+			} else {
+				/*
+				 * ifp is being destroyed, ia6 will be destroyed
+				 * by if_detach.
+				 */
+				ia6_release(ia6, &psref);
+			}
 			ia6 = NULL;
+			IFNET_UNLOCK(ifp);
 
 			if (regen)
 				goto addrloop; /* XXX: see below */
@@ -1883,20 +1901,53 @@ nd6_ioctl(u_long cmd, void *data, struct
 			_s = pserialize_read_enter();
 			for (ia = IN6_ADDRLIST_READER_FIRST(); ia;
 			     ia = ia_next) {
+				struct ifnet *ifa_ifp;
+				int bound;
+				struct psref psref;
+
 				/* ia might be removed.  keep the next ptr. */
 				ia_next = IN6_ADDRLIST_READER_NEXT(ia);
 
 				if ((ia->ia6_flags & IN6_IFF_AUTOCONF) == 0)
 					continue;
 
-				if (ia->ia6_ndpr == pfx) {
-					pserialize_read_exit(_s);
-					ND6_UNLOCK();
-					/* XXX NOMPSAFE? */
-					/* in6_purgeaddr may destroy pfx. */
+				if (ia->ia6_ndpr != pfx)
+					continue;
+
+				bound = curlwp_bind();
+				ia6_acquire(ia, &psref);
+				pserialize_read_exit(_s);
+				ND6_UNLOCK();
+
+				ifa_ifp = ia->ia_ifa.ifa_ifp;
+				if (ifa_ifp == ifp) {
+					/* Already have IFNET_LOCK(ifp) */
+					KASSERT(!if_is_deactivated(ifp));
+					ia6_release(ia, &psref);
 					in6_purgeaddr(&ia->ia_ifa);
+					curlwp_bindx(bound);
 					goto restart;
 				}
+				IFNET_LOCK(ifa_ifp);
+				/*
+				 * Need to take the lock first to prevent
+				 * if_detach from running in6_purgeaddr
+				 * concurrently.
+				 */
+				if (!if_is_deactivated(ifa_ifp)) {
+					ia6_release(ia, &psref);
+					in6_purgeaddr(&ia->ia_ifa);
+				} else {
+					/*
+					 * ifp is being destroyed, ia will be
+					 * destroyed by if_detach.
+					 */
+					ia6_release(ia, &psref);
+					/* XXX may cause busy loop */
+				}
+				IFNET_UNLOCK(ifa_ifp);
+				curlwp_bindx(bound);
+				goto restart;
 			}
 			pserialize_read_exit(_s);
 

Index: src/sys/netinet6/nd6_rtr.c
diff -u src/sys/netinet6/nd6_rtr.c:1.135 src/sys/netinet6/nd6_rtr.c:1.136
--- src/sys/netinet6/nd6_rtr.c:1.135	Tue Mar 14 04:21:38 2017
+++ src/sys/netinet6/nd6_rtr.c	Fri Dec 15 04:03:46 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: nd6_rtr.c,v 1.135 2017/03/14 04:21:38 ozaki-r Exp $	*/
+/*	$NetBSD: nd6_rtr.c,v 1.136 2017/12/15 04:03:46 ozaki-r Exp $	*/
 /*	$KAME: nd6_rtr.c,v 1.95 2001/02/07 08:09:47 itojun Exp $	*/
 
 /*
@@ -31,7 +31,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: nd6_rtr.c,v 1.135 2017/03/14 04:21:38 ozaki-r Exp $");
+__KERNEL_RCSID(0, "$NetBSD: nd6_rtr.c,v 1.136 2017/12/15 04:03:46 ozaki-r Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_net_mpsafe.h"
@@ -319,6 +319,7 @@ nd6_ra_input(struct mbuf *m, int off, in
 			   IN6_PRINT(ip6buf, &ip6->ip6_src),
 			   if_name(ifp), ndi->chlim, nd_ra->nd_ra_curhoplimit);
 	}
+	IFNET_LOCK(ifp);
 	ND6_WLOCK();
 	dr = defrtrlist_update(&drtr);
     }
@@ -378,6 +379,7 @@ nd6_ra_input(struct mbuf *m, int off, in
 		}
 	}
 	ND6_UNLOCK();
+	IFNET_UNLOCK(ifp);
 
 	/*
 	 * MTU

Reply via email to