DragonFlyBSD Kernel Audit
DF-2062 / fix.diff
← back to finding ↓ download raw
diff --git a/sys/dev/disk/dm/dm_table.c b/sys/dev/disk/dm/dm_table.c
--- a/sys/dev/disk/dm/dm_table.c
+++ b/sys/dev/disk/dm/dm_table.c
@@ -56,13 +56,13 @@
  * DM_TABLE_INACTIVE will return inactive table id.
  */
 static int
-dm_table_busy(dm_table_head_t *head, uint8_t table_id)
+dm_table_busy(dm_table_head_t *head, uint8_t table_id, u_int mode)
 {
 	uint8_t id;
 
 	id = 0;
 
-	lockmgr(&head->table_mtx, LK_SHARED);
+	lockmgr(&head->table_mtx, mode);
 
 	if (table_id == DM_TABLE_ACTIVE)
 		id = head->cur_active_table;
@@ -95,7 +95,24 @@
 {
 	uint8_t id;
 
-	id = dm_table_busy(head, table_id);
+	id = dm_table_busy(head, table_id, LK_SHARED);
+
+	return &head->tables[id];
+}
+
+/*
+ * Exclusive variant for callers that MUTATE the table list
+ * (e.g. dm_table_load_ioctl inserts entries into the inactive TAILQ).
+ * Taking LK_EXCLUSIVE serializes list mutation and prevents the
+ * non-atomic TAILQ_INSERT_TAIL pointer dance from racing against a
+ * concurrent loader/walker.
+ */
+dm_table_t *
+dm_table_get_entry_lock(dm_table_head_t *head, uint8_t table_id)
+{
+	uint8_t id;
+
+	id = dm_table_busy(head, table_id, LK_EXCLUSIVE);
 
 	return &head->tables[id];
 }
diff --git a/sys/dev/disk/dm/dm.h b/sys/dev/disk/dm/dm.h
--- a/sys/dev/disk/dm/dm.h
+++ b/sys/dev/disk/dm/dm.h
@@ -217,6 +217,7 @@
 
 /* dm_table.c  */
 dm_table_t *dm_table_get_entry(dm_table_head_t *, uint8_t);
+dm_table_t *dm_table_get_entry_lock(dm_table_head_t *, uint8_t);
 void dm_table_release(dm_table_head_t *, uint8_t s);
 void dm_table_switch_tables(dm_table_head_t *);
 int dm_table_destroy(dm_table_head_t *, uint8_t);
diff --git a/sys/dev/disk/dm/dm_ioctl.c b/sys/dev/disk/dm/dm_ioctl.c
--- a/sys/dev/disk/dm/dm_ioctl.c
+++ b/sys/dev/disk/dm/dm_ioctl.c
@@ -723,7 +723,7 @@
 		dm_table_destroy(&dmv->table_head, DM_TABLE_INACTIVE);
 
 	dm_dbg_print_flags(dmv->flags);
-	tbl = dm_table_get_entry(&dmv->table_head, DM_TABLE_INACTIVE);
+	tbl = dm_table_get_entry_lock(&dmv->table_head, DM_TABLE_INACTIVE);
 
 	prop_dictionary_set_uint32(dm_dict, DM_IOCTL_MINOR, dmv->minor);