DragonFlyBSD Kernel Audit
DF-0881 / fix.diff
← back to finding ↓ download raw
diff --git a/sys/vfs/udf/udf_vfsops.c b/sys/vfs/udf/udf_vfsops.c
--- a/sys/vfs/udf/udf_vfsops.c
+++ b/sys/vfs/udf/udf_vfsops.c
@@ -630,7 +630,7 @@
 	struct buf *bp;
 	unsigned char regid_id[UDF_REGID_ID_SIZE + 1];
 	int ptype, psize, error;
-	unsigned int i;
+	unsigned int i, max_entries;
 
 	for (i = 0; i < lvd->n_pm; i++) {
 		pmap = (union udf_pmap *)&lvd->maps[i * UDF_PMAP_SIZE];
@@ -659,6 +659,13 @@
 		}
 
 		pms = &pmap->pms;
+
+		/* Validate st_size: must hold at least the header + 1 entry. */
+		if (pms->st_size < sizeof(struct udf_sparing_table)) {
+			kprintf("udf: sparing table size %u too small\n",
+			    pms->st_size);
+			return(EINVAL);
+		}
 		udfmp->s_table = kmalloc(pms->st_size, M_UDFMOUNT,
 					M_WAITOK | M_ZERO);
 
@@ -687,9 +694,15 @@
 		}
 
 		/* See how many valid entries there are here.  The list is
-		 * supposed to be sorted. 0xfffffff0 and higher are not valid
+		 * supposed to be sorted. 0xfffffff0 and higher are not valid.
+		 * Bound the scan by the number of entries that actually fit
+		 * in the st_size-byte allocation; the on-disk rt_l may be
+		 * forged larger and would drive a heap OOB read otherwise.
 		 */
-		for (i = 0; i < udfmp->s_table->rt_l; i++) {
+		max_entries = (pms->st_size -
+		    offsetof(struct udf_sparing_table, entries)) /
+		    sizeof(struct spare_map_entry);
+		for (i = 0; i < udfmp->s_table->rt_l && i < max_entries; i++) {
 			udfmp->s_table_entries = i;
 			if (udfmp->s_table->entries[i].org >= 0xfffffff0)
 				break;