DragonFlyBSD Kernel Audit
DF-0590 / fix.diff
← back to finding ↓ download raw
diff --git a/sys/netgraph/bridge/ng_bridge.c b/sys/netgraph/bridge/ng_bridge.c
--- a/sys/netgraph/bridge/ng_bridge.c
+++ b/sys/netgraph/bridge/ng_bridge.c
@@ -99,6 +99,7 @@
 	u_int			hashMask;	/* numBuckets - 1 */
 	int			numLinks;	/* num connected links */
 	struct callout		timer;		/* one second periodic timer */
+	struct lock		lock;		/* SMP serialization: protects tab, numHosts, numBuckets, hashMask */
 };
 typedef struct ng_bridge_private *priv_p;
 
@@ -305,6 +306,7 @@
 	if (priv == NULL)
 		return (ENOMEM);
 	callout_init(&priv->timer);
+	lockinit(&priv->lock, "ng_bridge", 0, LK_CANRECURSE);
 
 	/* Allocate and initialize hash table, etc. */
 	priv->tab = kmalloc(MIN_BUCKETS * sizeof(*priv->tab), M_NETGRAPH,
@@ -784,10 +786,14 @@
 	const int bucket = HASH(addr, priv->hashMask);
 	struct ng_bridge_hent *hent;
 
+	lockmgr(&priv->lock, LK_SHARED);
 	SLIST_FOREACH(hent, &priv->tab[bucket], next) {
-		if (ETHER_EQUAL(hent->host.addr, addr))
+		if (ETHER_EQUAL(hent->host.addr, addr)) {
+			lockmgr(&priv->lock, LK_RELEASE);
 			return (&hent->host);
+		}
 	}
+	lockmgr(&priv->lock, LK_RELEASE);
 	return (NULL);
 }
 
@@ -802,6 +808,7 @@
 	const int bucket = HASH(addr, priv->hashMask);
 	struct ng_bridge_hent *hent;
 
+	lockmgr(&priv->lock, LK_EXCLUSIVE);
 #ifdef INVARIANTS
 	char ethstr[ETHER_ADDRSTRLEN + 1];
 
@@ -814,8 +821,10 @@
 
 	/* Allocate and initialize new hashtable entry */
 	hent = kmalloc(sizeof(*hent), M_NETGRAPH, M_NOWAIT);
-	if (hent == NULL)
+	if (hent == NULL) {
+		lockmgr(&priv->lock, LK_RELEASE);
 		return (0);
+	}
 	bcopy(addr, hent->host.addr, ETHER_ADDR_LEN);
 	hent->host.linkNum = linkNum;
 	hent->host.staleness = 0;
@@ -827,6 +836,7 @@
 
 	/* Resize table if necessary */
 	ng_bridge_rehash(priv);
+	lockmgr(&priv->lock, LK_RELEASE);
 	return (1);
 }
 
@@ -949,6 +959,10 @@
 	/* Register a new timeout, keeping the existing node reference */
 	callout_reset(&priv->timer, hz, ng_bridge_timeout, node);
 
+	/* Hold the per-node lock across the sweep + rehash so concurrent
+	 * rcvdata -> ng_bridge_put/get cannot mutate the table mid-sweep. */
+	lockmgr(&priv->lock, LK_EXCLUSIVE);
+
 	/* Update host time counters and remove stale entries */
 	for (bucket = 0; bucket < priv->numBuckets; bucket++) {
 		struct ng_bridge_hent **hptr = &SLIST_FIRST(&priv->tab[bucket]);
@@ -981,6 +995,8 @@
 	/* Decrease table size if necessary */
 	ng_bridge_rehash(priv);
 
+	lockmgr(&priv->lock, LK_RELEASE);
+
 	/* Decrease loop counter on muted looped back links */
 	for (counter = linkNum = 0; linkNum < NG_BRIDGE_MAX_LINKS; linkNum++) {
 		struct ng_bridge_link *const link = priv->links[linkNum];