summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorglebius <glebius@FreeBSD.org>2012-12-13 11:11:15 +0000
committerglebius <glebius@FreeBSD.org>2012-12-13 11:11:15 +0000
commit8137816adb03dc4adf599f4790b2f2cecbe5f5b4 (patch)
treee234b73341b1557dce509188f3ea487c6df2c5fa
parent2d5902ca92bed1978312dee52ee4a8d78fb4046a (diff)
downloadFreeBSD-src-8137816adb03dc4adf599f4790b2f2cecbe5f5b4.zip
FreeBSD-src-8137816adb03dc4adf599f4790b2f2cecbe5f5b4.tar.gz
Fix problem in r238990. The LLE_LINKED flag should be tested prior to
entering llentry_free(), and in case if we lose the race, we should simply perform LLE_FREE_LOCKED(). Otherwise, if the race is lost by the thread performing arptimer(), it will remove two references from the lle instead of one. Reported by: Ian FREISLICH <ianf clue.co.za>
-rw-r--r--sys/net/if_llatbl.c6
-rw-r--r--sys/netinet/if_ether.c16
-rw-r--r--sys/netinet6/nd6.c10
3 files changed, 20 insertions, 12 deletions
diff --git a/sys/net/if_llatbl.c b/sys/net/if_llatbl.c
index bb49fdd..5812623 100644
--- a/sys/net/if_llatbl.c
+++ b/sys/net/if_llatbl.c
@@ -109,12 +109,6 @@ llentry_free(struct llentry *lle)
IF_AFDATA_WLOCK_ASSERT(lle->lle_tbl->llt_ifp);
LLE_WLOCK_ASSERT(lle);
- /* XXX: guard against race with other llentry_free(). */
- if (!(lle->la_flags & LLE_LINKED)) {
- LLE_FREE_LOCKED(lle);
- return (0);
- }
-
LIST_REMOVE(lle, lle_next);
lle->la_flags &= ~(LLE_VALID | LLE_LINKED);
diff --git a/sys/netinet/if_ether.c b/sys/netinet/if_ether.c
index 2d92815..eaa10aa 100644
--- a/sys/netinet/if_ether.c
+++ b/sys/netinet/if_ether.c
@@ -165,7 +165,6 @@ arptimer(void *arg)
{
struct llentry *lle = (struct llentry *)arg;
struct ifnet *ifp;
- size_t pkts_dropped;
if (lle->la_flags & LLE_STATIC) {
LLE_WUNLOCK(lle);
@@ -192,11 +191,20 @@ arptimer(void *arg)
IF_AFDATA_LOCK(ifp);
LLE_WLOCK(lle);
- LLE_REMREF(lle);
- pkts_dropped = llentry_free(lle);
+ /* Guard against race with other llentry_free(). */
+ if (lle->la_flags & LLE_LINKED) {
+ size_t pkts_dropped;
+
+ LLE_REMREF(lle);
+ pkts_dropped = llentry_free(lle);
+ ARPSTAT_ADD(dropped, pkts_dropped);
+ } else
+ LLE_FREE_LOCKED(lle);
+
IF_AFDATA_UNLOCK(ifp);
- ARPSTAT_ADD(dropped, pkts_dropped);
+
ARPSTAT_INC(timeouts);
+
CURVNET_RESTORE();
}
diff --git a/sys/netinet6/nd6.c b/sys/netinet6/nd6.c
index 9993c77..5107f1d 100644
--- a/sys/netinet6/nd6.c
+++ b/sys/netinet6/nd6.c
@@ -1108,8 +1108,14 @@ nd6_free(struct llentry *ln, int gc)
LLE_WUNLOCK(ln);
IF_AFDATA_LOCK(ifp);
LLE_WLOCK(ln);
- LLE_REMREF(ln);
- llentry_free(ln);
+
+ /* Guard against race with other llentry_free(). */
+ if (ln->la_flags & LLE_LINKED) {
+ LLE_REMREF(ln);
+ llentry_free(ln);
+ } else
+ LLE_FREE_LOCKED(ln);
+
IF_AFDATA_UNLOCK(ifp);
return (next);
OpenPOWER on IntegriCloud