DragonFlyBSD Kernel Audit
DF-0195 / fix.diff
← back to finding ↓ download raw
diff --git a/sys/kern/subr_devstat.c b/sys/kern/subr_devstat.c
--- a/sys/kern/subr_devstat.c
+++ b/sys/kern/subr_devstat.c
@@ -33,6 +33,7 @@
 #include <sys/kernel.h>
 #include <sys/systm.h>
 #include <sys/buf.h>
+#include <sys/lock.h>
 #include <sys/sysctl.h>
 
 #include <sys/devicestat.h>
@@ -45,6 +46,22 @@
 static STAILQ_HEAD(devstatlist, devstat) device_statq;
 
 /*
+ * Lock protecting the device_statq list and the devstat_num_devs /
+ * devstat_generation counters.  devstat_add_entry() and
+ * devstat_remove_entry() take it exclusively; sysctl_devstat() takes it
+ * shared so a concurrent device detach cannot free a node out from under
+ * the world-readable sysctl walk (DF-0195).
+ */
+static struct lock devstat_lock;
+
+static void
+devstat_lock_init(void *arg __unused)
+{
+	lockinit(&devstat_lock, "devstat", 0, 0);
+}
+SYSINIT(devstatlock, SI_SUB_CREATE_INIT, SI_ORDER_ANY, devstat_lock_init, NULL);
+
+/*
  * Take a malloced and zeroed devstat structure given to us, fill it in 
  * and add it to the queue of devices.  
  */
@@ -61,6 +78,8 @@
 	if (ds == NULL)
 		return;
 
+	lockmgr(&devstat_lock, LK_EXCLUSIVE);
+
 	if (devstat_num_devs == 0)
 		STAILQ_INIT(&device_statq);
 
@@ -131,6 +150,8 @@
 	ds->device_type = device_type;
 	ds->priority = priority;
 	getmicrotime(&ds->dev_creation_time);
+
+	lockmgr(&devstat_lock, LK_RELEASE);
 }
 
 /*
@@ -144,6 +165,8 @@
 	if (ds == NULL)
 		return;
 
+	lockmgr(&devstat_lock, LK_EXCLUSIVE);
+
 	devstat_generation++;
 	devstat_num_devs--;
 
@@ -151,6 +174,8 @@
 
 	/* Remove this entry from the devstat queue */
 	STAILQ_REMOVE(devstat_head, ds, devstat, dev_links);
+
+	lockmgr(&devstat_lock, LK_RELEASE);
 }
 
 /*
@@ -272,8 +297,12 @@
 	struct devstat *nds;
 	struct devstatlist *devstat_head;
 
-	if (devstat_num_devs == 0)
+	lockmgr(&devstat_lock, LK_SHARED);
+
+	if (devstat_num_devs == 0) {
+		lockmgr(&devstat_lock, LK_RELEASE);
 		return(EINVAL);
+	}
 
 	error = 0;
 	devstat_head = &device_statq;
@@ -291,6 +320,8 @@
 	     nds = STAILQ_NEXT(nds, dev_links), i++)
 		error = SYSCTL_OUT(req, nds, sizeof(struct devstat));
 
+	lockmgr(&devstat_lock, LK_RELEASE);
+
 	return(error);
 }