udf: Check LVID earlier
authorJan Kara <jack@suse.cz>
Mon, 3 May 2021 09:39:03 +0000 (11:39 +0200)
committerJan Kara <jack@suse.cz>
Wed, 11 Aug 2021 14:54:44 +0000 (16:54 +0200)
We were checking validity of LVID entries only when getting
implementation use information from LVID in udf_sb_lvidiu(). However if
the LVID is suitably corrupted, it can cause problems also to code such
as udf_count_free() which doesn't use udf_sb_lvidiu(). So check validity
of LVID already when loading it from the disk and just disable LVID
altogether when it is not valid.

Reported-by: syzbot+7fbfe5fed73ebb675748@syzkaller.appspotmail.com
Signed-off-by: Jan Kara <jack@suse.cz>
fs/udf/super.c

index 2f83c12..1eeb75a 100644 (file)
@@ -108,16 +108,10 @@ struct logicalVolIntegrityDescImpUse *udf_sb_lvidiu(struct super_block *sb)
                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 *)
+                                       (((uint8_t *)(lvid + 1)) + offset);
 }
 
 /* UDF filesystem type */
@@ -1542,6 +1536,7 @@ 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;
@@ -1568,15 +1563,27 @@ static void udf_load_logicalvolint(struct super_block *sb, struct kernel_extent_
 
                lvid = (struct logicalVolIntegrityDesc *)final_bh->b_data;
                if (lvid->nextIntegrityExt.extLength == 0)
-                       return;
+                       goto check;
 
                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;
+       }
 }
 
 /*