DragonFlyBSD Kernel Audit
DF-2449 / fix.diff
← back to finding ↓ download raw
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
@@ -741,16 +741,31 @@
 			return ENOENT;
 		}
 		if ((table_en = kmalloc(sizeof(dm_table_entry_t),
-			    M_DM, M_WAITOK)) == NULL) {
+			    M_DM, M_WAITOK | M_ZERO)) == NULL) {
 			dm_table_release(&dmv->table_head, DM_TABLE_INACTIVE);
 			dm_dev_unbusy(dmv);
 			dm_target_unbusy(target);
 			return ENOMEM;
 		}
-		prop_dictionary_get_uint64(target_dict, DM_TABLE_START,
-		    &table_en->start);
-		prop_dictionary_get_uint64(target_dict, DM_TABLE_LENGTH,
-		    &table_en->length);
+		/*
+		 * Require start/length to be present in the per-table-entry dict.
+		 * prop_dictionary_get_uint64() returns false without writing *valp
+		 * when the key is absent, which would otherwise leave the field at
+		 * whatever stale slab bytes kmalloc returned (the M_DM kmalloc above
+		 * is not M_ZERO before this change).  M_ZERO on the kmalloc also
+		 * guarantees the whole struct starts zeroed for any future field
+		 * whose initialization is missed here.  DF-2449.
+		 */
+		if (!prop_dictionary_get_uint64(target_dict, DM_TABLE_START,
+			    &table_en->start) ||
+		    !prop_dictionary_get_uint64(target_dict, DM_TABLE_LENGTH,
+			    &table_en->length)) {
+			kfree(table_en, M_DM);
+			dm_table_release(&dmv->table_head, DM_TABLE_INACTIVE);
+			dm_dev_unbusy(dmv);
+			dm_target_unbusy(target);
+			return EINVAL;
+		}
 
 		dmdebug("table_en->start = %ju, table_en->length = %ju\n",
 			(uintmax_t)table_en->start,