Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 69d75671 authored by Jan Kara's avatar Jan Kara
Browse files

udf: Fortify LVID loading



A user has reported an oops in udf_statfs() that was caused by
numOfPartitions entry in LVID structure being corrupted. Fix the problem
by verifying whether numOfPartitions makes sense at least to the extent
that LVID fits into a single block as it should.

Reported-by: default avatarJuergen Weigert <jw@suse.com>
Signed-off-by: default avatarJan Kara <jack@suse.cz>
parent d8524ae9
Loading
Loading
Loading
Loading
+7 −9
Original line number Original line Diff line number Diff line
@@ -30,18 +30,17 @@ void udf_free_inode(struct inode *inode)
{
{
	struct super_block *sb = inode->i_sb;
	struct super_block *sb = inode->i_sb;
	struct udf_sb_info *sbi = UDF_SB(sb);
	struct udf_sb_info *sbi = UDF_SB(sb);
	struct logicalVolIntegrityDescImpUse *lvidiu = udf_sb_lvidiu(sb);


	if (lvidiu) {
		mutex_lock(&sbi->s_alloc_mutex);
		mutex_lock(&sbi->s_alloc_mutex);
	if (sbi->s_lvid_bh) {
		struct logicalVolIntegrityDescImpUse *lvidiu =
							udf_sb_lvidiu(sbi);
		if (S_ISDIR(inode->i_mode))
		if (S_ISDIR(inode->i_mode))
			le32_add_cpu(&lvidiu->numDirs, -1);
			le32_add_cpu(&lvidiu->numDirs, -1);
		else
		else
			le32_add_cpu(&lvidiu->numFiles, -1);
			le32_add_cpu(&lvidiu->numFiles, -1);
		udf_updated_lvid(sb);
		udf_updated_lvid(sb);
	}
		mutex_unlock(&sbi->s_alloc_mutex);
		mutex_unlock(&sbi->s_alloc_mutex);
	}


	udf_free_blocks(sb, NULL, &UDF_I(inode)->i_location, 0, 1);
	udf_free_blocks(sb, NULL, &UDF_I(inode)->i_location, 0, 1);
}
}
@@ -55,6 +54,7 @@ struct inode *udf_new_inode(struct inode *dir, umode_t mode, int *err)
	uint32_t start = UDF_I(dir)->i_location.logicalBlockNum;
	uint32_t start = UDF_I(dir)->i_location.logicalBlockNum;
	struct udf_inode_info *iinfo;
	struct udf_inode_info *iinfo;
	struct udf_inode_info *dinfo = UDF_I(dir);
	struct udf_inode_info *dinfo = UDF_I(dir);
	struct logicalVolIntegrityDescImpUse *lvidiu;


	inode = new_inode(sb);
	inode = new_inode(sb);


@@ -92,12 +92,10 @@ struct inode *udf_new_inode(struct inode *dir, umode_t mode, int *err)
		return NULL;
		return NULL;
	}
	}


	if (sbi->s_lvid_bh) {
	lvidiu = udf_sb_lvidiu(sb);
		struct logicalVolIntegrityDescImpUse *lvidiu;
	if (lvidiu) {

		iinfo->i_unique = lvid_get_unique_id(sb);
		iinfo->i_unique = lvid_get_unique_id(sb);
		mutex_lock(&sbi->s_alloc_mutex);
		mutex_lock(&sbi->s_alloc_mutex);
		lvidiu = udf_sb_lvidiu(sbi);
		if (S_ISDIR(mode))
		if (S_ISDIR(mode))
			le32_add_cpu(&lvidiu->numDirs, 1);
			le32_add_cpu(&lvidiu->numDirs, 1);
		else
		else
+40 −24
Original line number Original line Diff line number Diff line
@@ -94,13 +94,25 @@ static unsigned int udf_count_free(struct super_block *);
static int udf_statfs(struct dentry *, struct kstatfs *);
static int udf_statfs(struct dentry *, struct kstatfs *);
static int udf_show_options(struct seq_file *, struct dentry *);
static int udf_show_options(struct seq_file *, struct dentry *);


struct logicalVolIntegrityDescImpUse *udf_sb_lvidiu(struct udf_sb_info *sbi)
struct logicalVolIntegrityDescImpUse *udf_sb_lvidiu(struct super_block *sb)
{
{
	struct logicalVolIntegrityDesc *lvid =
	struct logicalVolIntegrityDesc *lvid;
		(struct logicalVolIntegrityDesc *)sbi->s_lvid_bh->b_data;
	unsigned int partnum;
	__u32 number_of_partitions = le32_to_cpu(lvid->numOfPartitions);
	unsigned int offset;
	__u32 offset = number_of_partitions * 2 *

				sizeof(uint32_t)/sizeof(uint8_t);
	if (!UDF_SB(sb)->s_lvid_bh)
		return NULL;
	lvid = (struct logicalVolIntegrityDesc *)UDF_SB(sb)->s_lvid_bh->b_data;
	partnum = le32_to_cpu(lvid->numOfPartitions);
	if ((sb->s_blocksize - sizeof(struct logicalVolIntegrityDescImpUse) -
	     offsetof(struct logicalVolIntegrityDesc, impUse)) /
	     (2 * sizeof(uint32_t)) < partnum) {
		udf_err(sb, "Logical volume integrity descriptor corrupted "
			"(numOfPartitions = %u)!\n", partnum);
		return NULL;
	}
	/* The offset is to skip freeSpaceTable and sizeTable arrays */
	offset = partnum * 2 * sizeof(uint32_t);
	return (struct logicalVolIntegrityDescImpUse *)&(lvid->impUse[offset]);
	return (struct logicalVolIntegrityDescImpUse *)&(lvid->impUse[offset]);
}
}


@@ -629,9 +641,10 @@ static int udf_remount_fs(struct super_block *sb, int *flags, char *options)
	struct udf_options uopt;
	struct udf_options uopt;
	struct udf_sb_info *sbi = UDF_SB(sb);
	struct udf_sb_info *sbi = UDF_SB(sb);
	int error = 0;
	int error = 0;
	struct logicalVolIntegrityDescImpUse *lvidiu = udf_sb_lvidiu(sb);


	if (sbi->s_lvid_bh) {
	if (lvidiu) {
		int write_rev = le16_to_cpu(udf_sb_lvidiu(sbi)->minUDFWriteRev);
		int write_rev = le16_to_cpu(lvidiu->minUDFWriteRev);
		if (write_rev > UDF_MAX_WRITE_VERSION && !(*flags & MS_RDONLY))
		if (write_rev > UDF_MAX_WRITE_VERSION && !(*flags & MS_RDONLY))
			return -EACCES;
			return -EACCES;
	}
	}
@@ -1905,11 +1918,12 @@ static void udf_open_lvid(struct super_block *sb)


	if (!bh)
	if (!bh)
		return;
		return;

	mutex_lock(&sbi->s_alloc_mutex);
	lvid = (struct logicalVolIntegrityDesc *)bh->b_data;
	lvid = (struct logicalVolIntegrityDesc *)bh->b_data;
	lvidiu = udf_sb_lvidiu(sbi);
	lvidiu = udf_sb_lvidiu(sb);
	if (!lvidiu)
		return;


	mutex_lock(&sbi->s_alloc_mutex);
	lvidiu->impIdent.identSuffix[0] = UDF_OS_CLASS_UNIX;
	lvidiu->impIdent.identSuffix[0] = UDF_OS_CLASS_UNIX;
	lvidiu->impIdent.identSuffix[1] = UDF_OS_ID_LINUX;
	lvidiu->impIdent.identSuffix[1] = UDF_OS_ID_LINUX;
	udf_time_to_disk_stamp(&lvid->recordingDateAndTime,
	udf_time_to_disk_stamp(&lvid->recordingDateAndTime,
@@ -1937,10 +1951,12 @@ static void udf_close_lvid(struct super_block *sb)


	if (!bh)
	if (!bh)
		return;
		return;
	lvid = (struct logicalVolIntegrityDesc *)bh->b_data;
	lvidiu = udf_sb_lvidiu(sb);
	if (!lvidiu)
		return;


	mutex_lock(&sbi->s_alloc_mutex);
	mutex_lock(&sbi->s_alloc_mutex);
	lvid = (struct logicalVolIntegrityDesc *)bh->b_data;
	lvidiu = udf_sb_lvidiu(sbi);
	lvidiu->impIdent.identSuffix[0] = UDF_OS_CLASS_UNIX;
	lvidiu->impIdent.identSuffix[0] = UDF_OS_CLASS_UNIX;
	lvidiu->impIdent.identSuffix[1] = UDF_OS_ID_LINUX;
	lvidiu->impIdent.identSuffix[1] = UDF_OS_ID_LINUX;
	udf_time_to_disk_stamp(&lvid->recordingDateAndTime, CURRENT_TIME);
	udf_time_to_disk_stamp(&lvid->recordingDateAndTime, CURRENT_TIME);
@@ -2093,15 +2109,19 @@ static int udf_fill_super(struct super_block *sb, void *options, int silent)


	if (sbi->s_lvid_bh) {
	if (sbi->s_lvid_bh) {
		struct logicalVolIntegrityDescImpUse *lvidiu =
		struct logicalVolIntegrityDescImpUse *lvidiu =
							udf_sb_lvidiu(sbi);
							udf_sb_lvidiu(sb);
		uint16_t minUDFReadRev = le16_to_cpu(lvidiu->minUDFReadRev);
		uint16_t minUDFReadRev;
		uint16_t minUDFWriteRev = le16_to_cpu(lvidiu->minUDFWriteRev);
		uint16_t minUDFWriteRev;
		/* uint16_t maxUDFWriteRev =
				le16_to_cpu(lvidiu->maxUDFWriteRev); */


		if (!lvidiu) {
			ret = -EINVAL;
			goto error_out;
		}
		minUDFReadRev = le16_to_cpu(lvidiu->minUDFReadRev);
		minUDFWriteRev = le16_to_cpu(lvidiu->minUDFWriteRev);
		if (minUDFReadRev > UDF_MAX_READ_VERSION) {
		if (minUDFReadRev > UDF_MAX_READ_VERSION) {
			udf_err(sb, "minUDFReadRev=%x (max is %x)\n",
			udf_err(sb, "minUDFReadRev=%x (max is %x)\n",
				le16_to_cpu(lvidiu->minUDFReadRev),
				minUDFReadRev,
				UDF_MAX_READ_VERSION);
				UDF_MAX_READ_VERSION);
			ret = -EINVAL;
			ret = -EINVAL;
			goto error_out;
			goto error_out;
@@ -2265,11 +2285,7 @@ static int udf_statfs(struct dentry *dentry, struct kstatfs *buf)
	struct logicalVolIntegrityDescImpUse *lvidiu;
	struct logicalVolIntegrityDescImpUse *lvidiu;
	u64 id = huge_encode_dev(sb->s_bdev->bd_dev);
	u64 id = huge_encode_dev(sb->s_bdev->bd_dev);


	if (sbi->s_lvid_bh != NULL)
	lvidiu = udf_sb_lvidiu(sb);
		lvidiu = udf_sb_lvidiu(sbi);
	else
		lvidiu = NULL;

	buf->f_type = UDF_SUPER_MAGIC;
	buf->f_type = UDF_SUPER_MAGIC;
	buf->f_bsize = sb->s_blocksize;
	buf->f_bsize = sb->s_blocksize;
	buf->f_blocks = sbi->s_partmaps[sbi->s_partition].s_partition_len;
	buf->f_blocks = sbi->s_partmaps[sbi->s_partition].s_partition_len;
+1 −1
Original line number Original line Diff line number Diff line
@@ -162,7 +162,7 @@ static inline struct udf_sb_info *UDF_SB(struct super_block *sb)
	return sb->s_fs_info;
	return sb->s_fs_info;
}
}


struct logicalVolIntegrityDescImpUse *udf_sb_lvidiu(struct udf_sb_info *sbi);
struct logicalVolIntegrityDescImpUse *udf_sb_lvidiu(struct super_block *sb);


int udf_compute_nr_groups(struct super_block *sb, u32 partition);
int udf_compute_nr_groups(struct super_block *sb, u32 partition);