]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/commitdiff
f2fs: fix to do sanity check on .cp_pack_total_block_count
authorChao Yu <chao@kernel.org>
Mon, 21 Mar 2022 15:22:11 +0000 (23:22 +0800)
committerStefan Bader <stefan.bader@canonical.com>
Wed, 27 Apr 2022 09:56:30 +0000 (11:56 +0200)
BugLink: https://bugs.launchpad.net/bugs/1969110
commit 5b5b4f85b01604389f7a0f11ef180a725bf0e2d4 upstream.

As bughunter reported in bugzilla:

https://bugzilla.kernel.org/show_bug.cgi?id=215709

f2fs may hang when mounting a fuzzed image, the dmesg shows as below:

__filemap_get_folio+0x3a9/0x590
pagecache_get_page+0x18/0x60
__get_meta_page+0x95/0x460 [f2fs]
get_checkpoint_version+0x2a/0x1e0 [f2fs]
validate_checkpoint+0x8e/0x2a0 [f2fs]
f2fs_get_valid_checkpoint+0xd0/0x620 [f2fs]
f2fs_fill_super+0xc01/0x1d40 [f2fs]
mount_bdev+0x18a/0x1c0
f2fs_mount+0x15/0x20 [f2fs]
legacy_get_tree+0x28/0x50
vfs_get_tree+0x27/0xc0
path_mount+0x480/0xaa0
do_mount+0x7c/0xa0
__x64_sys_mount+0x8b/0xe0
do_syscall_64+0x38/0xc0
entry_SYSCALL_64_after_hwframe+0x44/0xae

The root cause is cp_pack_total_block_count field in checkpoint was fuzzed
to one, as calcuated, two cp pack block locates in the same block address,
so then read latter cp pack block, it will block on the page lock due to
the lock has already held when reading previous cp pack block, fix it by
adding sanity check for cp_pack_total_block_count.

Cc: stable@vger.kernel.org
Signed-off-by: Chao Yu <chao.yu@oppo.com>
Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
(cherry picked from commit b065f398c8602af2014015ac978526351140a047)
Signed-off-by: Paolo Pisati <paolo.pisati@canonical.com>
fs/f2fs/checkpoint.c

index 99fced979718e3b12fba6252c218c11a6f50f5ea..1ff32926e1999daa26a954f2a3687b12f21b8043 100644 (file)
@@ -867,6 +867,7 @@ static struct page *validate_checkpoint(struct f2fs_sb_info *sbi,
        struct page *cp_page_1 = NULL, *cp_page_2 = NULL;
        struct f2fs_checkpoint *cp_block = NULL;
        unsigned long long cur_version = 0, pre_version = 0;
+       unsigned int cp_blocks;
        int err;
 
        err = get_checkpoint_version(sbi, cp_addr, &cp_block,
@@ -874,15 +875,16 @@ static struct page *validate_checkpoint(struct f2fs_sb_info *sbi,
        if (err)
                return NULL;
 
-       if (le32_to_cpu(cp_block->cp_pack_total_block_count) >
-                                       sbi->blocks_per_seg) {
+       cp_blocks = le32_to_cpu(cp_block->cp_pack_total_block_count);
+
+       if (cp_blocks > sbi->blocks_per_seg || cp_blocks <= F2FS_CP_PACKS) {
                f2fs_warn(sbi, "invalid cp_pack_total_block_count:%u",
                          le32_to_cpu(cp_block->cp_pack_total_block_count));
                goto invalid_cp;
        }
        pre_version = *version;
 
-       cp_addr += le32_to_cpu(cp_block->cp_pack_total_block_count) - 1;
+       cp_addr += cp_blocks - 1;
        err = get_checkpoint_version(sbi, cp_addr, &cp_block,
                                        &cp_page_2, version);
        if (err)