Commit c8f1140c authored by Jan Kara's avatar Jan Kara
Browse files

udf: Avoid invalid LVID used on mount



udf_load_logicalvolint() loads logical volume integrity descriptors.
Since there can be multiple blocks with LVIDs, we verify the contents of
only the last (prevailing) LVID found. However if we fail to load the
last LVID (either due to IO error or because it's checksum fails to
match), we never perform the verification of validity of the LVID we are
going to use. If such LVID contains invalid data, we can hit
out-of-bounds access or similar issues. Fix the problem by verifying
each LVID we are potentially going to accept.

Reported-by: default avatarRobert Morris <rtm@csail.mit.edu>
Signed-off-by: default avatarJan Kara <jack@suse.cz>
parent d0aa7260
Loading
Loading
Loading
Loading
+25 −17
Original line number Diff line number Diff line
@@ -1539,6 +1539,20 @@ static int udf_load_logicalvol(struct super_block *sb, sector_t block,
	return ret;
}

static bool udf_lvid_valid(struct super_block *sb,
			   struct logicalVolIntegrityDesc *lvid)
{
	u32 parts, impuselen;

	parts = le32_to_cpu(lvid->numOfPartitions);
	impuselen = le32_to_cpu(lvid->lengthOfImpUse);
	if (parts >= sb->s_blocksize || impuselen >= sb->s_blocksize ||
	    sizeof(struct logicalVolIntegrityDesc) + impuselen +
	    2 * parts * sizeof(u32) > sb->s_blocksize)
		return false;
	return true;
}

/*
 * Find the prevailing Logical Volume Integrity Descriptor.
 */
@@ -1549,7 +1563,6 @@ static void udf_load_logicalvolint(struct super_block *sb, struct kernel_extent_
	struct udf_sb_info *sbi = UDF_SB(sb);
	struct logicalVolIntegrityDesc *lvid;
	int indirections = 0;
	u32 parts, impuselen;

	while (++indirections <= UDF_MAX_LVID_NESTING) {
		final_bh = NULL;
@@ -1571,32 +1584,27 @@ static void udf_load_logicalvolint(struct super_block *sb, struct kernel_extent_
		if (!final_bh)
			return;

		lvid = (struct logicalVolIntegrityDesc *)final_bh->b_data;
		if (udf_lvid_valid(sb, lvid)) {
			brelse(sbi->s_lvid_bh);
			sbi->s_lvid_bh = final_bh;
		} else {
			udf_warn(sb, "Corrupted LVID (parts=%u, impuselen=%u), "
				 "ignoring.\n",
				 le32_to_cpu(lvid->numOfPartitions),
				 le32_to_cpu(lvid->lengthOfImpUse));
		}

		lvid = (struct logicalVolIntegrityDesc *)final_bh->b_data;
		if (lvid->nextIntegrityExt.extLength == 0)
			goto check;
			return;

		loc = leea_to_cpu(lvid->nextIntegrityExt);
	}

	udf_warn(sb, "Too many LVID indirections (max %u), ignoring.\n",
		UDF_MAX_LVID_NESTING);
out_err:
	brelse(sbi->s_lvid_bh);
	sbi->s_lvid_bh = NULL;
	return;
check:
	parts = le32_to_cpu(lvid->numOfPartitions);
	impuselen = le32_to_cpu(lvid->lengthOfImpUse);
	if (parts >= sb->s_blocksize || impuselen >= sb->s_blocksize ||
	    sizeof(struct logicalVolIntegrityDesc) + impuselen +
	    2 * parts * sizeof(u32) > sb->s_blocksize) {
		udf_warn(sb, "Corrupted LVID (parts=%u, impuselen=%u), "
			 "ignoring.\n", parts, impuselen);
		goto out_err;
	}
}

/*