⬢ DragonFlyBSD Kernel Audit
DF-4926 / fix.diff
← back to finding ↓ download raw
--- a/sys/dev/virtual/nvmm/nvmm_dragonfly.c
+++ b/sys/dev/virtual/nvmm/nvmm_dragonfly.c
@@ -281,6 +281,14 @@
 static d_ioctl_t dfbsd_nvmm_ioctl;
 static d_priv_dtor_t dfbsd_nvmm_dtor;
 
+/*
+ * The cdev and an open-file counter.  MOD_UNLOAD must not tear the module
+ * down while descriptors are open: their cdevpriv dtor (dfbsd_nvmm_dtor)
+ * and every in-flight ioctl are module text/state.
+ */
+static cdev_t nvmm_dev = NULL;
+static volatile unsigned int nvmm_opens;
+
 static struct dev_ops nvmm_ops = {
 	{ "nvmm", 0, D_MPSAFE },
 	.d_open = dfbsd_nvmm_open,
@@ -319,6 +327,8 @@
 		return error;
 	}
 
+	os_atomic_inc_uint(&nvmm_opens);
+
 	return 0;
 }
 
@@ -332,6 +342,10 @@
 	if (owner != &nvmm_root_owner) {
 		os_mem_free(owner, sizeof(*owner));
 	}
+
+	/* Released only after nvmm_kill_machines(), so that a concurrent
+	 * nvmm_detach() observes nmachines==0 before opens==0. */
+	os_atomic_dec_uint(&nvmm_opens);
 }
 
 static int
@@ -366,9 +380,37 @@
 static int
 nvmm_detach(void)
 {
+	/*
+	 * Refuse the unload while any descriptor is open or any machine
+	 * exists: the dtor and the in-flight ioctls live in this module.
+	 */
+	if (os_atomic_load_uint(&nvmm_opens) > 0)
+		return EBUSY;
 	if (os_atomic_load_uint(&nmachines) > 0)
 		return EBUSY;
 
+	/*
+	 * Remove the device FIRST: destroy_dev() revectors the ops to
+	 * dead_dev_ops, drains in-flight calls and blocks new ones, so the
+	 * teardown below cannot race concurrent ioctls/opens.
+	 */
+	if (nvmm_dev == NULL)
+		return 0;
+	destroy_dev(nvmm_dev);
+	nvmm_dev = NULL;
+
+	/*
+	 * An open() may have been in flight across the drain above; with the
+	 * device gone the counters are now stable.  Restore the device and
+	 * refuse rather than tearing down underneath the descriptor.
+	 */
+	if (os_atomic_load_uint(&nvmm_opens) > 0 ||
+	    os_atomic_load_uint(&nmachines) > 0) {
+		nvmm_dev = make_dev(&nvmm_ops, 0, UID_ROOT, GID_NVMM, 0640,
+		    "nvmm");
+		return EBUSY;
+	}
+
 	nvmm_fini();
 	return 0;
 }
@@ -376,7 +418,6 @@
 static int
 nvmm_modevent(module_t mod __unused, int type, void *data __unused)
 {
-	static cdev_t dev = NULL;
 	int error;
 
 	switch (type) {
@@ -389,19 +430,18 @@
 		if (error)
 			return error;
 
-		dev = make_dev(&nvmm_ops, 0, UID_ROOT, GID_NVMM, 0640, "nvmm");
-		if (dev == NULL) {
+		nvmm_dev = make_dev(&nvmm_ops, 0, UID_ROOT, GID_NVMM, 0640,
+		    "nvmm");
+		if (nvmm_dev == NULL) {
 			os_printf("nvmm: unable to create device\n");
 			error = ENOMEM;
 		}
 		break;
 
 	case MOD_UNLOAD:
-		if (dev == NULL)
-			return 0;
+		/* destroy_dev() is done by nvmm_detach(), BEFORE the
+		 * implementation is torn down. */
 		error = nvmm_detach();
-		if (error == 0)
-			destroy_dev(dev);
 		break;
 
 	case MOD_SHUTDOWN: