From nobody@FreeBSD.org  Fri Jul 23 07:49:27 2010
Return-Path: <nobody@FreeBSD.org>
Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34])
	by hub.freebsd.org (Postfix) with ESMTP id 1FBE1106564A
	for <freebsd-gnats-submit@FreeBSD.org>; Fri, 23 Jul 2010 07:49:27 +0000 (UTC)
	(envelope-from nobody@FreeBSD.org)
Received: from www.freebsd.org (www.freebsd.org [IPv6:2001:4f8:fff6::21])
	by mx1.freebsd.org (Postfix) with ESMTP id 0DFAB8FC0A
	for <freebsd-gnats-submit@FreeBSD.org>; Fri, 23 Jul 2010 07:49:27 +0000 (UTC)
Received: from www.freebsd.org (localhost [127.0.0.1])
	by www.freebsd.org (8.14.3/8.14.3) with ESMTP id o6N7nQ5w077510
	for <freebsd-gnats-submit@FreeBSD.org>; Fri, 23 Jul 2010 07:49:26 GMT
	(envelope-from nobody@www.freebsd.org)
Received: (from nobody@localhost)
	by www.freebsd.org (8.14.3/8.14.3/Submit) id o6N7nQOx077509;
	Fri, 23 Jul 2010 07:49:26 GMT
	(envelope-from nobody)
Message-Id: <201007230749.o6N7nQOx077509@www.freebsd.org>
Date: Fri, 23 Jul 2010 07:49:26 GMT
From: Dmitrij Tejblum <tejblum@yandex-team.ru>
To: freebsd-gnats-submit@FreeBSD.org
Subject: [patch] [ip6] Panics due to insufficient locking in netinet6/nd6.c
X-Send-Pr-Version: www-3.1
X-GNATS-Notify:

>Number:         148857
>Category:       kern
>Synopsis:       [patch] [ip6] Panics due to insufficient locking in netinet6/nd6.c
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    bz
>State:          closed
>Quarter:        
>Keywords:       
>Date-Required:  
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Fri Jul 23 07:50:03 UTC 2010
>Closed-Date:    Thu Dec 02 10:41:55 UTC 2010
>Last-Modified:  Fri Dec 10 15:40:09 UTC 2010
>Originator:     Dmitrij Tejblum
>Release:        8.1-PRERELEASE
>Organization:
Yandex
>Environment:
>Description:
nd6_llinfo_timer() heavily use and modify an `struct llentry' called `ln'. It should lock it, to protect against someone else work with the llentry. However, it  does not.
>How-To-Repeat:
It is better to run kernel with INVARIANTS.

Run ping6 to an on-link IPv6 address that is not assigned to any node. The system will panic after a few seconds. The panic is caused by immediate generation of DST_UNREACH icmp response (since the address is unreachable) and ping6 sending the next ping. Both of these actions work with ln->la_hold queue of packets.
>Fix:
I've tested the attached patch with INVARIANTS and WITNESS

Patch attached with submission follows:

--- sys/netinet6/nd6.c	2010-07-19 17:46:38.000000000 +0400
+++ sys/netinet6/nd6.c	2010-07-23 00:05:57.000000000 +0400
@@ -400,12 +400,13 @@ skip1:
  */
 void
 nd6_llinfo_settimer_locked(struct llentry *ln, long tick)
 {
 	int canceled;
 
+	LLE_WLOCK_ASSERT(ln);
 	if (tick < 0) {
 		ln->la_expire = 0;
 		ln->ln_ntick = 0;
 		canceled = callout_stop(&ln->ln_timer_ch);
 	} else {
 		ln->la_expire = time_second + tick / hz;
@@ -437,31 +438,33 @@ static void
 nd6_llinfo_timer(void *arg)
 {
 	struct llentry *ln;
 	struct in6_addr *dst;
 	struct ifnet *ifp;
 	struct nd_ifinfo *ndi = NULL;
+	struct mbuf *m = NULL;
 
 	ln = (struct llentry *)arg;
 	if (ln == NULL) {
 		panic("%s: NULL entry!\n", __func__);
 		return;
 	}
+	LLE_WLOCK_ASSERT(ln);
 
 	if ((ifp = ((ln->lle_tbl != NULL) ? ln->lle_tbl->llt_ifp : NULL)) == NULL)
 		panic("ln ifp == NULL");
 
 	CURVNET_SET(ifp->if_vnet);
 
 	if (ln->ln_ntick > 0) {
 		if (ln->ln_ntick > INT_MAX) {
 			ln->ln_ntick -= INT_MAX;
-			nd6_llinfo_settimer(ln, INT_MAX);
+			nd6_llinfo_settimer_locked(ln, INT_MAX);
 		} else {
 			ln->ln_ntick = 0;
-			nd6_llinfo_settimer(ln, ln->ln_ntick);
+			nd6_llinfo_settimer_locked(ln, ln->ln_ntick);
 		}
 		goto done;
 	}
 
 	ndi = ND_IFINFO(ifp);
 	dst = &L3_ADDR_SIN6(ln)->sin6_addr;
@@ -476,39 +479,38 @@ nd6_llinfo_timer(void *arg)
 	}
 
 	switch (ln->ln_state) {
 	case ND6_LLINFO_INCOMPLETE:
 		if (ln->la_asked < V_nd6_mmaxtries) {
 			ln->la_asked++;
-			nd6_llinfo_settimer(ln, (long)ndi->retrans * hz / 1000);
+			nd6_llinfo_settimer_locked(ln, (long)ndi->retrans * hz / 1000);
 			nd6_ns_output(ifp, NULL, dst, ln, 0);
 		} else {
-			struct mbuf *m = ln->la_hold;
+			m = ln->la_hold;
 			if (m) {
 				struct mbuf *m0;
 
 				/*
 				 * assuming every packet in la_hold has the
 				 * same IP header
 				 */
 				m0 = m->m_nextpkt;
 				m->m_nextpkt = NULL;
-				icmp6_error2(m, ICMP6_DST_UNREACH,
-				    ICMP6_DST_UNREACH_ADDR, 0, ifp);
+				/* send error after unlock, to avoid reversal */
 
 				ln->la_hold = m0;
 				clear_llinfo_pqueue(ln);
 			}
 			(void)nd6_free(ln, 0);
 			ln = NULL;
 		}
 		break;
 	case ND6_LLINFO_REACHABLE:
 		if (!ND6_LLINFO_PERMANENT(ln)) {
 			ln->ln_state = ND6_LLINFO_STALE;
-			nd6_llinfo_settimer(ln, (long)V_nd6_gctimer * hz);
+			nd6_llinfo_settimer_locked(ln, (long)V_nd6_gctimer * hz);
 		}
 		break;
 
 	case ND6_LLINFO_STALE:
 		/* Garbage Collection(RFC 2461 5.3) */
 		if (!ND6_LLINFO_PERMANENT(ln)) {
@@ -519,33 +521,36 @@ nd6_llinfo_timer(void *arg)
 
 	case ND6_LLINFO_DELAY:
 		if (ndi && (ndi->flags & ND6_IFF_PERFORMNUD) != 0) {
 			/* We need NUD */
 			ln->la_asked = 1;
 			ln->ln_state = ND6_LLINFO_PROBE;
-			nd6_llinfo_settimer(ln, (long)ndi->retrans * hz / 1000);
+			nd6_llinfo_settimer_locked(ln, (long)ndi->retrans * hz / 1000);
 			nd6_ns_output(ifp, dst, dst, ln, 0);
 		} else {
 			ln->ln_state = ND6_LLINFO_STALE; /* XXX */
-			nd6_llinfo_settimer(ln, (long)V_nd6_gctimer * hz);
+			nd6_llinfo_settimer_locked(ln, (long)V_nd6_gctimer * hz);
 		}
 		break;
 	case ND6_LLINFO_PROBE:
 		if (ln->la_asked < V_nd6_umaxtries) {
 			ln->la_asked++;
-			nd6_llinfo_settimer(ln, (long)ndi->retrans * hz / 1000);
+			nd6_llinfo_settimer_locked(ln, (long)ndi->retrans * hz / 1000);
 			nd6_ns_output(ifp, dst, dst, ln, 0);
 		} else {
 			(void)nd6_free(ln, 0);
 			ln = NULL;
 		}
 		break;
 	}
 done:
 	if (ln != NULL)
-		LLE_FREE(ln);
+		LLE_FREE_LOCKED(ln);
+	if (m != NULL)
+		icmp6_error2(m, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_ADDR, 
+		    0, ifp);
 	CURVNET_RESTORE();
 }
 
 
 /*
  * ND6 timer routine to expire default route list and prefix list
@@ -851,13 +856,13 @@ nd6_lookup(struct in6_addr *addr6, int f
 	if (flags & ND6_EXCLUSIVE)
 	    llflags |= LLE_EXCLUSIVE;	
 	
 	ln = lla_lookup(LLTABLE6(ifp), llflags, (struct sockaddr *)&sin6);
 	if ((ln != NULL) && (flags & LLE_CREATE)) {
 		ln->ln_state = ND6_LLINFO_NOSTATE;
-		callout_init(&ln->ln_timer_ch, 0);
+		callout_init_rw(&ln->ln_timer_ch, &ln->lle_lock, CALLOUT_RETURNUNLOCKED);
 	}
 	
 	return (ln);
 }
 
 /*
@@ -997,19 +1002,20 @@ static struct llentry *
 nd6_free(struct llentry *ln, int gc)
 {
         struct llentry *next;
 	struct nd_defrouter *dr;
 	struct ifnet *ifp=NULL;
 
+	LLE_WLOCK_ASSERT(ln);
 	/*
 	 * we used to have pfctlinput(PRC_HOSTDEAD) here.
 	 * even though it is not harmful, it was not really necessary.
 	 */
 
 	/* cancel timer */
-	nd6_llinfo_settimer(ln, -1);
+	nd6_llinfo_settimer_locked(ln, -1);
 
 	if (!V_ip6_forwarding) {
 		int s;
 		s = splnet();
 		dr = defrouter_lookup(&L3_ADDR_SIN6(ln)->sin6_addr, ln->lle_tbl->llt_ifp);
 
@@ -1025,18 +1031,17 @@ nd6_free(struct llentry *ln, int gc)
 			 * thing, especially when we're using router preference
 			 * values.
 			 * XXX: the check for ln_state would be redundant,
 			 *      but we intentionally keep it just in case.
 			 */
 			if (dr->expire > time_second)
-				nd6_llinfo_settimer(ln,
+				nd6_llinfo_settimer_locked(ln,
 				    (dr->expire - time_second) * hz);
 			else
-				nd6_llinfo_settimer(ln, (long)V_nd6_gctimer * hz);
+				nd6_llinfo_settimer_locked(ln, (long)V_nd6_gctimer * hz);
 			splx(s);
-			LLE_WLOCK(ln);
 			LLE_REMREF(ln);
 			LLE_WUNLOCK(ln);
 			return (LIST_NEXT(ln, lle_next));
 		}
 
 		if (ln->ln_router || dr) {
@@ -1086,12 +1091,13 @@ nd6_free(struct llentry *ln, int gc)
 	 * might have freed other entries (particularly the old next entry) as
 	 * a side effect (XXX).
 	 */
 	next = LIST_NEXT(ln, lle_next);
 
 	ifp = ln->lle_tbl->llt_ifp;
+	LLE_WUNLOCK(ln);
 	IF_AFDATA_LOCK(ifp);
 	LLE_WLOCK(ln);
 	LLE_REMREF(ln);
 	llentry_free(ln);
 	IF_AFDATA_UNLOCK(ifp);
 
@@ -1829,33 +1835,33 @@ nd6_output_lle(struct ifnet *ifp, struct
 			i--;
 		}
 	} else {
 		ln->la_hold = m;
 	}
 	/*
+	 * If there has been no NS for the neighbor after entering the
+	 * INCOMPLETE state, send the first solicitation.
+	 */
+	if (!ND6_LLINFO_PERMANENT(ln) && ln->la_asked == 0) {
+		ln->la_asked++;
+		
+		nd6_llinfo_settimer_locked(ln,
+		    (long)ND_IFINFO(ifp)->retrans * hz / 1000);
+		nd6_ns_output(ifp, NULL, &dst->sin6_addr, ln, 0);
+	}
+	/*
 	 * We did the lookup (no lle arg) so we
 	 * need to do the unlock here
 	 */
 	if (lle == NULL) {
 		if (flags & LLE_EXCLUSIVE)
 			LLE_WUNLOCK(ln);
 		else
 			LLE_RUNLOCK(ln);
 	}
 	
-	/*
-	 * If there has been no NS for the neighbor after entering the
-	 * INCOMPLETE state, send the first solicitation.
-	 */
-	if (!ND6_LLINFO_PERMANENT(ln) && ln->la_asked == 0) {
-		ln->la_asked++;
-		
-		nd6_llinfo_settimer(ln,
-		    (long)ND_IFINFO(ifp)->retrans * hz / 1000);
-		nd6_ns_output(ifp, NULL, &dst->sin6_addr, ln, 0);
-	}
 	return (0);
 
   sendpkt:
 	/* discard the packet if IPv6 operation is disabled on the interface */
 	if ((ND_IFINFO(ifp)->flags & ND6_IFF_IFDISABLED)) {
 		error = ENETDOWN; /* better error? */
--- sys/netinet6/nd6_nbr.c	2010-07-19 17:46:38.000000000 +0400
+++ sys/netinet6/nd6_nbr.c	2010-07-22 17:39:39.000000000 +0400
@@ -421,12 +421,17 @@ nd6_ns_output(struct ifnet *ifp, const s
 		}
 	}
 	if (m == NULL)
 		return;
 	m->m_pkthdr.rcvif = NULL;
 
+	if (ln != NULL) {
+		LLE_WLOCK_ASSERT(ln);
+		LLE_WUNLOCK(ln);
+	}
+
 	if (daddr6 == NULL || IN6_IS_ADDR_MULTICAST(daddr6)) {
 		m->m_flags |= M_MCAST;
 		im6o.im6o_multicast_ifp = ifp;
 		im6o.im6o_multicast_hlim = 255;
 		im6o.im6o_multicast_loop = 0;
 	}
@@ -473,23 +478,27 @@ nd6_ns_output(struct ifnet *ifp, const s
 		 * - saddr6 belongs to the outgoing interface.
 		 * Otherwise, we perform the source address selection as usual.
 		 */
 		struct ip6_hdr *hip6;		/* hold ip6 */
 		struct in6_addr *hsrc = NULL;
 
-		if ((ln != NULL) && ln->la_hold) {
-			/*
-			 * assuming every packet in la_hold has the same IP
-			 * header
-			 */
-			hip6 = mtod(ln->la_hold, struct ip6_hdr *);
-			/* XXX pullup? */
-			if (sizeof(*hip6) < ln->la_hold->m_len)
-				hsrc = &hip6->ip6_src;
-			else
-				hsrc = NULL;
+		if (ln != NULL) {
+			LLE_RLOCK(ln);
+			if (ln->la_hold) {
+				/*
+				 * assuming every packet in la_hold has the same IP
+				 * header
+				 */
+				hip6 = mtod(ln->la_hold, struct ip6_hdr *);
+				/* XXX pullup? */
+				if (sizeof(*hip6) < ln->la_hold->m_len)
+					hsrc = &hip6->ip6_src;
+				else
+					hsrc = NULL;
+			}
+			LLE_RUNLOCK(ln);
 		}
 		if (hsrc && (ifa = (struct ifaddr *)in6ifa_ifpwithaddr(ifp,
 		    hsrc)) != NULL) {
 			src = hsrc;
 			ifa_free(ifa);
 		} else {
@@ -570,19 +579,23 @@ nd6_ns_output(struct ifnet *ifp, const s
 	icmp6_ifstat_inc(ifp, ifs6_out_neighborsolicit);
 	ICMP6STAT_INC(icp6s_outhist[ND_NEIGHBOR_SOLICIT]);
 
 	if (ro.ro_rt) {		/* we don't cache this route. */
 		RTFREE(ro.ro_rt);
 	}
+	if (ln)
+		LLE_WLOCK(ln);
 	return;
 
   bad:
 	if (ro.ro_rt) {
 		RTFREE(ro.ro_rt);
 	}
 	m_freem(m);
+	if (ln)
+		LLE_WLOCK(ln);
 	return;
 }
 
 /*
  * Neighbor advertisement input handling.
  *


>Release-Note:
>Audit-Trail:
Responsible-Changed-From-To: freebsd-bugs->freebsd-net 
Responsible-Changed-By: linimon 
Responsible-Changed-When: Fri Jul 23 21:46:23 UTC 2010 
Responsible-Changed-Why:  
Over to maintainer(s). 

http://www.freebsd.org/cgi/query-pr.cgi?pr=148857 
Responsible-Changed-From-To: freebsd-net->bz 
Responsible-Changed-By: remko 
Responsible-Changed-When: Fri Jul 23 21:50:40 UTC 2010 
Responsible-Changed-Why:  
I asked Bjoern to look at this, so assign it to him. 

Bjoern, if you have the opportunity please look into this, thanks! 

http://www.freebsd.org/cgi/query-pr.cgi?pr=148857 

From: "Bjoern A. Zeeb" <bz@FreeBSD.org>
To: bug-followup@FreeBSD.org, tejblum@yandex-team.ru
Cc:  
Subject: Re: kern/148857: [patch] [ip6] Panics due to insufficient locking
 in netinet6/nd6.c
Date: Wed, 11 Aug 2010 17:15:44 +0000 (UTC)

 Hey,
 
 this is the 2nd report with similar symptoms I have seen but a
 different way to trigger it. Despite all efforts I am not able to
 reproduce it, which I would very much like to - not questioning the
 locking issue.
 
 Can you give me a good way how to do that (real configurations,
 sequence of commands)?
 
 Also what kind of systems/NICs/etc. are you using. Any virtualization
 like VMWare invovled?
 
 Thanks.
 
 -- 
 Bjoern A. Zeeb                       This signature is about you not me.

From: Dmitrij Tejblum <tejblum@yandex-team.ru>
To: "Bjoern A. Zeeb" <bz@FreeBSD.org>
Cc: bug-followup@FreeBSD.org
Subject: Re: kern/148857: [patch] [ip6] Panics due to insufficient locking
 in netinet6/nd6.c
Date: Thu, 12 Aug 2010 18:06:11 +0400

 Bjoern A. Zeeb wrote:
 > Hey,
 >
 > this is the 2nd report with similar symptoms I have seen but a
 > different way to trigger it. Despite all efforts I am not able to
 > reproduce it, which I would very much like to - not questioning the
 > locking issue.
 >
 > Can you give me a good way how to do that (real configurations,
 > sequence of commands)?
 
 Well, on one of our test servers, where network is configured like this:
 
 em1: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> metric 0 mtu 1500
          
 options=209b<RXCSUM,TXCSUM,VLAN_MTU,VLAN_HWTAGGING,VLAN_HWCSUM,WOL_MAGIC>
          ether 00:0e:0c:09:2e:7b
          inet 213.180.201.240 netmask 0xffffffc0 broadcast 213.180.201.255
          inet6 fe80::20e:cff:fe09:2e7b%em1 prefixlen 64 scopeid 0x2
          inet6 2a02:6b8:0:403:20e:cff:fe09:2e7b prefixlen 64 autoconf
          nd6 options=3<PERFORMNUD,ACCEPT_RTADV>
          media: Ethernet autoselect (1000baseT <full-duplex>)
          status: active
 
 the reproduction is as simple as
 
 ping6 2a02:6b8:0:403:0123:4567:89ab:cdef
 # there is no such address on this LAN
 
 But I have to admit I cannot reproduce it this way on e.g. my desktop.
 
 The server is an old real (i.e. no virtualization) server with such 
 hardware:
 
 
 Copyright (c) 1992-2010 The FreeBSD Project.
 Copyright (c) 1979, 1980, 1983, 1986, 1988, 1989, 1991, 1992, 1993, 1994
          The Regents of the University of California. All rights reserved.
 FreeBSD is a registered trademark of The FreeBSD Foundation.
 FreeBSD 8.1-STABLE #23: Thu Aug 12 15:05:52 MSD 2010
 root@orange.yandex.net:/usr/obj/usr/src/sys/ORANGE i386
 WARNING: WITNESS option enabled, expect reduced performance.
 Timecounter "i8254" frequency 1193182 Hz quality 0
 CPU: Intel(R) Xeon(TM) CPU 2.80GHz (2791.01-MHz 686-class CPU)
    Origin = "GenuineIntel"  Id = 0xf25  Family = f  Model = 2  Stepping = 5
    
 Features=0xbfebfbff<FPU,VME,DE,PSE,TSC,MSR,PAE,MCE,CX8,APIC,SEP,MTRR,PGE,MCA,C
 MOV,PAT,PSE36,CLFLUSH,DTS,ACPI,MMX,FXSR,SSE,SSE2,SS,HTT,TM,PBE>
    Features2=0x4400<CNXT-ID,xTPR>
 real memory  = 6442450944 (6144 MB)
 avail memory = 3932667904 (3750 MB)
 ACPI APIC Table: <INTEL  SWV25 >
 FreeBSD/SMP: Multiprocessor System Detected: 4 CPUs
 FreeBSD/SMP: 2 package(s) x 1 core(s) x 2 HTT threads
   cpu0 (BSP): APIC ID:  0
   cpu1 (AP/HT): APIC ID:  1
   cpu2 (AP): APIC ID:  6
   cpu3 (AP/HT): APIC ID:  7
 
 
 The NIC is Intel 82546EB
 
 em1: <Intel(R) PRO/1000 Legacy Network Connection 1.0.1> port 
 0x2000-0x203f mem 0xfe6e0000-0xfe6fffff irq 31 at device 7.1 on pci3
 em1: [FILTER]
 em1: Ethernet address: 00:0e:0c:09:2e:7b
 
 but I don't see how this can be NIC related. The problem in this case is 
 simultaneous call of icmp6_error2() from nd6_llinfo_timer() (nd6.c line 
 409) and freeing the same mbuf due to hold queue overflow (IIRC, it 
 happens at nd6.c line 1823:
                  while (i >= V_nd6_maxqueuelen) {
                          m_hold = ln->la_hold;
                          ln->la_hold = ln->la_hold->m_nextpkt;
                          m_freem(m_hold);
                          i--;
                  }
 ) Perhaps, on some systems with other CPUs or somesuch this actions is 
 not so simultaneous, so it is not so easy to reproduce.
 
 
 Thanks,
 Dmitry
 
 
 

From: Andrey Chernov <ache@nagual.pp.ru>
To: bug-followup@FreeBSD.org, tejblum@yandex-team.ru
Cc:  
Subject: Re: kern/148857: [patch] [ip6] Panics due to insufficient locking
 in netinet6/nd6.c
Date: Sat, 02 Oct 2010 17:44:59 +0400

 For recent -current the patch have missing
 struct mbuf *m = NULL;
 declaration early in the nd6_llinfo_timer().
 
 Besides that, it fix the panic, thanx!

From: "Bjoern A. Zeeb" <bz@FreeBSD.org>
To: bug-followup@FreeBSD.org, tejblum@yandex-team.ru
Cc:  
Subject: Re: kern/148857: [patch] [ip6] Panics due to insufficient locking
 in netinet6/nd6.c
Date: Sat, 20 Nov 2010 20:00:39 +0000 (UTC)

 Hey,
 
 based on your version I have updated patches for both HEAD and
 stable/8 with slight changes to the locking.  Could you please
 test/review and let me know?
 
 You can find the two versions temporary here as well:
 
 - for HEAD:
    http://people.freebsd.org/~bz/20101120-01-nd6-locking-pr148857.diff
 - for stable/8:
    http://people.freebsd.org/~bz/20101120-02-nd6-locking-pr148857-stable8.diff
 
 (the stable/8 version was only compile time tested, but is included
 below):
 
 
 !
 ! For stable/8:
 !
 ! MFC rNNNNNN:
 !
 ! Plug well observed races on la_hold entries with the callout handler.
 !
 ! Call the handler function with the lock held, return unlocked as we
 ! might free the entry.  Rework functions later in the call graph to be
 ! either called with the lock held or, only if needed, unlocked.
 !
 ! Place asserts to document and tighten assumptions on various lle locking,
 ! which were not always true before.
 !
 ! We call nd6_ns_output() unlocked and the assignment of ip6->ip6_src was
 ! decentralized to minimize possible complexity introduced with the formerly
 ! missing locking there.  This also resulted in a push down of local
 ! variable scopes into smaller blocks.
 !
 !
 ! PR:		kern/148857
 ! Submitted by:	Dmitrij Tejblum (tejblum yandex-team.ru) (original version)
 ! Tested by: 
 ! Reviewed by: 
 ! MFC After:	4 days
 
 Property changes on: sys
 ___________________________________________________________________
 Modified: svn:mergeinfo
     Merged /head/sys:r215418,215423,215559
 
 
 Property changes on: sys/dev/xen/xenpci
 ___________________________________________________________________
 Modified: svn:mergeinfo
     Merged /head/sys/dev/xen/xenpci:r215418,215423,215559
 
 Index: sys/netinet6/nd6.c
 ===================================================================
 --- sys/netinet6/nd6.c	(revision 215573)
 +++ sys/netinet6/nd6.c	(working copy)
 @@ -403,6 +403,8 @@ nd6_llinfo_settimer_locked(struct llentry *ln, lon
   {
   	int canceled;
 
 +	LLE_WLOCK_ASSERT(ln);
 +
   	if (tick < 0) {
   		ln->la_expire = 0;
   		ln->ln_ntick = 0;
 @@ -443,6 +445,7 @@ nd6_llinfo_timer(void *arg)
 
   	KASSERT(arg != NULL, ("%s: arg NULL", __func__));
   	ln = (struct llentry *)arg;
 +	LLE_WLOCK_ASSERT(ln);
   	ifp = ln->lle_tbl->llt_ifp;
 
   	CURVNET_SET(ifp->if_vnet);
 @@ -450,10 +453,10 @@ nd6_llinfo_timer(void *arg)
   	if (ln->ln_ntick > 0) {
   		if (ln->ln_ntick > INT_MAX) {
   			ln->ln_ntick -= INT_MAX;
 -			nd6_llinfo_settimer(ln, INT_MAX);
 +			nd6_llinfo_settimer_locked(ln, INT_MAX);
   		} else {
   			ln->ln_ntick = 0;
 -			nd6_llinfo_settimer(ln, ln->ln_ntick);
 +			nd6_llinfo_settimer_locked(ln, ln->ln_ntick);
   		}
   		goto done;
   	}
 @@ -474,8 +477,10 @@ nd6_llinfo_timer(void *arg)
   	case ND6_LLINFO_INCOMPLETE:
   		if (ln->la_asked < V_nd6_mmaxtries) {
   			ln->la_asked++;
 -			nd6_llinfo_settimer(ln, (long)ndi->retrans * hz / 1000);
 +			nd6_llinfo_settimer_locked(ln, (long)ndi->retrans * hz / 1000);
 +			LLE_WUNLOCK(ln);
   			nd6_ns_output(ifp, NULL, dst, ln, 0);
 +			LLE_WLOCK(ln);
   		} else {
   			struct mbuf *m = ln->la_hold;
   			if (m) {
 @@ -483,24 +488,24 @@ nd6_llinfo_timer(void *arg)
 
   				/*
   				 * assuming every packet in la_hold has the
 -				 * same IP header
 +				 * same IP header.  Send error after unlock.
   				 */
   				m0 = m->m_nextpkt;
   				m->m_nextpkt = NULL;
 -				icmp6_error2(m, ICMP6_DST_UNREACH,
 -				    ICMP6_DST_UNREACH_ADDR, 0, ifp);
 -
   				ln->la_hold = m0;
   				clear_llinfo_pqueue(ln);
   			}
   			(void)nd6_free(ln, 0);
   			ln = NULL;
 +			if (m != NULL)
 +				icmp6_error2(m, ICMP6_DST_UNREACH,
 +				    ICMP6_DST_UNREACH_ADDR, 0, ifp);
   		}
   		break;
   	case ND6_LLINFO_REACHABLE:
   		if (!ND6_LLINFO_PERMANENT(ln)) {
   			ln->ln_state = ND6_LLINFO_STALE;
 -			nd6_llinfo_settimer(ln, (long)V_nd6_gctimer * hz);
 +			nd6_llinfo_settimer_locked(ln, (long)V_nd6_gctimer * hz);
   		}
   		break;
 
 @@ -517,27 +522,34 @@ nd6_llinfo_timer(void *arg)
   			/* We need NUD */
   			ln->la_asked = 1;
   			ln->ln_state = ND6_LLINFO_PROBE;
 -			nd6_llinfo_settimer(ln, (long)ndi->retrans * hz / 1000);
 +			nd6_llinfo_settimer_locked(ln, (long)ndi->retrans * hz / 1000);
 +			LLE_WUNLOCK(ln);
   			nd6_ns_output(ifp, dst, dst, ln, 0);
 +			LLE_WLOCK(ln);
   		} else {
   			ln->ln_state = ND6_LLINFO_STALE; /* XXX */
 -			nd6_llinfo_settimer(ln, (long)V_nd6_gctimer * hz);
 +			nd6_llinfo_settimer_locked(ln, (long)V_nd6_gctimer * hz);
   		}
   		break;
   	case ND6_LLINFO_PROBE:
   		if (ln->la_asked < V_nd6_umaxtries) {
   			ln->la_asked++;
 -			nd6_llinfo_settimer(ln, (long)ndi->retrans * hz / 1000);
 +			nd6_llinfo_settimer_locked(ln, (long)ndi->retrans * hz / 1000);
 +			LLE_WUNLOCK(ln);
   			nd6_ns_output(ifp, dst, dst, ln, 0);
 +			LLE_WLOCK(ln);
   		} else {
   			(void)nd6_free(ln, 0);
   			ln = NULL;
   		}
   		break;
 +	default:
 +		panic("%s: paths in a dark night can be confusing: %d",
 +		    __func__, ln->ln_state);
   	}
   done:
   	if (ln != NULL)
 -		LLE_FREE(ln);
 +		LLE_FREE_LOCKED(ln);
   	CURVNET_RESTORE();
   }
 
 @@ -832,7 +844,7 @@ nd6_lookup(struct in6_addr *addr6, int flags, stru
   {
   	struct sockaddr_in6 sin6;
   	struct llentry *ln;
 -	int llflags = 0;
 +	int llflags;
 
   	bzero(&sin6, sizeof(sin6));
   	sin6.sin6_len = sizeof(struct sockaddr_in6);
 @@ -841,16 +853,15 @@ nd6_lookup(struct in6_addr *addr6, int flags, stru
 
   	IF_AFDATA_LOCK_ASSERT(ifp);
 
 +	llflags = 0;
   	if (flags & ND6_CREATE)
   	    llflags |= LLE_CREATE;
   	if (flags & ND6_EXCLUSIVE)
   	    llflags |= LLE_EXCLUSIVE;
 
   	ln = lla_lookup(LLTABLE6(ifp), llflags, (struct sockaddr *)&sin6);
 -	if ((ln != NULL) && (flags & LLE_CREATE)) {
 +	if ((ln != NULL) && (llflags & LLE_CREATE))
   		ln->ln_state = ND6_LLINFO_NOSTATE;
 -		callout_init(&ln->ln_timer_ch, 0);
 -	}
 
   	return (ln);
   }
 @@ -993,21 +1004,24 @@ nd6_free(struct llentry *ln, int gc)
   {
           struct llentry *next;
   	struct nd_defrouter *dr;
 -	struct ifnet *ifp=NULL;
 +	struct ifnet *ifp;
 
 +	LLE_WLOCK_ASSERT(ln);
 +
   	/*
   	 * we used to have pfctlinput(PRC_HOSTDEAD) here.
   	 * even though it is not harmful, it was not really necessary.
   	 */
 
   	/* cancel timer */
 -	nd6_llinfo_settimer(ln, -1);
 +	nd6_llinfo_settimer_locked(ln, -1);
 
 +	ifp = ln->lle_tbl->llt_ifp;
 +
   	if (!V_ip6_forwarding) {
 -		int s;
 -		s = splnet();
 -		dr = defrouter_lookup(&L3_ADDR_SIN6(ln)->sin6_addr, ln->lle_tbl->llt_ifp);
 
 +		dr = defrouter_lookup(&L3_ADDR_SIN6(ln)->sin6_addr, ifp);
 +
   		if (dr != NULL && dr->expire &&
   		    ln->ln_state == ND6_LLINFO_STALE && gc) {
   			/*
 @@ -1023,15 +1037,16 @@ nd6_free(struct llentry *ln, int gc)
   			 *      but we intentionally keep it just in case.
   			 */
   			if (dr->expire > time_second)
 -				nd6_llinfo_settimer(ln,
 +				nd6_llinfo_settimer_locked(ln,
   				    (dr->expire - time_second) * hz);
   			else
 -				nd6_llinfo_settimer(ln, (long)V_nd6_gctimer * hz);
 -			splx(s);
 -			LLE_WLOCK(ln);
 +				nd6_llinfo_settimer_locked(ln,
 +				    (long)V_nd6_gctimer * hz);
 +
 +			next = LIST_NEXT(ln, lle_next);
   			LLE_REMREF(ln);
   			LLE_WUNLOCK(ln);
 -			return (LIST_NEXT(ln, lle_next));
 +			return (next);
   		}
 
   		if (ln->ln_router || dr) {
 @@ -1040,7 +1055,7 @@ nd6_free(struct llentry *ln, int gc)
   			 * is in the Default Router List.
   			 * See a corresponding comment in nd6_na_input().
   			 */
 -			rt6_flush(&L3_ADDR_SIN6(ln)->sin6_addr, ln->lle_tbl->llt_ifp);
 +			rt6_flush(&L3_ADDR_SIN6(ln)->sin6_addr, ifp);
   		}
 
   		if (dr) {
 @@ -1068,11 +1083,13 @@ nd6_free(struct llentry *ln, int gc)
   			pfxlist_onlink_check();
 
   			/*
 -			 * refresh default router list
 +			 * Refresh default router list.  Have to unlock as
 +			 * it calls into nd6_lookup(), still holding a ref.
   			 */
 +			LLE_WUNLOCK(ln);
   			defrouter_select();
 +			LLE_WLOCK(ln);
   		}
 -		splx(s);
   	}
 
   	/*
 @@ -1083,7 +1100,11 @@ nd6_free(struct llentry *ln, int gc)
   	 */
   	next = LIST_NEXT(ln, lle_next);
 
 -	ifp = ln->lle_tbl->llt_ifp;
 +	/*
 +	 * Save to unlock. We still hold an extra reference and will not
 +	 * free(9) in llentry_free() if someone else holds one as well.
 +	 */
 +	LLE_WUNLOCK(ln);
   	IF_AFDATA_LOCK(ifp);
   	LLE_WLOCK(ln);
   	LLE_REMREF(ln);
 @@ -1386,7 +1407,7 @@ nd6_cache_lladdr(struct ifnet *ifp, struct in6_add
   	int do_update;
   	int olladdr;
   	int llchange;
 -	int flags = 0;
 +	int flags;
   	int newstate = 0;
   	uint16_t router = 0;
   	struct sockaddr_in6 sin6;
 @@ -1413,13 +1434,13 @@ nd6_cache_lladdr(struct ifnet *ifp, struct in6_add
   	 * Spec says nothing in sections for RA, RS and NA.  There's small
   	 * description on it in NS section (RFC 2461 7.2.3).
   	 */
 -	flags |= lladdr ? ND6_EXCLUSIVE : 0;
 +	flags = lladdr ? ND6_EXCLUSIVE : 0;
   	IF_AFDATA_LOCK(ifp);
   	ln = nd6_lookup(from, flags, ifp);
 
   	if (ln == NULL) {
 -		flags |= LLE_EXCLUSIVE;
 -		ln = nd6_lookup(from, flags |ND6_CREATE, ifp);
 +		flags |= ND6_EXCLUSIVE;
 +		ln = nd6_lookup(from, flags | ND6_CREATE, ifp);
   		IF_AFDATA_UNLOCK(ifp);
   		is_newentry = 1;
   	} else {
 @@ -1805,6 +1826,9 @@ nd6_output_lle(struct ifnet *ifp, struct ifnet *or
   		LLE_RUNLOCK(ln);
   		goto retry;
   	}
 +
 +	LLE_WLOCK_ASSERT(ln);
 +
   	if (ln->la_hold) {
   		struct mbuf *m_hold;
   		int i;
 @@ -1826,28 +1850,29 @@ nd6_output_lle(struct ifnet *ifp, struct ifnet *or
   	} else {
   		ln->la_hold = m;
   	}
 +
   	/*
 -	 * We did the lookup (no lle arg) so we
 -	 * need to do the unlock here
 -	 */
 -	if (lle == NULL) {
 -		if (flags & LLE_EXCLUSIVE)
 -			LLE_WUNLOCK(ln);
 -		else
 -			LLE_RUNLOCK(ln);
 -	}
 - 
 -	/*
   	 * If there has been no NS for the neighbor after entering the
   	 * INCOMPLETE state, send the first solicitation.
   	 */
   	if (!ND6_LLINFO_PERMANENT(ln) && ln->la_asked == 0) {
   		ln->la_asked++;
 
 -		nd6_llinfo_settimer(ln,
 +		nd6_llinfo_settimer_locked(ln,
   		    (long)ND_IFINFO(ifp)->retrans * hz / 1000);
 +		LLE_WUNLOCK(ln);
   		nd6_ns_output(ifp, NULL, &dst->sin6_addr, ln, 0);
 +		if (lle != NULL && ln == lle)
 +			LLE_WLOCK(lle);
 +
 +	} else if (lle == NULL || ln != lle) {
 +		/*
 +		 * We did the lookup (no lle arg) so we
 +		 * need to do the unlock here.
 +		 */
 +		LLE_WUNLOCK(ln);
   	}
 +
   	return (0);
 
     sendpkt:
 Index: sys/netinet6/nd6_nbr.c
 ===================================================================
 --- sys/netinet6/nd6_nbr.c	(revision 215573)
 +++ sys/netinet6/nd6_nbr.c	(working copy)
 @@ -381,15 +381,12 @@ nd6_ns_output(struct ifnet *ifp, const struct in6_
   	struct mbuf *m;
   	struct ip6_hdr *ip6;
   	struct nd_neighbor_solicit *nd_ns;
 -	struct in6_addr *src, src_in;
   	struct ip6_moptions im6o;
   	int icmp6len;
   	int maxlen;
   	caddr_t mac;
   	struct route_in6 ro;
 
 -	bzero(&ro, sizeof(ro));
 -
   	if (IN6_IS_ADDR_MULTICAST(taddr6))
   		return;
 
 @@ -416,6 +413,8 @@ nd6_ns_output(struct ifnet *ifp, const struct in6_
   		return;
   	m->m_pkthdr.rcvif = NULL;
 
 +	bzero(&ro, sizeof(ro));
 +
   	if (daddr6 == NULL || IN6_IS_ADDR_MULTICAST(daddr6)) {
   		m->m_flags |= M_MCAST;
   		im6o.im6o_multicast_ifp = ifp;
 @@ -465,28 +464,35 @@ nd6_ns_output(struct ifnet *ifp, const struct in6_
   		 * - saddr6 belongs to the outgoing interface.
   		 * Otherwise, we perform the source address selection as usual.
   		 */
 -		struct ip6_hdr *hip6;		/* hold ip6 */
 -		struct in6_addr *hsrc = NULL;
 +		struct in6_addr *hsrc;
 
 -		if ((ln != NULL) && ln->la_hold) {
 -			/*
 -			 * assuming every packet in la_hold has the same IP
 -			 * header
 -			 */
 -			hip6 = mtod(ln->la_hold, struct ip6_hdr *);
 -			/* XXX pullup? */
 -			if (sizeof(*hip6) < ln->la_hold->m_len)
 -				hsrc = &hip6->ip6_src;
 -			else
 -				hsrc = NULL;
 +		hsrc = NULL;
 +		if (ln != NULL) {
 +			LLE_RLOCK(ln);
 +			if (ln->la_hold != NULL) {
 +				struct ip6_hdr *hip6;		/* hold ip6 */
 +
 +				/*
 +				 * assuming every packet in la_hold has the same IP
 +				 * header
 +				 */
 +				hip6 = mtod(ln->la_hold, struct ip6_hdr *);
 +				/* XXX pullup? */
 +				if (sizeof(*hip6) < ln->la_hold->m_len) {
 +					ip6->ip6_src = hip6->ip6_src;
 +					hsrc = &hip6->ip6_src;
 +				}
 +			}
 +			LLE_RUNLOCK(ln);
   		}
   		if (hsrc && (ifa = (struct ifaddr *)in6ifa_ifpwithaddr(ifp,
   		    hsrc)) != NULL) {
 -			src = hsrc;
 +			/* ip6_src set already. */
   			ifa_free(ifa);
   		} else {
   			int error;
   			struct sockaddr_in6 dst_sa;
 +			struct in6_addr src_in;
 
   			bzero(&dst_sa, sizeof(dst_sa));
   			dst_sa.sin6_family = AF_INET6;
 @@ -504,7 +510,7 @@ nd6_ns_output(struct ifnet *ifp, const struct in6_
   				    error));
   				goto bad;
   			}
 -			src = &src_in;
 +			ip6->ip6_src = src_in;
   		}
   	} else {
   		/*
 @@ -514,10 +520,8 @@ nd6_ns_output(struct ifnet *ifp, const struct in6_
   		 * above), but we do so here explicitly to make the intention
   		 * clearer.
   		 */
 -		bzero(&src_in, sizeof(src_in));
 -		src = &src_in;
 +		bzero(&ip6->ip6_src, sizeof(ip6->ip6_src));
   	}
 -	ip6->ip6_src = *src;
   	nd_ns = (struct nd_neighbor_solicit *)(ip6 + 1);
   	nd_ns->nd_ns_type = ND_NEIGHBOR_SOLICIT;
   	nd_ns->nd_ns_code = 0;
 Index: sys/netinet6/in6.c
 ===================================================================
 --- sys/netinet6/in6.c	(revision 215573)
 +++ sys/netinet6/in6.c	(working copy)
 @@ -2325,10 +2325,12 @@ in6_lltable_new(const struct sockaddr *l3addr, u_i
   	if (lle == NULL)		/* NB: caller generates msg */
   		return NULL;
 
 -	callout_init(&lle->base.ln_timer_ch, CALLOUT_MPSAFE);
   	lle->l3_addr6 = *(const struct sockaddr_in6 *)l3addr;
   	lle->base.lle_refcnt = 1;
   	LLE_LOCK_INIT(&lle->base);
 +	callout_init_rw(&lle->base.ln_timer_ch, &lle->base.lle_lock,
 +	    CALLOUT_RETURNUNLOCKED);
 +
   	return &lle->base;
   }
 
 
 Property changes on: sys/contrib/pf
 ___________________________________________________________________
 Modified: svn:mergeinfo
     Merged /head/sys/contrib/pf:r215418,215423,215559
 
 
 Property changes on: sys/contrib/dev/acpica
 ___________________________________________________________________
 Modified: svn:mergeinfo
     Merged /head/sys/contrib/dev/acpica:r215418,215423,215559
 
 
 Property changes on: sys/cddl/contrib/opensolaris
 ___________________________________________________________________
 Modified: svn:mergeinfo
     Merged /head/sys/cddl/contrib/opensolaris:r215418,215423,215559
 
 
 Property changes on: sys/amd64/include/xen
 ___________________________________________________________________
 Modified: svn:mergeinfo
     Merged /head/sys/amd64/include/xen:r215418,215423,215559
 
 
 
 
 -- 
 Bjoern A. Zeeb                              Welcome a new stage of life.
          <ks> Going to jail sucks -- <bz> All my daemons like it!
    http://www.freebsd.org/doc/en_US.ISO8859-1/books/handbook/jails.html
State-Changed-From-To: open->feedback 
State-Changed-By: bz 
State-Changed-When: Sun Nov 21 16:43:00 UTC 2010 
State-Changed-Why:  
An updated patch has been suggested. 

http://www.freebsd.org/cgi/query-pr.cgi?pr=148857 
State-Changed-From-To: feedback->patched 
State-Changed-By: bz 
State-Changed-When: Mon Nov 29 00:05:52 UTC 2010 
State-Changed-Why:  
Comitted to HEAD. Would love to see review/testing before merging. 

In case you want to test on stable/8 of today or later, please 
use the patch formerly generated or now comitted to HEAD. The old 
stable/8 patch will no longer apply. 

http://www.freebsd.org/cgi/query-pr.cgi?pr=148857 

From: dfilter@FreeBSD.ORG (dfilter service)
To: bug-followup@FreeBSD.org
Cc:  
Subject: Re: kern/148857: commit references a PR
Date: Mon, 29 Nov 2010 00:04:18 +0000 (UTC)

 Author: bz
 Date: Mon Nov 29 00:04:08 2010
 New Revision: 216022
 URL: http://svn.freebsd.org/changeset/base/216022
 
 Log:
   Plug well observed races on la_hold entries with the callout handler.
   
   Call the handler function with the lock held, return unlocked as we
   might free the entry.  Rework functions later in the call graph to be
   either called with the lock held or, only if needed, unlocked.
   
   Place asserts to document and tighten assumptions on various lle locking,
   which were not always true before.
   
   We call nd6_ns_output() unlocked and the assignment of ip6->ip6_src was
   decentralized to minimize possible complexity introduced with the formerly
   missing locking there.  This also resulted in a push down of local
   variable scopes into smaller blocks.
   
   Reported by:	many
   PR:		kern/148857
   Submitted by:	Dmitrij Tejblum (tejblum yandex-team.ru) (original version)
   MFC After:	4 days
 
 Modified:
   head/sys/netinet6/in6.c
   head/sys/netinet6/nd6.c
   head/sys/netinet6/nd6_nbr.c
 
 Modified: head/sys/netinet6/in6.c
 ==============================================================================
 --- head/sys/netinet6/in6.c	Sun Nov 28 23:34:20 2010	(r216021)
 +++ head/sys/netinet6/in6.c	Mon Nov 29 00:04:08 2010	(r216022)
 @@ -2349,10 +2349,12 @@ in6_lltable_new(const struct sockaddr *l
  	if (lle == NULL)		/* NB: caller generates msg */
  		return NULL;
  
 -	callout_init(&lle->base.ln_timer_ch, CALLOUT_MPSAFE);
  	lle->l3_addr6 = *(const struct sockaddr_in6 *)l3addr;
  	lle->base.lle_refcnt = 1;
  	LLE_LOCK_INIT(&lle->base);
 +	callout_init_rw(&lle->base.ln_timer_ch, &lle->base.lle_lock,
 +	    CALLOUT_RETURNUNLOCKED);
 +
  	return &lle->base;
  }
  
 
 Modified: head/sys/netinet6/nd6.c
 ==============================================================================
 --- head/sys/netinet6/nd6.c	Sun Nov 28 23:34:20 2010	(r216021)
 +++ head/sys/netinet6/nd6.c	Mon Nov 29 00:04:08 2010	(r216022)
 @@ -411,6 +411,8 @@ nd6_llinfo_settimer_locked(struct llentr
  {
  	int canceled;
  
 +	LLE_WLOCK_ASSERT(ln);
 +
  	if (tick < 0) {
  		ln->la_expire = 0;
  		ln->ln_ntick = 0;
 @@ -451,6 +453,7 @@ nd6_llinfo_timer(void *arg)
  
  	KASSERT(arg != NULL, ("%s: arg NULL", __func__));
  	ln = (struct llentry *)arg;
 +	LLE_WLOCK_ASSERT(ln);
  	ifp = ln->lle_tbl->llt_ifp;
  
  	CURVNET_SET(ifp->if_vnet);
 @@ -458,10 +461,10 @@ nd6_llinfo_timer(void *arg)
  	if (ln->ln_ntick > 0) {
  		if (ln->ln_ntick > INT_MAX) {
  			ln->ln_ntick -= INT_MAX;
 -			nd6_llinfo_settimer(ln, INT_MAX);
 +			nd6_llinfo_settimer_locked(ln, INT_MAX);
  		} else {
  			ln->ln_ntick = 0;
 -			nd6_llinfo_settimer(ln, ln->ln_ntick);
 +			nd6_llinfo_settimer_locked(ln, ln->ln_ntick);
  		}
  		goto done;
  	}
 @@ -482,8 +485,10 @@ nd6_llinfo_timer(void *arg)
  	case ND6_LLINFO_INCOMPLETE:
  		if (ln->la_asked < V_nd6_mmaxtries) {
  			ln->la_asked++;
 -			nd6_llinfo_settimer(ln, (long)ndi->retrans * hz / 1000);
 +			nd6_llinfo_settimer_locked(ln, (long)ndi->retrans * hz / 1000);
 +			LLE_WUNLOCK(ln);
  			nd6_ns_output(ifp, NULL, dst, ln, 0);
 +			LLE_WLOCK(ln);
  		} else {
  			struct mbuf *m = ln->la_hold;
  			if (m) {
 @@ -491,24 +496,24 @@ nd6_llinfo_timer(void *arg)
  
  				/*
  				 * assuming every packet in la_hold has the
 -				 * same IP header
 +				 * same IP header.  Send error after unlock.
  				 */
  				m0 = m->m_nextpkt;
  				m->m_nextpkt = NULL;
 -				icmp6_error2(m, ICMP6_DST_UNREACH,
 -				    ICMP6_DST_UNREACH_ADDR, 0, ifp);
 -
  				ln->la_hold = m0;
  				clear_llinfo_pqueue(ln);
  			}
  			(void)nd6_free(ln, 0);
  			ln = NULL;
 +			if (m != NULL)
 +				icmp6_error2(m, ICMP6_DST_UNREACH,
 +				    ICMP6_DST_UNREACH_ADDR, 0, ifp);
  		}
  		break;
  	case ND6_LLINFO_REACHABLE:
  		if (!ND6_LLINFO_PERMANENT(ln)) {
  			ln->ln_state = ND6_LLINFO_STALE;
 -			nd6_llinfo_settimer(ln, (long)V_nd6_gctimer * hz);
 +			nd6_llinfo_settimer_locked(ln, (long)V_nd6_gctimer * hz);
  		}
  		break;
  
 @@ -525,27 +530,34 @@ nd6_llinfo_timer(void *arg)
  			/* We need NUD */
  			ln->la_asked = 1;
  			ln->ln_state = ND6_LLINFO_PROBE;
 -			nd6_llinfo_settimer(ln, (long)ndi->retrans * hz / 1000);
 +			nd6_llinfo_settimer_locked(ln, (long)ndi->retrans * hz / 1000);
 +			LLE_WUNLOCK(ln);
  			nd6_ns_output(ifp, dst, dst, ln, 0);
 +			LLE_WLOCK(ln);
  		} else {
  			ln->ln_state = ND6_LLINFO_STALE; /* XXX */
 -			nd6_llinfo_settimer(ln, (long)V_nd6_gctimer * hz);
 +			nd6_llinfo_settimer_locked(ln, (long)V_nd6_gctimer * hz);
  		}
  		break;
  	case ND6_LLINFO_PROBE:
  		if (ln->la_asked < V_nd6_umaxtries) {
  			ln->la_asked++;
 -			nd6_llinfo_settimer(ln, (long)ndi->retrans * hz / 1000);
 +			nd6_llinfo_settimer_locked(ln, (long)ndi->retrans * hz / 1000);
 +			LLE_WUNLOCK(ln);
  			nd6_ns_output(ifp, dst, dst, ln, 0);
 +			LLE_WLOCK(ln);
  		} else {
  			(void)nd6_free(ln, 0);
  			ln = NULL;
  		}
  		break;
 +	default:
 +		panic("%s: paths in a dark night can be confusing: %d",
 +		    __func__, ln->ln_state);
  	}
  done:
  	if (ln != NULL)
 -		LLE_FREE(ln);
 +		LLE_FREE_LOCKED(ln);
  	CURVNET_RESTORE();
  }
  
 @@ -996,7 +1008,9 @@ nd6_free(struct llentry *ln, int gc)
  {
          struct llentry *next;
  	struct nd_defrouter *dr;
 -	struct ifnet *ifp=NULL;
 +	struct ifnet *ifp;
 +
 +	LLE_WLOCK_ASSERT(ln);
  
  	/*
  	 * we used to have pfctlinput(PRC_HOSTDEAD) here.
 @@ -1004,12 +1018,13 @@ nd6_free(struct llentry *ln, int gc)
  	 */
  
  	/* cancel timer */
 -	nd6_llinfo_settimer(ln, -1);
 +	nd6_llinfo_settimer_locked(ln, -1);
 +
 +	ifp = ln->lle_tbl->llt_ifp;
  
  	if (!V_ip6_forwarding) {
 -		int s;
 -		s = splnet();
 -		dr = defrouter_lookup(&L3_ADDR_SIN6(ln)->sin6_addr, ln->lle_tbl->llt_ifp);
 +
 +		dr = defrouter_lookup(&L3_ADDR_SIN6(ln)->sin6_addr, ifp);
  
  		if (dr != NULL && dr->expire &&
  		    ln->ln_state == ND6_LLINFO_STALE && gc) {
 @@ -1026,15 +1041,16 @@ nd6_free(struct llentry *ln, int gc)
  			 *      but we intentionally keep it just in case.
  			 */
  			if (dr->expire > time_second)
 -				nd6_llinfo_settimer(ln,
 +				nd6_llinfo_settimer_locked(ln,
  				    (dr->expire - time_second) * hz);
  			else
 -				nd6_llinfo_settimer(ln, (long)V_nd6_gctimer * hz);
 -			splx(s);
 -			LLE_WLOCK(ln);
 +				nd6_llinfo_settimer_locked(ln,
 +				    (long)V_nd6_gctimer * hz);
 +
 +			next = LIST_NEXT(ln, lle_next);
  			LLE_REMREF(ln);
  			LLE_WUNLOCK(ln);
 -			return (LIST_NEXT(ln, lle_next));
 +			return (next);
  		}
  
  		if (ln->ln_router || dr) {
 @@ -1043,7 +1059,7 @@ nd6_free(struct llentry *ln, int gc)
  			 * is in the Default Router List.
  			 * See a corresponding comment in nd6_na_input().
  			 */
 -			rt6_flush(&L3_ADDR_SIN6(ln)->sin6_addr, ln->lle_tbl->llt_ifp);
 +			rt6_flush(&L3_ADDR_SIN6(ln)->sin6_addr, ifp);
  		}
  
  		if (dr) {
 @@ -1071,11 +1087,13 @@ nd6_free(struct llentry *ln, int gc)
  			pfxlist_onlink_check();
  
  			/*
 -			 * refresh default router list
 +			 * Refresh default router list.  Have to unlock as
 +			 * it calls into nd6_lookup(), still holding a ref.
  			 */
 +			LLE_WUNLOCK(ln);
  			defrouter_select();
 +			LLE_WLOCK(ln);
  		}
 -		splx(s);
  	}
  
  	/*
 @@ -1086,7 +1104,11 @@ nd6_free(struct llentry *ln, int gc)
  	 */
  	next = LIST_NEXT(ln, lle_next);
  
 -	ifp = ln->lle_tbl->llt_ifp;
 +	/*
 +	 * Save to unlock. We still hold an extra reference and will not
 +	 * free(9) in llentry_free() if someone else holds one as well.
 +	 */
 +	LLE_WUNLOCK(ln);
  	IF_AFDATA_LOCK(ifp);
  	LLE_WLOCK(ln);
  	LLE_REMREF(ln);
 @@ -1875,6 +1897,9 @@ nd6_output_lle(struct ifnet *ifp, struct
  		LLE_RUNLOCK(ln);
  		goto retry;
  	}
 +
 +	LLE_WLOCK_ASSERT(ln);
 +
  	if (ln->la_hold) {
  		struct mbuf *m_hold;
  		int i;
 @@ -1896,17 +1921,7 @@ nd6_output_lle(struct ifnet *ifp, struct
  	} else {
  		ln->la_hold = m;
  	}
 -	/*
 -	 * We did the lookup (no lle arg) so we
 -	 * need to do the unlock here
 -	 */
 -	if (lle == NULL) {
 -		if (flags & LLE_EXCLUSIVE)
 -			LLE_WUNLOCK(ln);
 -		else
 -			LLE_RUNLOCK(ln);
 -	}
 -	
 +
  	/*
  	 * If there has been no NS for the neighbor after entering the
  	 * INCOMPLETE state, send the first solicitation.
 @@ -1914,10 +1929,21 @@ nd6_output_lle(struct ifnet *ifp, struct
  	if (!ND6_LLINFO_PERMANENT(ln) && ln->la_asked == 0) {
  		ln->la_asked++;
  		
 -		nd6_llinfo_settimer(ln,
 +		nd6_llinfo_settimer_locked(ln,
  		    (long)ND_IFINFO(ifp)->retrans * hz / 1000);
 +		LLE_WUNLOCK(ln);
  		nd6_ns_output(ifp, NULL, &dst->sin6_addr, ln, 0);
 +		if (lle != NULL && ln == lle)
 +			LLE_WLOCK(lle);
 +
 +	} else if (lle == NULL || ln != lle) {
 +		/*
 +		 * We did the lookup (no lle arg) so we
 +		 * need to do the unlock here.
 +		 */
 +		LLE_WUNLOCK(ln);
  	}
 +
  	return (0);
  
    sendpkt:
 
 Modified: head/sys/netinet6/nd6_nbr.c
 ==============================================================================
 --- head/sys/netinet6/nd6_nbr.c	Sun Nov 28 23:34:20 2010	(r216021)
 +++ head/sys/netinet6/nd6_nbr.c	Mon Nov 29 00:04:08 2010	(r216022)
 @@ -383,7 +383,6 @@ nd6_ns_output(struct ifnet *ifp, const s
  	struct m_tag *mtag;
  	struct ip6_hdr *ip6;
  	struct nd_neighbor_solicit *nd_ns;
 -	struct in6_addr *src, src_in;
  	struct ip6_moptions im6o;
  	int icmp6len;
  	int maxlen;
 @@ -467,28 +466,35 @@ nd6_ns_output(struct ifnet *ifp, const s
  		 * - saddr6 belongs to the outgoing interface.
  		 * Otherwise, we perform the source address selection as usual.
  		 */
 -		struct ip6_hdr *hip6;		/* hold ip6 */
 -		struct in6_addr *hsrc = NULL;
 +		struct in6_addr *hsrc;
  
 -		if ((ln != NULL) && ln->la_hold) {
 -			/*
 -			 * assuming every packet in la_hold has the same IP
 -			 * header
 -			 */
 -			hip6 = mtod(ln->la_hold, struct ip6_hdr *);
 -			/* XXX pullup? */
 -			if (sizeof(*hip6) < ln->la_hold->m_len)
 -				hsrc = &hip6->ip6_src;
 -			else
 -				hsrc = NULL;
 +		hsrc = NULL;
 +		if (ln != NULL) {
 +			LLE_RLOCK(ln);
 +			if (ln->la_hold != NULL) {
 +				struct ip6_hdr *hip6;		/* hold ip6 */
 +
 +				/*
 +				 * assuming every packet in la_hold has the same IP
 +				 * header
 +				 */
 +				hip6 = mtod(ln->la_hold, struct ip6_hdr *);
 +				/* XXX pullup? */
 +				if (sizeof(*hip6) < ln->la_hold->m_len) {
 +					ip6->ip6_src = hip6->ip6_src;
 +					hsrc = &hip6->ip6_src;
 +				}
 +			}
 +			LLE_RUNLOCK(ln);
  		}
  		if (hsrc && (ifa = (struct ifaddr *)in6ifa_ifpwithaddr(ifp,
  		    hsrc)) != NULL) {
 -			src = hsrc;
 +			/* ip6_src set already. */
  			ifa_free(ifa);
  		} else {
  			int error;
  			struct sockaddr_in6 dst_sa;
 +			struct in6_addr src_in;
  
  			bzero(&dst_sa, sizeof(dst_sa));
  			dst_sa.sin6_family = AF_INET6;
 @@ -506,7 +512,7 @@ nd6_ns_output(struct ifnet *ifp, const s
  				    error));
  				goto bad;
  			}
 -			src = &src_in;
 +			ip6->ip6_src = src_in;
  		}
  	} else {
  		/*
 @@ -516,10 +522,8 @@ nd6_ns_output(struct ifnet *ifp, const s
  		 * above), but we do so here explicitly to make the intention
  		 * clearer.
  		 */
 -		bzero(&src_in, sizeof(src_in));
 -		src = &src_in;
 +		bzero(&ip6->ip6_src, sizeof(ip6->ip6_src));
  	}
 -	ip6->ip6_src = *src;
  	nd_ns = (struct nd_neighbor_solicit *)(ip6 + 1);
  	nd_ns->nd_ns_type = ND_NEIGHBOR_SOLICIT;
  	nd_ns->nd_ns_code = 0;
 _______________________________________________
 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-unsubscribe@freebsd.org"
 

From: dfilter@FreeBSD.ORG (dfilter service)
To: bug-followup@FreeBSD.org
Cc:  
Subject: Re: kern/148857: commit references a PR
Date: Thu,  2 Dec 2010 10:39:53 +0000 (UTC)

 Author: bz
 Date: Thu Dec  2 10:39:44 2010
 New Revision: 216118
 URL: http://svn.freebsd.org/changeset/base/216118
 
 Log:
   MFC r216022:
   
     Plug well observed races on la_hold entries with the callout handler.
   
     Call the handler function with the lock held, return unlocked as we
     might free the entry. Rework functions later in the call graph to be
     either called with the lock held or, only if needed, unlocked.
   
     Place asserts to document and tighten assumptions on various lle locking,
     which were not always true before.
   
     We call nd6_ns_output() unlocked and the assignment of ip6->ip6_src was
     decentralized to minimize possible complexity introduced with the formerly
     missing locking there. This also resulted in a push down of local
     variable scopes into smaller blocks.
   
     Reported by:	many
     Submitted by:	Dmitrij Tejblum (tejblum yandex-team.ru) (original version)
   Tested by:	remko
   PR:		kern/148857
   Approved by:	re (kib)
 
 Modified:
   stable/8/sys/netinet6/in6.c
   stable/8/sys/netinet6/nd6.c
   stable/8/sys/netinet6/nd6_nbr.c
 Directory Properties:
   stable/8/sys/   (props changed)
   stable/8/sys/amd64/include/xen/   (props changed)
   stable/8/sys/cddl/contrib/opensolaris/   (props changed)
   stable/8/sys/contrib/dev/acpica/   (props changed)
   stable/8/sys/contrib/pf/   (props changed)
 
 Modified: stable/8/sys/netinet6/in6.c
 ==============================================================================
 --- stable/8/sys/netinet6/in6.c	Thu Dec  2 09:10:45 2010	(r216117)
 +++ stable/8/sys/netinet6/in6.c	Thu Dec  2 10:39:44 2010	(r216118)
 @@ -2325,10 +2325,12 @@ in6_lltable_new(const struct sockaddr *l
  	if (lle == NULL)		/* NB: caller generates msg */
  		return NULL;
  
 -	callout_init(&lle->base.ln_timer_ch, CALLOUT_MPSAFE);
  	lle->l3_addr6 = *(const struct sockaddr_in6 *)l3addr;
  	lle->base.lle_refcnt = 1;
  	LLE_LOCK_INIT(&lle->base);
 +	callout_init_rw(&lle->base.ln_timer_ch, &lle->base.lle_lock,
 +	    CALLOUT_RETURNUNLOCKED);
 +
  	return &lle->base;
  }
  
 
 Modified: stable/8/sys/netinet6/nd6.c
 ==============================================================================
 --- stable/8/sys/netinet6/nd6.c	Thu Dec  2 09:10:45 2010	(r216117)
 +++ stable/8/sys/netinet6/nd6.c	Thu Dec  2 10:39:44 2010	(r216118)
 @@ -403,6 +403,8 @@ nd6_llinfo_settimer_locked(struct llentr
  {
  	int canceled;
  
 +	LLE_WLOCK_ASSERT(ln);
 +
  	if (tick < 0) {
  		ln->la_expire = 0;
  		ln->ln_ntick = 0;
 @@ -443,6 +445,7 @@ nd6_llinfo_timer(void *arg)
  
  	KASSERT(arg != NULL, ("%s: arg NULL", __func__));
  	ln = (struct llentry *)arg;
 +	LLE_WLOCK_ASSERT(ln);
  	ifp = ln->lle_tbl->llt_ifp;
  
  	CURVNET_SET(ifp->if_vnet);
 @@ -450,10 +453,10 @@ nd6_llinfo_timer(void *arg)
  	if (ln->ln_ntick > 0) {
  		if (ln->ln_ntick > INT_MAX) {
  			ln->ln_ntick -= INT_MAX;
 -			nd6_llinfo_settimer(ln, INT_MAX);
 +			nd6_llinfo_settimer_locked(ln, INT_MAX);
  		} else {
  			ln->ln_ntick = 0;
 -			nd6_llinfo_settimer(ln, ln->ln_ntick);
 +			nd6_llinfo_settimer_locked(ln, ln->ln_ntick);
  		}
  		goto done;
  	}
 @@ -474,8 +477,10 @@ nd6_llinfo_timer(void *arg)
  	case ND6_LLINFO_INCOMPLETE:
  		if (ln->la_asked < V_nd6_mmaxtries) {
  			ln->la_asked++;
 -			nd6_llinfo_settimer(ln, (long)ndi->retrans * hz / 1000);
 +			nd6_llinfo_settimer_locked(ln, (long)ndi->retrans * hz / 1000);
 +			LLE_WUNLOCK(ln);
  			nd6_ns_output(ifp, NULL, dst, ln, 0);
 +			LLE_WLOCK(ln);
  		} else {
  			struct mbuf *m = ln->la_hold;
  			if (m) {
 @@ -483,24 +488,24 @@ nd6_llinfo_timer(void *arg)
  
  				/*
  				 * assuming every packet in la_hold has the
 -				 * same IP header
 +				 * same IP header.  Send error after unlock.
  				 */
  				m0 = m->m_nextpkt;
  				m->m_nextpkt = NULL;
 -				icmp6_error2(m, ICMP6_DST_UNREACH,
 -				    ICMP6_DST_UNREACH_ADDR, 0, ifp);
 -
  				ln->la_hold = m0;
  				clear_llinfo_pqueue(ln);
  			}
  			(void)nd6_free(ln, 0);
  			ln = NULL;
 +			if (m != NULL)
 +				icmp6_error2(m, ICMP6_DST_UNREACH,
 +				    ICMP6_DST_UNREACH_ADDR, 0, ifp);
  		}
  		break;
  	case ND6_LLINFO_REACHABLE:
  		if (!ND6_LLINFO_PERMANENT(ln)) {
  			ln->ln_state = ND6_LLINFO_STALE;
 -			nd6_llinfo_settimer(ln, (long)V_nd6_gctimer * hz);
 +			nd6_llinfo_settimer_locked(ln, (long)V_nd6_gctimer * hz);
  		}
  		break;
  
 @@ -517,27 +522,34 @@ nd6_llinfo_timer(void *arg)
  			/* We need NUD */
  			ln->la_asked = 1;
  			ln->ln_state = ND6_LLINFO_PROBE;
 -			nd6_llinfo_settimer(ln, (long)ndi->retrans * hz / 1000);
 +			nd6_llinfo_settimer_locked(ln, (long)ndi->retrans * hz / 1000);
 +			LLE_WUNLOCK(ln);
  			nd6_ns_output(ifp, dst, dst, ln, 0);
 +			LLE_WLOCK(ln);
  		} else {
  			ln->ln_state = ND6_LLINFO_STALE; /* XXX */
 -			nd6_llinfo_settimer(ln, (long)V_nd6_gctimer * hz);
 +			nd6_llinfo_settimer_locked(ln, (long)V_nd6_gctimer * hz);
  		}
  		break;
  	case ND6_LLINFO_PROBE:
  		if (ln->la_asked < V_nd6_umaxtries) {
  			ln->la_asked++;
 -			nd6_llinfo_settimer(ln, (long)ndi->retrans * hz / 1000);
 +			nd6_llinfo_settimer_locked(ln, (long)ndi->retrans * hz / 1000);
 +			LLE_WUNLOCK(ln);
  			nd6_ns_output(ifp, dst, dst, ln, 0);
 +			LLE_WLOCK(ln);
  		} else {
  			(void)nd6_free(ln, 0);
  			ln = NULL;
  		}
  		break;
 +	default:
 +		panic("%s: paths in a dark night can be confusing: %d",
 +		    __func__, ln->ln_state);
  	}
  done:
  	if (ln != NULL)
 -		LLE_FREE(ln);
 +		LLE_FREE_LOCKED(ln);
  	CURVNET_RESTORE();
  }
  
 @@ -992,7 +1004,9 @@ nd6_free(struct llentry *ln, int gc)
  {
          struct llentry *next;
  	struct nd_defrouter *dr;
 -	struct ifnet *ifp=NULL;
 +	struct ifnet *ifp;
 +
 +	LLE_WLOCK_ASSERT(ln);
  
  	/*
  	 * we used to have pfctlinput(PRC_HOSTDEAD) here.
 @@ -1000,12 +1014,13 @@ nd6_free(struct llentry *ln, int gc)
  	 */
  
  	/* cancel timer */
 -	nd6_llinfo_settimer(ln, -1);
 +	nd6_llinfo_settimer_locked(ln, -1);
 +
 +	ifp = ln->lle_tbl->llt_ifp;
  
  	if (!V_ip6_forwarding) {
 -		int s;
 -		s = splnet();
 -		dr = defrouter_lookup(&L3_ADDR_SIN6(ln)->sin6_addr, ln->lle_tbl->llt_ifp);
 +
 +		dr = defrouter_lookup(&L3_ADDR_SIN6(ln)->sin6_addr, ifp);
  
  		if (dr != NULL && dr->expire &&
  		    ln->ln_state == ND6_LLINFO_STALE && gc) {
 @@ -1022,15 +1037,16 @@ nd6_free(struct llentry *ln, int gc)
  			 *      but we intentionally keep it just in case.
  			 */
  			if (dr->expire > time_second)
 -				nd6_llinfo_settimer(ln,
 +				nd6_llinfo_settimer_locked(ln,
  				    (dr->expire - time_second) * hz);
  			else
 -				nd6_llinfo_settimer(ln, (long)V_nd6_gctimer * hz);
 -			splx(s);
 -			LLE_WLOCK(ln);
 +				nd6_llinfo_settimer_locked(ln,
 +				    (long)V_nd6_gctimer * hz);
 +
 +			next = LIST_NEXT(ln, lle_next);
  			LLE_REMREF(ln);
  			LLE_WUNLOCK(ln);
 -			return (LIST_NEXT(ln, lle_next));
 +			return (next);
  		}
  
  		if (ln->ln_router || dr) {
 @@ -1039,7 +1055,7 @@ nd6_free(struct llentry *ln, int gc)
  			 * is in the Default Router List.
  			 * See a corresponding comment in nd6_na_input().
  			 */
 -			rt6_flush(&L3_ADDR_SIN6(ln)->sin6_addr, ln->lle_tbl->llt_ifp);
 +			rt6_flush(&L3_ADDR_SIN6(ln)->sin6_addr, ifp);
  		}
  
  		if (dr) {
 @@ -1067,11 +1083,13 @@ nd6_free(struct llentry *ln, int gc)
  			pfxlist_onlink_check();
  
  			/*
 -			 * refresh default router list
 +			 * Refresh default router list.  Have to unlock as
 +			 * it calls into nd6_lookup(), still holding a ref.
  			 */
 +			LLE_WUNLOCK(ln);
  			defrouter_select();
 +			LLE_WLOCK(ln);
  		}
 -		splx(s);
  	}
  
  	/*
 @@ -1082,7 +1100,11 @@ nd6_free(struct llentry *ln, int gc)
  	 */
  	next = LIST_NEXT(ln, lle_next);
  
 -	ifp = ln->lle_tbl->llt_ifp;
 +	/*
 +	 * Save to unlock. We still hold an extra reference and will not
 +	 * free(9) in llentry_free() if someone else holds one as well.
 +	 */
 +	LLE_WUNLOCK(ln);
  	IF_AFDATA_LOCK(ifp);
  	LLE_WLOCK(ln);
  	LLE_REMREF(ln);
 @@ -1804,6 +1826,9 @@ nd6_output_lle(struct ifnet *ifp, struct
  		LLE_RUNLOCK(ln);
  		goto retry;
  	}
 +
 +	LLE_WLOCK_ASSERT(ln);
 +
  	if (ln->la_hold) {
  		struct mbuf *m_hold;
  		int i;
 @@ -1825,17 +1850,7 @@ nd6_output_lle(struct ifnet *ifp, struct
  	} else {
  		ln->la_hold = m;
  	}
 -	/*
 -	 * We did the lookup (no lle arg) so we
 -	 * need to do the unlock here
 -	 */
 -	if (lle == NULL) {
 -		if (flags & LLE_EXCLUSIVE)
 -			LLE_WUNLOCK(ln);
 -		else
 -			LLE_RUNLOCK(ln);
 -	}
 -	
 +
  	/*
  	 * If there has been no NS for the neighbor after entering the
  	 * INCOMPLETE state, send the first solicitation.
 @@ -1843,10 +1858,21 @@ nd6_output_lle(struct ifnet *ifp, struct
  	if (!ND6_LLINFO_PERMANENT(ln) && ln->la_asked == 0) {
  		ln->la_asked++;
  		
 -		nd6_llinfo_settimer(ln,
 +		nd6_llinfo_settimer_locked(ln,
  		    (long)ND_IFINFO(ifp)->retrans * hz / 1000);
 +		LLE_WUNLOCK(ln);
  		nd6_ns_output(ifp, NULL, &dst->sin6_addr, ln, 0);
 +		if (lle != NULL && ln == lle)
 +			LLE_WLOCK(lle);
 +
 +	} else if (lle == NULL || ln != lle) {
 +		/*
 +		 * We did the lookup (no lle arg) so we
 +		 * need to do the unlock here.
 +		 */
 +		LLE_WUNLOCK(ln);
  	}
 +
  	return (0);
  
    sendpkt:
 
 Modified: stable/8/sys/netinet6/nd6_nbr.c
 ==============================================================================
 --- stable/8/sys/netinet6/nd6_nbr.c	Thu Dec  2 09:10:45 2010	(r216117)
 +++ stable/8/sys/netinet6/nd6_nbr.c	Thu Dec  2 10:39:44 2010	(r216118)
 @@ -381,7 +381,6 @@ nd6_ns_output(struct ifnet *ifp, const s
  	struct mbuf *m;
  	struct ip6_hdr *ip6;
  	struct nd_neighbor_solicit *nd_ns;
 -	struct in6_addr *src, src_in;
  	struct ip6_moptions im6o;
  	int icmp6len;
  	int maxlen;
 @@ -465,28 +464,35 @@ nd6_ns_output(struct ifnet *ifp, const s
  		 * - saddr6 belongs to the outgoing interface.
  		 * Otherwise, we perform the source address selection as usual.
  		 */
 -		struct ip6_hdr *hip6;		/* hold ip6 */
 -		struct in6_addr *hsrc = NULL;
 +		struct in6_addr *hsrc;
  
 -		if ((ln != NULL) && ln->la_hold) {
 -			/*
 -			 * assuming every packet in la_hold has the same IP
 -			 * header
 -			 */
 -			hip6 = mtod(ln->la_hold, struct ip6_hdr *);
 -			/* XXX pullup? */
 -			if (sizeof(*hip6) < ln->la_hold->m_len)
 -				hsrc = &hip6->ip6_src;
 -			else
 -				hsrc = NULL;
 +		hsrc = NULL;
 +		if (ln != NULL) {
 +			LLE_RLOCK(ln);
 +			if (ln->la_hold != NULL) {
 +				struct ip6_hdr *hip6;		/* hold ip6 */
 +
 +				/*
 +				 * assuming every packet in la_hold has the same IP
 +				 * header
 +				 */
 +				hip6 = mtod(ln->la_hold, struct ip6_hdr *);
 +				/* XXX pullup? */
 +				if (sizeof(*hip6) < ln->la_hold->m_len) {
 +					ip6->ip6_src = hip6->ip6_src;
 +					hsrc = &hip6->ip6_src;
 +				}
 +			}
 +			LLE_RUNLOCK(ln);
  		}
  		if (hsrc && (ifa = (struct ifaddr *)in6ifa_ifpwithaddr(ifp,
  		    hsrc)) != NULL) {
 -			src = hsrc;
 +			/* ip6_src set already. */
  			ifa_free(ifa);
  		} else {
  			int error;
  			struct sockaddr_in6 dst_sa;
 +			struct in6_addr src_in;
  
  			bzero(&dst_sa, sizeof(dst_sa));
  			dst_sa.sin6_family = AF_INET6;
 @@ -504,7 +510,7 @@ nd6_ns_output(struct ifnet *ifp, const s
  				    error));
  				goto bad;
  			}
 -			src = &src_in;
 +			ip6->ip6_src = src_in;
  		}
  	} else {
  		/*
 @@ -514,10 +520,8 @@ nd6_ns_output(struct ifnet *ifp, const s
  		 * above), but we do so here explicitly to make the intention
  		 * clearer.
  		 */
 -		bzero(&src_in, sizeof(src_in));
 -		src = &src_in;
 +		bzero(&ip6->ip6_src, sizeof(ip6->ip6_src));
  	}
 -	ip6->ip6_src = *src;
  	nd_ns = (struct nd_neighbor_solicit *)(ip6 + 1);
  	nd_ns->nd_ns_type = ND_NEIGHBOR_SOLICIT;
  	nd_ns->nd_ns_code = 0;
 _______________________________________________
 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-unsubscribe@freebsd.org"
 
State-Changed-From-To: patched->closed 
State-Changed-By: bz 
State-Changed-When: Thu Dec 2 10:41:24 UTC 2010 
State-Changed-Why:  
Change was merged to stable/8 and will be part of the upcoming 8.2-R. 
Thanks for reporting and the patch! 

http://www.freebsd.org/cgi/query-pr.cgi?pr=148857 

From: dfilter@FreeBSD.ORG (dfilter service)
To: bug-followup@FreeBSD.org
Cc:  
Subject: Re: kern/148857: commit references a PR
Date: Tue,  7 Dec 2010 22:43:33 +0000 (UTC)

 Author: bz
 Date: Tue Dec  7 22:43:29 2010
 New Revision: 216277
 URL: http://svn.freebsd.org/changeset/base/216277
 
 Log:
   Loosen the locking in nd6-free() again after r216022 to avoid
   a LOR and a recursed lock.
   
   Reported by:	delphij
   Tested by:	delphij
   PR:		kern/148857
   MFC After:	3 days
 
 Modified:
   head/sys/netinet6/nd6.c
 
 Modified: head/sys/netinet6/nd6.c
 ==============================================================================
 --- head/sys/netinet6/nd6.c	Tue Dec  7 22:43:25 2010	(r216276)
 +++ head/sys/netinet6/nd6.c	Tue Dec  7 22:43:29 2010	(r216277)
 @@ -1053,15 +1053,6 @@ nd6_free(struct llentry *ln, int gc)
  			return (next);
  		}
  
 -		if (ln->ln_router || dr) {
 -			/*
 -			 * rt6_flush must be called whether or not the neighbor
 -			 * is in the Default Router List.
 -			 * See a corresponding comment in nd6_na_input().
 -			 */
 -			rt6_flush(&L3_ADDR_SIN6(ln)->sin6_addr, ifp);
 -		}
 -
  		if (dr) {
  			/*
  			 * Unreachablity of a router might affect the default
 @@ -1077,8 +1068,28 @@ nd6_free(struct llentry *ln, int gc)
  			 * or the entry itself will be deleted.
  			 */
  			ln->ln_state = ND6_LLINFO_INCOMPLETE;
 +		}
 +
 +		if (ln->ln_router || dr) {
  
  			/*
 +			 * We need to unlock to avoid a LOR with rt6_flush() with the
 +			 * rnh and for the calls to pfxlist_onlink_check() and
 +			 * defrouter_select() in the block further down for calls
 +			 * into nd6_lookup().  We still hold a ref.
 +			 */
 +			LLE_WUNLOCK(ln);
 +
 +			/*
 +			 * rt6_flush must be called whether or not the neighbor
 +			 * is in the Default Router List.
 +			 * See a corresponding comment in nd6_na_input().
 +			 */
 +			rt6_flush(&L3_ADDR_SIN6(ln)->sin6_addr, ifp);
 +		}
 +
 +		if (dr) {
 +			/*
  			 * Since defrouter_select() does not affect the
  			 * on-link determination and MIP6 needs the check
  			 * before the default router selection, we perform
 @@ -1087,13 +1098,13 @@ nd6_free(struct llentry *ln, int gc)
  			pfxlist_onlink_check();
  
  			/*
 -			 * Refresh default router list.  Have to unlock as
 -			 * it calls into nd6_lookup(), still holding a ref.
 +			 * Refresh default router list.
  			 */
 -			LLE_WUNLOCK(ln);
  			defrouter_select();
 -			LLE_WLOCK(ln);
  		}
 +
 +		if (ln->ln_router || dr)
 +			LLE_WLOCK(ln);
  	}
  
  	/*
 _______________________________________________
 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-unsubscribe@freebsd.org"
 

From: dfilter@FreeBSD.ORG (dfilter service)
To: bug-followup@FreeBSD.org
Cc:  
Subject: Re: kern/148857: commit references a PR
Date: Fri, 10 Dec 2010 15:38:00 +0000 (UTC)

 Author: bz
 Date: Fri Dec 10 15:37:54 2010
 New Revision: 216359
 URL: http://svn.freebsd.org/changeset/base/216359
 
 Log:
   MFC r216277:
   
     Loosen the locking in nd6-free() again after r216022 (r216118 in stable/8)
     to avoid a LOR and a recursed lock.
   
     Reported by:	delphij
     Tested by:	delphij
   PR:		kern/148857
   Approved by:	re (kib)
 
 Modified:
   stable/8/sys/netinet6/nd6.c
 Directory Properties:
   stable/8/sys/   (props changed)
   stable/8/sys/amd64/include/xen/   (props changed)
   stable/8/sys/cddl/contrib/opensolaris/   (props changed)
   stable/8/sys/contrib/dev/acpica/   (props changed)
   stable/8/sys/contrib/pf/   (props changed)
 
 Modified: stable/8/sys/netinet6/nd6.c
 ==============================================================================
 --- stable/8/sys/netinet6/nd6.c	Fri Dec 10 15:05:49 2010	(r216358)
 +++ stable/8/sys/netinet6/nd6.c	Fri Dec 10 15:37:54 2010	(r216359)
 @@ -1049,15 +1049,6 @@ nd6_free(struct llentry *ln, int gc)
  			return (next);
  		}
  
 -		if (ln->ln_router || dr) {
 -			/*
 -			 * rt6_flush must be called whether or not the neighbor
 -			 * is in the Default Router List.
 -			 * See a corresponding comment in nd6_na_input().
 -			 */
 -			rt6_flush(&L3_ADDR_SIN6(ln)->sin6_addr, ifp);
 -		}
 -
  		if (dr) {
  			/*
  			 * Unreachablity of a router might affect the default
 @@ -1073,8 +1064,28 @@ nd6_free(struct llentry *ln, int gc)
  			 * or the entry itself will be deleted.
  			 */
  			ln->ln_state = ND6_LLINFO_INCOMPLETE;
 +		}
 +
 +		if (ln->ln_router || dr) {
  
  			/*
 +			 * We need to unlock to avoid a LOR with rt6_flush() with the
 +			 * rnh and for the calls to pfxlist_onlink_check() and
 +			 * defrouter_select() in the block further down for calls
 +			 * into nd6_lookup().  We still hold a ref.
 +			 */
 +			LLE_WUNLOCK(ln);
 +
 +			/*
 +			 * rt6_flush must be called whether or not the neighbor
 +			 * is in the Default Router List.
 +			 * See a corresponding comment in nd6_na_input().
 +			 */
 +			rt6_flush(&L3_ADDR_SIN6(ln)->sin6_addr, ifp);
 +		}
 +
 +		if (dr) {
 +			/*
  			 * Since defrouter_select() does not affect the
  			 * on-link determination and MIP6 needs the check
  			 * before the default router selection, we perform
 @@ -1083,13 +1094,13 @@ nd6_free(struct llentry *ln, int gc)
  			pfxlist_onlink_check();
  
  			/*
 -			 * Refresh default router list.  Have to unlock as
 -			 * it calls into nd6_lookup(), still holding a ref.
 +			 * Refresh default router list.
  			 */
 -			LLE_WUNLOCK(ln);
  			defrouter_select();
 -			LLE_WLOCK(ln);
  		}
 +
 +		if (ln->ln_router || dr)
 +			LLE_WLOCK(ln);
  	}
  
  	/*
 _______________________________________________
 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-unsubscribe@freebsd.org"
 
>Unformatted:
