]> www.infradead.org Git - users/hch/misc.git/commitdiff
erofs: fix crafted invalid cases for encoded extents
authorGao Xiang <hsiangkao@linux.alibaba.com>
Sun, 12 Oct 2025 13:59:25 +0000 (21:59 +0800)
committerGao Xiang <hsiangkao@linux.alibaba.com>
Fri, 17 Oct 2025 07:21:36 +0000 (15:21 +0800)
Robert recently reported two corrupted images that can cause system
crashes, which are related to the new encoded extents introduced
in Linux 6.15:

  - The first one [1] has plen != 0 (e.g. plen == 0x2000000) but
    (plen & Z_EROFS_EXTENT_PLEN_MASK) == 0. It is used to represent
    special extents such as sparse extents (!EROFS_MAP_MAPPED), but
    previously only plen == 0 was handled;

  - The second one [2] has pa 0xffffffffffdcffed and plen 0xb4000,
    then "cur [0xfffffffffffff000] += bvec.bv_len [0x1000]" in
    "} while ((cur += bvec.bv_len) < end);" wraps around, causing an
    out-of-bound access of pcl->compressed_bvecs[] in
    z_erofs_submit_queue().  EROFS only supports 48-bit physical block
    addresses (up to 1EiB for 4k blocks), so add a sanity check to
    enforce this.

Fixes: 1d191b4ca51d ("erofs: implement encoded extent metadata")
Reported-by: Robert Morris <rtm@csail.mit.edu>
Closes: https://lore.kernel.org/r/75022.1759355830@localhost [1]
Closes: https://lore.kernel.org/r/80524.1760131149@localhost [2]
Reviewed-by: Hongbo Li <lihongbo22@huawei.com>
Signed-off-by: Gao Xiang <hsiangkao@linux.alibaba.com>
fs/erofs/zmap.c

index e5581dbeb4c2bc6ccc53cffbd705dc3af9c23152..8007814f721e44a36de6bdfd9bd3a713c96532f9 100644 (file)
@@ -596,7 +596,7 @@ static int z_erofs_map_blocks_ext(struct inode *inode,
                        vi->z_fragmentoff = map->m_plen;
                        if (recsz > offsetof(struct z_erofs_extent, pstart_lo))
                                vi->z_fragmentoff |= map->m_pa << 32;
-               } else if (map->m_plen) {
+               } else if (map->m_plen & Z_EROFS_EXTENT_PLEN_MASK) {
                        map->m_flags |= EROFS_MAP_MAPPED |
                                EROFS_MAP_FULL_MAPPED | EROFS_MAP_ENCODED;
                        fmt = map->m_plen >> Z_EROFS_EXTENT_PLEN_FMT_BIT;
@@ -715,6 +715,7 @@ static int z_erofs_map_sanity_check(struct inode *inode,
                                    struct erofs_map_blocks *map)
 {
        struct erofs_sb_info *sbi = EROFS_I_SB(inode);
+       u64 pend;
 
        if (!(map->m_flags & EROFS_MAP_ENCODED))
                return 0;
@@ -732,6 +733,10 @@ static int z_erofs_map_sanity_check(struct inode *inode,
        if (unlikely(map->m_plen > Z_EROFS_PCLUSTER_MAX_SIZE ||
                     map->m_llen > Z_EROFS_PCLUSTER_MAX_DSIZE))
                return -EOPNOTSUPP;
+       /* Filesystems beyond 48-bit physical block addresses are invalid */
+       if (unlikely(check_add_overflow(map->m_pa, map->m_plen, &pend) ||
+                    (pend >> sbi->blkszbits) >= BIT_ULL(48)))
+               return -EFSCORRUPTED;
        return 0;
 }