diff options
author | Amir Goldstein <amir73il@gmail.com> | 2017-02-02 02:55:59 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2017-02-04 03:47:11 -0500 |
commit | e5325fcf70b1cd2c45503b854fde3f1bd44216da (patch) | |
tree | bf257857d39f5a0b6ba3fd11cdbc17e7d0ec7cbe /fs | |
parent | 624e54b5aff15c6d49974f404c5cef5ce7f89cd0 (diff) |
xfs: sanity check directory inode di_size
commit 3c6f46eacd876bd723a9bad3c6882714c052fd8e upstream.
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 <darrick.wong@oracle.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Amir Goldstein <amir73il@gmail.com>
Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/xfs/libxfs/xfs_dir2.c | 3 | ||||
-rw-r--r-- | fs/xfs/libxfs/xfs_inode_buf.c | 7 |
2 files changed, 7 insertions, 3 deletions
diff --git a/fs/xfs/libxfs/xfs_dir2.c b/fs/xfs/libxfs/xfs_dir2.c index 20a96dd5af7e..ec326d272efb 100644 --- a/fs/xfs/libxfs/xfs_dir2.c +++ b/fs/xfs/libxfs/xfs_dir2.c | |||
@@ -631,7 +631,8 @@ xfs_dir2_isblock( | |||
631 | if ((rval = xfs_bmap_last_offset(args->dp, &last, XFS_DATA_FORK))) | 631 | if ((rval = xfs_bmap_last_offset(args->dp, &last, XFS_DATA_FORK))) |
632 | return rval; | 632 | return rval; |
633 | rval = XFS_FSB_TO_B(args->dp->i_mount, last) == args->geo->blksize; | 633 | 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; | ||
635 | *vp = rval; | 636 | *vp = rval; |
636 | return 0; | 637 | return 0; |
637 | } | 638 | } |
diff --git a/fs/xfs/libxfs/xfs_inode_buf.c b/fs/xfs/libxfs/xfs_inode_buf.c index c906e50515f0..977245de8953 100644 --- a/fs/xfs/libxfs/xfs_inode_buf.c +++ b/fs/xfs/libxfs/xfs_inode_buf.c | |||
@@ -386,6 +386,7 @@ xfs_dinode_verify( | |||
386 | struct xfs_inode *ip, | 386 | struct xfs_inode *ip, |
387 | struct xfs_dinode *dip) | 387 | struct xfs_dinode *dip) |
388 | { | 388 | { |
389 | uint16_t mode; | ||
389 | uint16_t flags; | 390 | uint16_t flags; |
390 | uint64_t flags2; | 391 | uint64_t flags2; |
391 | 392 | ||
@@ -396,8 +397,10 @@ xfs_dinode_verify( | |||
396 | if (be64_to_cpu(dip->di_size) & (1ULL << 63)) | 397 | if (be64_to_cpu(dip->di_size) & (1ULL << 63)) |
397 | return false; | 398 | return false; |
398 | 399 | ||
399 | /* No zero-length symlinks. */ | 400 | mode = be16_to_cpu(dip->di_mode); |
400 | if (S_ISLNK(be16_to_cpu(dip->di_mode)) && dip->di_size == 0) | 401 | |
402 | /* No zero-length symlinks/dirs. */ | ||
403 | if ((S_ISLNK(mode) || S_ISDIR(mode)) && dip->di_size == 0) | ||
401 | return false; | 404 | return false; |
402 | 405 | ||
403 | /* only version 3 or greater inodes are extensively verified here */ | 406 | /* only version 3 or greater inodes are extensively verified here */ |