Skip to content

Commit 3c6f46e

Browse files
amir73ildjwong
authored andcommitted
xfs: sanity check directory inode di_size
This changes fixes an assertion hit when fuzzing on-disk i_mode values. The easy case to fix is when changing an empty file i_mode to S_IFDIR. In this case, xfs_dinode_verify() detects an illegal zero size for directory and fails to load the inode structure from disk. For the case of non empty file whose i_mode is changed to S_IFDIR, the ASSERT() statement in xfs_dir2_isblock() is replaced with return -EFSCORRUPTED, to avoid interacting with corrupted jusk also when XFS_DEBUG is disabled. Suggested-by: Darrick J. Wong <[email protected]> Reviewed-by: Christoph Hellwig <[email protected]> Signed-off-by: Amir Goldstein <[email protected]> Reviewed-by: Darrick J. Wong <[email protected]> Signed-off-by: Darrick J. Wong <[email protected]>
1 parent bf46ecc commit 3c6f46e

File tree

2 files changed

+7
-3
lines changed

2 files changed

+7
-3
lines changed

fs/xfs/libxfs/xfs_dir2.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -631,7 +631,8 @@ xfs_dir2_isblock(
631631
if ((rval = xfs_bmap_last_offset(args->dp, &last, XFS_DATA_FORK)))
632632
return rval;
633633
rval = XFS_FSB_TO_B(args->dp->i_mount, last) == args->geo->blksize;
634-
ASSERT(rval == 0 || args->dp->i_d.di_size == args->geo->blksize);
634+
if (rval != 0 && args->dp->i_d.di_size != args->geo->blksize)
635+
return -EFSCORRUPTED;
635636
*vp = rval;
636637
return 0;
637638
}

fs/xfs/libxfs/xfs_inode_buf.c

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -386,6 +386,7 @@ xfs_dinode_verify(
386386
xfs_ino_t ino,
387387
struct xfs_dinode *dip)
388388
{
389+
uint16_t mode;
389390
uint16_t flags;
390391
uint64_t flags2;
391392

@@ -396,8 +397,10 @@ xfs_dinode_verify(
396397
if (be64_to_cpu(dip->di_size) & (1ULL << 63))
397398
return false;
398399

399-
/* No zero-length symlinks. */
400-
if (S_ISLNK(be16_to_cpu(dip->di_mode)) && dip->di_size == 0)
400+
mode = be16_to_cpu(dip->di_mode);
401+
402+
/* No zero-length symlinks/dirs. */
403+
if ((S_ISLNK(mode) || S_ISDIR(mode)) && dip->di_size == 0)
401404
return false;
402405

403406
/* only version 3 or greater inodes are extensively verified here */

0 commit comments

Comments
 (0)