⬢ DragonFlyBSD Kernel Audit
DF-5065 / fix.diff
← back to finding ↓ download raw
diff --git a/sys/dev/disk/dm/dm_target.c b/sys/dev/disk/dm/dm_target.c
--- a/sys/dev/disk/dm/dm_target.c
+++ b/sys/dev/disk/dm/dm_target.c
@@ -277,15 +277,21 @@
 int
 dm_target_uninit(void)
 {
-	dm_target_t *dm_target;
-
 	lockmgr(&dm_target_mutex, LK_EXCLUSIVE);
 
-	while ((dm_target = TAILQ_FIRST(&dm_target_list)) != NULL) {
-		TAILQ_REMOVE(&dm_target_list, dm_target, dm_target_next);
-		dm_target_free(dm_target);
+	/*
+	 * A non-empty registry means a target module is still loaded and
+	 * registered -- it calls dm_target_remove() from its own MOD_UNLOAD
+	 * and would run through freed dm module text and a destroyed
+	 * registry lock -- or a table still holds a reference (ref_cnt > 0),
+	 * and freeing the entry leaves a dangling struct dm_target pointer
+	 * in live dm_table_entry_t's.  Refuse; the caller must veto the
+	 * module unload.
+	 */
+	if (!TAILQ_EMPTY(&dm_target_list)) {
+		lockmgr(&dm_target_mutex, LK_RELEASE);
+		return EBUSY;
 	}
-	KKASSERT(TAILQ_EMPTY(&dm_target_list));
 
 	lockmgr(&dm_target_mutex, LK_RELEASE);
 
diff --git a/sys/dev/disk/dm/device-mapper.c b/sys/dev/disk/dm/device-mapper.c
--- a/sys/dev/disk/dm/device-mapper.c
+++ b/sys/dev/disk/dm/device-mapper.c
@@ -187,11 +187,28 @@
 static int
 dmdestroy(void)
 {
+	int error;
+
+	/*
+	 * Unregister the built-in targets explicitly: their SYSUNINITs
+	 * (DM_TARGET_BUILTIN, dm.h) only run after this MOD_UNLOAD handler.
+	 */
+	dm_target_remove("error");
+	dm_target_remove("zero");
+
+	/*
+	 * Refuse to unload while any target module is still registered or
+	 * any table still holds a target reference: dm_target_uninit()
+	 * must not free the registry (or destroy its lock) underneath a
+	 * still-loaded target module's dm_target_remove() call.
+	 */
+	if ((error = dm_target_uninit()) != 0)
+		return error;
+
 	destroy_dev(dmcdev);
 
 	dm_dev_uninit();
 	dm_pdev_uninit();
-	dm_target_uninit();
 
 	return 0;
 }