⬢ DragonFlyBSD Kernel Audit
DF-3103 / fix.diff
← back to finding ↓ download raw
# DF-3103 fix: validate l_ea/l_ad at vget and honor offset/length in
# the embedded-ICB read path.
--- a/sys/vfs/udf/udf_vnops.c
@@ -403,7 +404,7 @@
 			error = uiomove(data, size, uio);
 		if (bp != NULL)
 			brelse(bp);
-		if (error)
+		if (error || size == 0)
 			break;
 	}
 
@@ -1037,6 +1038,12 @@
 		fentry = node->fentry;
 		*data = &fentry->data[fentry->l_ea];
 		*size = fentry->l_ad;
+		if (offset >= *size)
+			*size = 0;
+		else {
+			*data += offset;
+			*size -= offset;
+		}
 		return(0);
 	} else if (error != 0) {
 		return(error);
--- a/sys/vfs/udf/udf_vfsops.c
@@ -524,6 +524,22 @@
 		brelse(bp);
 		return(ENOMEM);
 	}
+	/*
+	 * A file entry is exactly one logical block; the EA area and the
+	 * allocation-descriptor area must both fit in it.  Otherwise the
+	 * 32-bit UDF_FENTRY_SIZE + l_ea + l_ad sum can wrap and undersize
+	 * the fentry allocation while udf_readatoffset()'s embedded path
+	 * still trusts the raw l_ea/l_ad.
+	 */
+	if (udfmp->bsize < UDF_FENTRY_SIZE ||
+	    fe->l_ea > (uint32_t)(udfmp->bsize - UDF_FENTRY_SIZE) ||
+	    fe->l_ad > (uint32_t)(udfmp->bsize - UDF_FENTRY_SIZE) - fe->l_ea) {
+		kprintf("udf_vget: invalid l_ea %u / l_ad %u\n",
+		    fe->l_ea, fe->l_ad);
+		kfree(unode, M_UDFNODE);
+		brelse(bp);
+		return(EINVAL);
+	}
 	size = UDF_FENTRY_SIZE + fe->l_ea + fe->l_ad;
 	unode->fentry = kmalloc(size, M_UDFFENTRY, M_WAITOK | M_ZERO);