From: Dave Chinner Date: Thu, 2 May 2013 12:59:20 +0000 (-0500) Subject: xfs_repair: validate on-disk extent count better X-Git-Tag: v3.1.11~2 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=5fa285318bf8133041aab508a765c81a85174c65;p=thirdparty%2Fxfsprogs-dev.git xfs_repair: validate on-disk extent count better When scanning a btree format inode, we trust the extent count to be in range. However, values of the range 2^31 <= cnt < 2^32 are invalid and can cause problems with signed range checks. This results in assert failures which validating the extent count such as: xfs_repair: dinode.c:768: process_bmbt_reclist_int: Assertion `i < *numrecs' failed. Validate the extent count is at least within the positive range of a signed 32 bit integer before using it. Signed-off-by: Dave Chinner Reviewed-by: Christoph Hellwig Reviewed-by: Mark Tinguely Signed-off-by: Rich Johnston --- diff --git a/repair/dinode.c b/repair/dinode.c index 5a2da395e..239bb7b47 100644 --- a/repair/dinode.c +++ b/repair/dinode.c @@ -1293,7 +1293,7 @@ process_exinode( xfs_bmbt_rec_t *rp; xfs_dfiloff_t first_key; xfs_dfiloff_t last_key; - int numrecs; + int32_t numrecs; int ret; lino = XFS_AGINO_TO_INO(mp, agno, ino); @@ -1301,6 +1301,15 @@ process_exinode( *tot = 0; numrecs = XFS_DFORK_NEXTENTS(dip, whichfork); + /* + * We've already decided on the maximum number of extents on the inode, + * and numrecs may be corrupt. Hence make sure we only allow numrecs to + * be in the range of valid on-disk numbers, which is: + * 0 < numrecs < 2^31 - 1 + */ + if (numrecs < 0) + numrecs = *nex; + /* * XXX - if we were going to fix up the btree record, * we'd do it right here. For now, if there's a problem, @@ -2038,11 +2047,23 @@ process_inode_data_fork( { xfs_ino_t lino = XFS_AGINO_TO_INO(mp, agno, ino); int err = 0; + int nex; + + /* + * extent count on disk is only valid for positive values. The kernel + * uses negative values in memory. hence if we see negative numbers + * here, trash it! + */ + nex = be32_to_cpu(dino->di_nextents); + if (nex < 0) + *nextents = 1; + else + *nextents = nex; - *nextents = be32_to_cpu(dino->di_nextents); if (*nextents > be64_to_cpu(dino->di_nblocks)) *nextents = 1; + if (dino->di_format != XFS_DINODE_FMT_LOCAL && type != XR_INO_RTDATA) *dblkmap = blkmap_alloc(*nextents, XFS_DATA_FORK); *nextents = 0;