⬢ DragonFlyBSD Kernel Audit
DF-4563 / fix.diff
← back to finding ↓ download raw
--- a/sys/dev/disk/vn/vn.c
+++ b/sys/dev/disk/vn/vn.c
@@ -71,6 +71,7 @@
 #include <sys/stat.h>
 #include <sys/module.h>
 #include <sys/vnioctl.h>
+#include <sys/callout.h>
 
 #include <vm/vm.h>
 #include <vm/vm_object.h>
@@ -132,6 +133,7 @@
 	u_long		 sc_options;	/* options 			*/
 	cdev_t		 sc_dev;	/* devices that refer to this unit */
 	SLIST_ENTRY(vn_softc) sc_list;
+	struct callout	 sc_callout;	/* deferred destroy (DF-4563) */
 };
 
 static SLIST_HEAD(, vn_softc) vn_list;
@@ -166,6 +168,25 @@
 	return 0;
 }
 
+/*
+ * DF-4563: the VNF_DESTROY teardown must not run from inside the rawdev
+ * close path, where it races the concurrent last-but-one close()'s cdev
+ * sysref releases (double-put -> sysref_put panic) and frees the struct
+ * disk that the generic diskclose() of the losing close still
+ * dereferences.  Run the teardown deferred instead.
+ */
+static void
+vn_destroy_deferred(void *arg)
+{
+	struct vn_softc *vn = arg;
+
+	KKASSERT(disk_getopencount(&vn->sc_disk) == 0);
+	disk_destroy(&vn->sc_disk);
+	devfs_clone_bitmap_put(&DEVFS_CLONE_BITMAP(vn), vn->sc_unit);
+	SLIST_REMOVE(&vn_list, vn, vn_softc, sc_list);
+	kfree(vn, M_VN);
+}
+
 static	int
 vnclose(struct dev_close_args *ap)
 {
@@ -177,13 +198,10 @@
 
 	vn->sc_flags &= ~VNF_OPENED;
 
-	/* The disk has been detached and can now be safely destroyed */
+	/* The disk has been detached: destroy it deferred (DF-4563) */
 	if (vn->sc_flags & VNF_DESTROY) {
 		KKASSERT(disk_getopencount(&vn->sc_disk) == 0);
-		disk_destroy(&vn->sc_disk);
-		devfs_clone_bitmap_put(&DEVFS_CLONE_BITMAP(vn), dkunit(dev));
-		SLIST_REMOVE(&vn_list, vn, vn_softc, sc_list);
-		kfree(vn, M_VN);
+		callout_reset(&vn->sc_callout, hz, vn_destroy_deferred, vn);
 	}
 	return (0);
 }
@@ -194,6 +212,7 @@
 	struct vn_softc *vn;
 
 	vn = kmalloc(sizeof *vn, M_VN, M_WAITOK | M_ZERO);
+	callout_init_mp(&vn->sc_callout);
 	return vn;
 }
 
@@ -909,6 +928,9 @@
 			if (vn->sc_flags & VNF_INITED)
 				vnclear(vn);
 
+			/* DF-4563: cancel any pending deferred destroy */
+			callout_drain(&vn->sc_callout);
+
 			kfree(vn, M_VN);
 		}
 		destroy_autoclone_dev(dev, &DEVFS_CLONE_BITMAP(vn));