From 96544a93eda44ec177760ae6b511188dfc6b047e Mon Sep 17 00:00:00 2001 From: Jeff Johnson Date: Sun, 26 May 2024 11:53:49 -0700 Subject: [PATCH 01/10] jbd2: add missing MODULE_DESCRIPTION() Fix the 'make W=1' warning: WARNING: modpost: missing MODULE_DESCRIPTION() in fs/jbd2/jbd2.o Signed-off-by: Jeff Johnson Signed-off-by: Jan Kara Message-Id: <20240526-md-fs-jbd2-v1-1-7bba6665327d@quicinc.com> --- fs/jbd2/journal.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/jbd2/journal.c b/fs/jbd2/journal.c index 03c4b9214f56..0d2825c131cc 100644 --- a/fs/jbd2/journal.c +++ b/fs/jbd2/journal.c @@ -3181,6 +3181,7 @@ static void __exit journal_exit(void) jbd2_journal_destroy_caches(); } +MODULE_DESCRIPTION("Generic filesystem journal-writing module"); MODULE_LICENSE("GPL"); module_init(journal_init); module_exit(journal_exit); From 03fceb8dbd09923f271271a438da049f75eb9c10 Mon Sep 17 00:00:00 2001 From: Jeff Johnson Date: Sun, 26 May 2024 12:05:23 -0700 Subject: [PATCH 02/10] isofs: add missing MODULE_DESCRIPTION() Fix the 'make W=1' warning: WARNING: modpost: missing MODULE_DESCRIPTION() in fs/isofs/isofs.o Signed-off-by: Jeff Johnson Signed-off-by: Jan Kara Message-Id: <20240526-md-fs-isofs-v1-1-60e2e36a3d46@quicinc.com> --- fs/isofs/inode.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/isofs/inode.c b/fs/isofs/inode.c index 93b1077a380a..2bb8b422f434 100644 --- a/fs/isofs/inode.c +++ b/fs/isofs/inode.c @@ -1625,4 +1625,5 @@ static void __exit exit_iso9660_fs(void) module_init(init_iso9660_fs) module_exit(exit_iso9660_fs) +MODULE_DESCRIPTION("ISO 9660 CDROM file system support"); MODULE_LICENSE("GPL"); From c1f1b25a6089eb93a11a147e8de766c0d6ad29ac Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Mon, 20 May 2024 15:18:44 +0200 Subject: [PATCH 03/10] udf: Drop pointless IS_IMMUTABLE and IS_APPEND check udf_setsize() checks for IS_IMMUTABLE and IS_APPEND flags. This is however pointless as UDF does not have capability to store these flags and never allows to set them. Furthermore this is the only place in UDF code that was actually checking these flags. Remove the pointless check. Signed-off-by: Jan Kara --- fs/udf/inode.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/fs/udf/inode.c b/fs/udf/inode.c index 2fb21c5ffccf..9d39fdd26015 100644 --- a/fs/udf/inode.c +++ b/fs/udf/inode.c @@ -1247,8 +1247,6 @@ int udf_setsize(struct inode *inode, loff_t newsize) if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode))) return -EINVAL; - if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) - return -EPERM; filemap_invalidate_lock(inode->i_mapping); iinfo = UDF_I(inode); From 8832fc1e502687869606bb0a7b79848ed3bf036f Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Mon, 20 May 2024 15:23:37 +0200 Subject: [PATCH 04/10] udf: Fix lock ordering in udf_evict_inode() udf_evict_inode() calls udf_setsize() to truncate deleted inode. However inode deletion through udf_evict_inode() can happen from inode reclaim context and udf_setsize() grabs mapping->invalidate_lock which isn't generally safe to acquire from fs reclaim context since we allocate pages under mapping->invalidate_lock for example in a page fault path. This is however not a real deadlock possibility as by the time udf_evict_inode() is called, nobody can be accessing the inode, even less work with its page cache. So this is just a lockdep triggering false positive. Fix the problem by moving mapping->invalidate_lock locking outsize of udf_setsize() into udf_setattr() as grabbing mapping->invalidate_lock from udf_evict_inode() is pointless. Reported-by: syzbot+0333a6f4b88bcd68a62f@syzkaller.appspotmail.com Fixes: b9a861fd527a ("udf: Protect truncate and file type conversion with invalidate_lock") Signed-off-by: Jan Kara --- fs/udf/file.c | 2 ++ fs/udf/inode.c | 11 ++++------- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/fs/udf/file.c b/fs/udf/file.c index 97c59585208c..3a4179de316b 100644 --- a/fs/udf/file.c +++ b/fs/udf/file.c @@ -232,7 +232,9 @@ static int udf_setattr(struct mnt_idmap *idmap, struct dentry *dentry, if ((attr->ia_valid & ATTR_SIZE) && attr->ia_size != i_size_read(inode)) { + filemap_invalidate_lock(inode->i_mapping); error = udf_setsize(inode, attr->ia_size); + filemap_invalidate_unlock(inode->i_mapping); if (error) return error; } diff --git a/fs/udf/inode.c b/fs/udf/inode.c index 9d39fdd26015..4726a4d014b6 100644 --- a/fs/udf/inode.c +++ b/fs/udf/inode.c @@ -1248,7 +1248,6 @@ int udf_setsize(struct inode *inode, loff_t newsize) S_ISLNK(inode->i_mode))) return -EINVAL; - filemap_invalidate_lock(inode->i_mapping); iinfo = UDF_I(inode); if (newsize > inode->i_size) { if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) { @@ -1261,11 +1260,11 @@ int udf_setsize(struct inode *inode, loff_t newsize) } err = udf_expand_file_adinicb(inode); if (err) - goto out_unlock; + return err; } err = udf_extend_file(inode, newsize); if (err) - goto out_unlock; + return err; set_size: truncate_setsize(inode, newsize); } else { @@ -1283,14 +1282,14 @@ set_size: err = block_truncate_page(inode->i_mapping, newsize, udf_get_block); if (err) - goto out_unlock; + return err; truncate_setsize(inode, newsize); down_write(&iinfo->i_data_sem); udf_clear_extent_cache(inode); err = udf_truncate_extents(inode); up_write(&iinfo->i_data_sem); if (err) - goto out_unlock; + return err; } update_time: inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode)); @@ -1298,8 +1297,6 @@ update_time: udf_sync_inode(inode); else mark_inode_dirty(inode); -out_unlock: - filemap_invalidate_unlock(inode->i_mapping); return err; } From 27ab33854873e6fb958cb074681a0107cc2ecc4c Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Mon, 17 Jun 2024 17:41:51 +0200 Subject: [PATCH 05/10] udf: Fix bogus checksum computation in udf_rename() Syzbot reports uninitialized memory access in udf_rename() when updating checksum of '..' directory entry of a moved directory. This is indeed true as we pass on-stack diriter.fi to the udf_update_tag() and because that has only struct fileIdentDesc included in it and not the impUse or name fields, the checksumming function is going to checksum random stack contents beyond the end of the structure. This is actually harmless because the following udf_fiiter_write_fi() will recompute the checksum from on-disk buffers where everything is properly included. So all that is needed is just removing the bogus calculation. Fixes: e9109a92d2a9 ("udf: Convert udf_rename() to new directory iteration code") Link: https://lore.kernel.org/all/000000000000cf405f060d8f75a9@google.com/T/ Link: https://patch.msgid.link/20240617154201.29512-1-jack@suse.cz Reported-by: syzbot+d31185aa54170f7fc1f5@syzkaller.appspotmail.com Signed-off-by: Jan Kara --- fs/udf/namei.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/fs/udf/namei.c b/fs/udf/namei.c index 1308109fd42d..78a603129dd5 100644 --- a/fs/udf/namei.c +++ b/fs/udf/namei.c @@ -876,8 +876,6 @@ static int udf_rename(struct mnt_idmap *idmap, struct inode *old_dir, if (has_diriter) { diriter.fi.icb.extLocation = cpu_to_lelb(UDF_I(new_dir)->i_location); - udf_update_tag((char *)&diriter.fi, - udf_dir_entry_len(&diriter.fi)); udf_fiiter_write_fi(&diriter, NULL); udf_fiiter_release(&diriter); } From a90d4471146de21745980cba51ce88e7926bcc4f Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Mon, 17 Jun 2024 17:41:52 +0200 Subject: [PATCH 06/10] udf: Avoid using corrupted block bitmap buffer When the filesystem block bitmap is corrupted, we detect the corruption while loading the bitmap and fail the allocation with error. However the next allocation from the same bitmap will notice the bitmap buffer is already loaded and tries to allocate from the bitmap with mixed results (depending on the exact nature of the bitmap corruption). Fix the problem by using BH_verified bit to indicate whether the bitmap is valid or not. Reported-by: syzbot+5f682cd029581f9edfd1@syzkaller.appspotmail.com CC: stable@vger.kernel.org Link: https://patch.msgid.link/20240617154201.29512-2-jack@suse.cz Fixes: 1e0d4adf17e7 ("udf: Check consistency of Space Bitmap Descriptor") Signed-off-by: Jan Kara --- fs/udf/balloc.c | 15 +++++++++++++-- fs/udf/super.c | 3 ++- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/fs/udf/balloc.c b/fs/udf/balloc.c index ab3ffc355949..558ad046972a 100644 --- a/fs/udf/balloc.c +++ b/fs/udf/balloc.c @@ -64,8 +64,12 @@ static int read_block_bitmap(struct super_block *sb, } for (i = 0; i < count; i++) - if (udf_test_bit(i + off, bh->b_data)) + if (udf_test_bit(i + off, bh->b_data)) { + bitmap->s_block_bitmap[bitmap_nr] = + ERR_PTR(-EFSCORRUPTED); + brelse(bh); return -EFSCORRUPTED; + } return 0; } @@ -81,8 +85,15 @@ static int __load_block_bitmap(struct super_block *sb, block_group, nr_groups); } - if (bitmap->s_block_bitmap[block_group]) + if (bitmap->s_block_bitmap[block_group]) { + /* + * The bitmap failed verification in the past. No point in + * trying again. + */ + if (IS_ERR(bitmap->s_block_bitmap[block_group])) + return PTR_ERR(bitmap->s_block_bitmap[block_group]); return block_group; + } retval = read_block_bitmap(sb, bitmap, block_group, block_group); if (retval < 0) diff --git a/fs/udf/super.c b/fs/udf/super.c index 9381a66c6ce5..92d477053905 100644 --- a/fs/udf/super.c +++ b/fs/udf/super.c @@ -336,7 +336,8 @@ static void udf_sb_free_bitmap(struct udf_bitmap *bitmap) int nr_groups = bitmap->s_nr_groups; for (i = 0; i < nr_groups; i++) - brelse(bitmap->s_block_bitmap[i]); + if (!IS_ERR_OR_NULL(bitmap->s_block_bitmap[i])) + brelse(bitmap->s_block_bitmap[i]); kvfree(bitmap); } From 8037da38d33cd354deafc41eee62da0fbcb5135c Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Mon, 17 Jun 2024 17:41:53 +0200 Subject: [PATCH 07/10] udf: Drop load_block_bitmap() wrapper The wrapper is completely pointless as all the checks are already done in __load_block_bitmap(). Just drop it and rename __load_block_bitmap(). Link: https://patch.msgid.link/20240617154201.29512-3-jack@suse.cz Signed-off-by: Jan Kara --- fs/udf/balloc.c | 23 +++-------------------- 1 file changed, 3 insertions(+), 20 deletions(-) diff --git a/fs/udf/balloc.c b/fs/udf/balloc.c index 558ad046972a..a76490b2ca19 100644 --- a/fs/udf/balloc.c +++ b/fs/udf/balloc.c @@ -73,9 +73,9 @@ static int read_block_bitmap(struct super_block *sb, return 0; } -static int __load_block_bitmap(struct super_block *sb, - struct udf_bitmap *bitmap, - unsigned int block_group) +static int load_block_bitmap(struct super_block *sb, + struct udf_bitmap *bitmap, + unsigned int block_group) { int retval = 0; int nr_groups = bitmap->s_nr_groups; @@ -102,23 +102,6 @@ static int __load_block_bitmap(struct super_block *sb, return block_group; } -static inline int load_block_bitmap(struct super_block *sb, - struct udf_bitmap *bitmap, - unsigned int block_group) -{ - int slot; - - slot = __load_block_bitmap(sb, bitmap, block_group); - - if (slot < 0) - return slot; - - if (!bitmap->s_block_bitmap[slot]) - return -EIO; - - return slot; -} - static void udf_add_free_space(struct super_block *sb, u16 partition, u32 cnt) { struct udf_sb_info *sbi = UDF_SB(sb); From ebbe26fd54a9621994bc16b14f2ba8f84c089693 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Thu, 20 Jun 2024 12:52:17 +0200 Subject: [PATCH 08/10] udf: Avoid excessive partition lengths Avoid mounting filesystems where the partition would overflow the 32-bits used for block number. Also refuse to mount filesystems where the partition length is so large we cannot safely index bits in a block bitmap. Link: https://patch.msgid.link/20240620130403.14731-1-jack@suse.cz Signed-off-by: Jan Kara --- fs/udf/super.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/fs/udf/super.c b/fs/udf/super.c index 92d477053905..3460ecc826d1 100644 --- a/fs/udf/super.c +++ b/fs/udf/super.c @@ -1111,12 +1111,19 @@ static int udf_fill_partdesc_info(struct super_block *sb, struct udf_part_map *map; struct udf_sb_info *sbi = UDF_SB(sb); struct partitionHeaderDesc *phd; + u32 sum; int err; map = &sbi->s_partmaps[p_index]; map->s_partition_len = le32_to_cpu(p->partitionLength); /* blocks */ map->s_partition_root = le32_to_cpu(p->partitionStartingLocation); + if (check_add_overflow(map->s_partition_root, map->s_partition_len, + &sum)) { + udf_err(sb, "Partition %d has invalid location %u + %u\n", + p_index, map->s_partition_root, map->s_partition_len); + return -EFSCORRUPTED; + } if (p->accessType == cpu_to_le32(PD_ACCESS_TYPE_READ_ONLY)) map->s_partition_flags |= UDF_PART_FLAG_READ_ONLY; @@ -1172,6 +1179,14 @@ static int udf_fill_partdesc_info(struct super_block *sb, bitmap->s_extPosition = le32_to_cpu( phd->unallocSpaceBitmap.extPosition); map->s_partition_flags |= UDF_PART_FLAG_UNALLOC_BITMAP; + /* Check whether math over bitmap won't overflow. */ + if (check_add_overflow(map->s_partition_len, + sizeof(struct spaceBitmapDesc) << 3, + &sum)) { + udf_err(sb, "Partition %d is too long (%u)\n", p_index, + map->s_partition_len); + return -EFSCORRUPTED; + } udf_debug("unallocSpaceBitmap (part %d) @ %u\n", p_index, bitmap->s_extPosition); } From 56e69e59751d20993f243fb7dd6991c4e522424c Mon Sep 17 00:00:00 2001 From: Roman Smirnov Date: Thu, 20 Jun 2024 10:24:13 +0300 Subject: [PATCH 09/10] udf: prevent integer overflow in udf_bitmap_free_blocks() An overflow may occur if the function is called with the last block and an offset greater than zero. It is necessary to add a check to avoid this. Found by Linux Verification Center (linuxtesting.org) with Svace. [JK: Make test cover also unalloc table freeing] Link: https://patch.msgid.link/20240620072413.7448-1-r.smirnov@omp.ru Suggested-by: Jan Kara Signed-off-by: Roman Smirnov Signed-off-by: Jan Kara --- fs/udf/balloc.c | 36 +++++++++++++----------------------- 1 file changed, 13 insertions(+), 23 deletions(-) diff --git a/fs/udf/balloc.c b/fs/udf/balloc.c index a76490b2ca19..d8fc11765d61 100644 --- a/fs/udf/balloc.c +++ b/fs/udf/balloc.c @@ -18,6 +18,7 @@ #include "udfdecl.h" #include +#include #include "udf_i.h" #include "udf_sb.h" @@ -123,7 +124,6 @@ static void udf_bitmap_free_blocks(struct super_block *sb, { struct udf_sb_info *sbi = UDF_SB(sb); struct buffer_head *bh = NULL; - struct udf_part_map *partmap; unsigned long block; unsigned long block_group; unsigned long bit; @@ -132,19 +132,9 @@ static void udf_bitmap_free_blocks(struct super_block *sb, unsigned long overflow; mutex_lock(&sbi->s_alloc_mutex); - partmap = &sbi->s_partmaps[bloc->partitionReferenceNum]; - if (bloc->logicalBlockNum + count < count || - (bloc->logicalBlockNum + count) > partmap->s_partition_len) { - udf_debug("%u < %d || %u + %u > %u\n", - bloc->logicalBlockNum, 0, - bloc->logicalBlockNum, count, - partmap->s_partition_len); - goto error_return; - } - + /* We make sure this cannot overflow when mounting the filesystem */ block = bloc->logicalBlockNum + offset + (sizeof(struct spaceBitmapDesc) << 3); - do { overflow = 0; block_group = block >> (sb->s_blocksize_bits + 3); @@ -374,7 +364,6 @@ static void udf_table_free_blocks(struct super_block *sb, uint32_t count) { struct udf_sb_info *sbi = UDF_SB(sb); - struct udf_part_map *partmap; uint32_t start, end; uint32_t elen; struct kernel_lb_addr eloc; @@ -383,16 +372,6 @@ static void udf_table_free_blocks(struct super_block *sb, struct udf_inode_info *iinfo; mutex_lock(&sbi->s_alloc_mutex); - partmap = &sbi->s_partmaps[bloc->partitionReferenceNum]; - if (bloc->logicalBlockNum + count < count || - (bloc->logicalBlockNum + count) > partmap->s_partition_len) { - udf_debug("%u < %d || %u + %u > %u\n", - bloc->logicalBlockNum, 0, - bloc->logicalBlockNum, count, - partmap->s_partition_len); - goto error_return; - } - iinfo = UDF_I(table); udf_add_free_space(sb, sbi->s_partition, count); @@ -667,6 +646,17 @@ void udf_free_blocks(struct super_block *sb, struct inode *inode, { uint16_t partition = bloc->partitionReferenceNum; struct udf_part_map *map = &UDF_SB(sb)->s_partmaps[partition]; + uint32_t blk; + + if (check_add_overflow(bloc->logicalBlockNum, offset, &blk) || + check_add_overflow(blk, count, &blk) || + bloc->logicalBlockNum + count > map->s_partition_len) { + udf_debug("Invalid request to free blocks: (%d, %u), off %u, " + "len %u, partition len %u\n", + partition, bloc->logicalBlockNum, offset, count, + map->s_partition_len); + return; + } if (map->s_partition_flags & UDF_PART_FLAG_UNALLOC_BITMAP) { udf_bitmap_free_blocks(sb, map->s_uspace.s_bitmap, From 322a6aff03937aa1ece33b4e46c298eafaf9ac41 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Mon, 24 Jun 2024 17:12:56 +0200 Subject: [PATCH 10/10] ext2: Verify bitmap and itable block numbers before using them Verify bitmap block numbers and inode table blocks are sane before using them for checking bits in the block bitmap. CC: stable@vger.kernel.org Signed-off-by: Jan Kara --- fs/ext2/balloc.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/fs/ext2/balloc.c b/fs/ext2/balloc.c index 1bfd6ab11038..b8cfab8f98b9 100644 --- a/fs/ext2/balloc.c +++ b/fs/ext2/balloc.c @@ -77,26 +77,33 @@ static int ext2_valid_block_bitmap(struct super_block *sb, ext2_grpblk_t next_zero_bit; ext2_fsblk_t bitmap_blk; ext2_fsblk_t group_first_block; + ext2_grpblk_t max_bit; group_first_block = ext2_group_first_block_no(sb, block_group); + max_bit = ext2_group_last_block_no(sb, block_group) - group_first_block; /* check whether block bitmap block number is set */ bitmap_blk = le32_to_cpu(desc->bg_block_bitmap); offset = bitmap_blk - group_first_block; - if (!ext2_test_bit(offset, bh->b_data)) + if (offset < 0 || offset > max_bit || + !ext2_test_bit(offset, bh->b_data)) /* bad block bitmap */ goto err_out; /* check whether the inode bitmap block number is set */ bitmap_blk = le32_to_cpu(desc->bg_inode_bitmap); offset = bitmap_blk - group_first_block; - if (!ext2_test_bit(offset, bh->b_data)) + if (offset < 0 || offset > max_bit || + !ext2_test_bit(offset, bh->b_data)) /* bad block bitmap */ goto err_out; /* check whether the inode table block number is set */ bitmap_blk = le32_to_cpu(desc->bg_inode_table); offset = bitmap_blk - group_first_block; + if (offset < 0 || offset > max_bit || + offset + EXT2_SB(sb)->s_itb_per_group - 1 > max_bit) + goto err_out; next_zero_bit = ext2_find_next_zero_bit(bh->b_data, offset + EXT2_SB(sb)->s_itb_per_group, offset);