]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/commitdiff
metadump: catch used extent array overflow
authorCarlos Maiolino <cem@kernel.org>
Thu, 13 Nov 2025 13:57:11 +0000 (14:57 +0100)
committerAndrey Albershteyn <aalbersh@kernel.org>
Mon, 24 Nov 2025 14:35:05 +0000 (15:35 +0100)
An user reported a SIGSEGV when attempting to create a metadump image of
a filesystem.
The reason is because we fail to catch a possible overflow in the
used extents array in process_exinode() which may happen if the extent
count is corrupted.
This leads process_bmbt_reclist() to attempt to index into the array
using the bogus extent count with:

convert_extent(&rp[numrecs - 1], &o, &s, &c, &f);

Fix this by extending the used counter to uint64_t and
checking for the overflow possibility.

Reported-by: hubert . <hubjin657@outlook.com>
Suggested-by: Dave Chinner <david@fromorbit.com>
Signed-off-by: Carlos Maiolino <cmaiolino@redhat.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
db/metadump.c

index 24eb99da172396ecab89be53570c1563da84882b..39639a0d51b0801c40d60c6f66994edd15d68625 100644 (file)
@@ -2395,21 +2395,24 @@ process_btinode(
 
 static int
 process_exinode(
-       struct xfs_dinode       *dip,
+       struct xfs_dinode       *dip,
        int                     whichfork)
 {
        xfs_extnum_t            max_nex = xfs_iext_max_nextents(
                        xfs_dinode_has_large_extent_counts(dip), whichfork);
        xfs_extnum_t            nex = xfs_dfork_nextents(dip, whichfork);
-       int                     used = nex * sizeof(struct xfs_bmbt_rec);
+       uint64_t                used;
 
-       if (nex > max_nex || used > XFS_DFORK_SIZE(dip, mp, whichfork)) {
-               if (metadump.show_warnings)
-                       print_warning("bad number of extents %llu in inode %lld",
-                               (unsigned long long)nex,
-                               (long long)metadump.cur_ino);
-               return 1;
-       }
+       if (check_mul_overflow(nex, sizeof(struct xfs_bmbt_rec), &used))
+               goto out_warn;
+
+       /* Invalid number of extents */
+       if (nex > max_nex)
+               goto out_warn;
+
+       /* Extent array should fit into the inode fork */
+       if (used > XFS_DFORK_SIZE(dip, mp, whichfork))
+               goto out_warn;
 
        /* Zero unused data fork past used extents */
        if (metadump.zero_stale_data &&
@@ -2421,6 +2424,12 @@ process_exinode(
        return process_bmbt_reclist(dip, whichfork,
                        (struct xfs_bmbt_rec *)XFS_DFORK_PTR(dip, whichfork),
                        nex);
+
+out_warn:
+       if (metadump.show_warnings)
+               print_warning("bad number of extents %llu in inode %lld",
+                       (unsigned long long)nex, (long long)metadump.cur_ino);
+       return 1;
 }
 
 static int