DragonFlyBSD Kernel Audit
DF-2572 / fix.diff
← back to finding ↓ download raw
diff --git a/sys/netgraph/ng_device.c b/sys/netgraph/ng_device.c
--- a/sys/netgraph/ng_device.c
+++ b/sys/netgraph/ng_device.c
@@ -41,6 +41,7 @@
 #include <sys/malloc.h>
 #include <sys/conf.h>
 #include <sys/poll.h>
+#include <sys/spinlock2.h>
 
 #include <netgraph/ng_message.h>
 #include <netgraph/netgraph.h>
@@ -94,6 +95,7 @@
 /* global data */
 struct ngd_softc {
 	SLIST_HEAD(, ngd_connection) head;
+	struct spinlock ngd_lock;	/* protects the connection list */
 
 	node_p node;
 	char nodename[NG_NODESIZ];
@@ -180,6 +182,7 @@
 #endif /* NGD_DEBUG */ 
 
 	SLIST_INIT(&sc->head);
+	spin_lock_init(&sc->ngd_lock);
 
         if (ng_make_node_common(&typestruct, &sc->node) != 0) {
                 kprintf("%s(): ng_make_node_common failed\n", __func__);
@@ -233,8 +236,10 @@
 	kprintf("%s()\n", __func__);
 #endif /* NGD_DEBUG */
 
+	spin_lock(&sc->ngd_lock);
 	/* When there is no list yet, the first device unit is always 0. */
 	if (SLIST_EMPTY(&sc->head)) {
+		spin_unlock(&sc->ngd_lock);
 		unit = 0;
 		return(unit);
 	}
@@ -253,6 +258,7 @@
 			unit = n;
 		}
 	}
+	spin_unlock(&sc->ngd_lock);
 
 	return(unit);
 }
@@ -306,7 +312,9 @@
 	new_connection->loc = 0;
 	new_connection->active_hook = hook;
 
+	spin_lock(&sc->ngd_lock);
 	SLIST_INSERT_HEAD(&sc->head, new_connection, links);
+	spin_unlock(&sc->ngd_lock);
 
 	return(0);
 }
@@ -342,11 +350,13 @@
 	kprintf("%s()\n", __func__);
 #endif /* NGD_DEBUG */
 
+	spin_lock(&sc->ngd_lock);
 	SLIST_FOREACH(tmp, &sc->head, links) {
 		if(tmp->active_hook == hook) {
 			connection = tmp;
 		}
 	}
+	spin_unlock(&sc->ngd_lock);
 	if(connection == NULL) {
 		kprintf("%s(): connection still NULL, no hook found\n", __func__);
 		return(-1);
@@ -393,22 +403,26 @@
 	kprintf("%s()\n", __func__);
 #endif /* NGD_DEBUG */
 
+	spin_lock(&sc->ngd_lock);
 	SLIST_FOREACH(tmp, &sc->head, links) {
 		if(tmp->active_hook == hook) {
 			connection = tmp;
 		}
 	}
 	if(connection == NULL) {
+		spin_unlock(&sc->ngd_lock);
 		kprintf("%s(): connection still NULL, no hook found\n",
 		    __func__);
 		return(-1);
 	}
+	SLIST_REMOVE(&sc->head, connection, ngd_connection, links);
+	spin_unlock(&sc->ngd_lock);
 
-        kfree(connection->readq, M_DEVBUF);
-
+	/* disconnect no longer touches the list; drain cdev readers BEFORE
+	 * freeing readq so in-flight ngdread/ngdwrite observe a live object. */
 	destroy_dev(connection->ngddev);
-
-	SLIST_REMOVE(&sc->head, connection, ngd_connection, links);
+	kfree(connection->readq, M_DEVBUF);
+	kfree(connection, M_DEVBUF);
 
 	return(0);
 }
@@ -461,11 +475,13 @@
 	kprintf("%s()\n", __func__);
 #endif /* NGD_DEBUG */
 
+	spin_lock(&sc->ngd_lock);
 	SLIST_FOREACH(tmp, &sc->head, links) {
 		if(tmp->ngddev == dev) {
 			connection = tmp;
 		}
 	}
+	spin_unlock(&sc->ngd_lock);
 	if(connection == NULL) {
 		kprintf("%s(): connection still NULL, no dev found\n",
 		    __func__);
@@ -515,11 +531,13 @@
 	kprintf("%s()\n", __func__);
 #endif /* NGD_DEBUG */
 
+	spin_lock(&sc->ngd_lock);
 	SLIST_FOREACH(tmp, &sc->head, links) {
 		if(tmp->ngddev == dev) {
 			connection = tmp;
 		}
 	}
+	spin_unlock(&sc->ngd_lock);
 	if(connection == NULL) {
 		kprintf("%s(): connection still NULL, no dev found\n", __func__);
 		return(-1);
@@ -569,11 +587,13 @@
 	kprintf("%s()\n", __func__);
 #endif /* NGD_DEBUG */
 
+	spin_lock(&sc->ngd_lock);
 	SLIST_FOREACH(tmp, &sc->head, links) {
 		if(tmp->ngddev == dev) {
 			connection = tmp;
 		}
 	}
+	spin_unlock(&sc->ngd_lock);
 
 	if(connection == NULL) {
 		kprintf("%s(): connection still NULL, no dev found\n", __func__);
@@ -614,11 +634,13 @@
 
 	if (events & (POLLIN | POLLRDNORM)) {
 		/* get the connection we have to know the loc from */
+		spin_lock(&sc->ngd_lock);
 		SLIST_FOREACH(tmp, &sc->head, links) {
 			if(tmp->ngddev == dev) {
 				connection = tmp;
 			}
 		}
+		spin_unlock(&sc->ngd_lock);
 		if(connection == NULL) {
 			kprintf("%s(): ERROR: connection still NULL, "
 				"no dev found\n", __func__);