From: Darrick J. Wong <darrick.wong@xxxxxxxxxx> Add checks for the metadata inode flag so that we don't ever leak metadata inodes out to userspace, and we don't ever try to read a regular inode as metadata. Signed-off-by: Darrick J. Wong <darrick.wong@xxxxxxxxxx> --- fs/xfs/libxfs/xfs_inode_buf.c | 5 +++++ fs/xfs/scrub/common.c | 3 ++- fs/xfs/scrub/inode_repair.c | 3 +++ fs/xfs/scrub/scrub.c | 1 + fs/xfs/xfs_icache.c | 4 +++- fs/xfs/xfs_inode.c | 8 ++++++++ fs/xfs/xfs_itable.c | 6 ++++++ 7 files changed, 28 insertions(+), 2 deletions(-) diff --git a/fs/xfs/libxfs/xfs_inode_buf.c b/fs/xfs/libxfs/xfs_inode_buf.c index 8fa1050c1ae2..debacf69f126 100644 --- a/fs/xfs/libxfs/xfs_inode_buf.c +++ b/fs/xfs/libxfs/xfs_inode_buf.c @@ -557,6 +557,11 @@ xfs_dinode_verify( flags2 = be64_to_cpu(dip->di_flags2); + /* don't allow the metadata iflag if we don't have metadir */ + if ((flags2 & XFS_DIFLAG2_METADATA) && + !xfs_sb_version_hasmetadir(&mp->m_sb)) + return __this_address; + /* don't allow reflink/cowextsize if we don't have reflink */ if ((flags2 & (XFS_DIFLAG2_REFLINK | XFS_DIFLAG2_COWEXTSIZE)) && !xfs_sb_version_hasreflink(&mp->m_sb)) diff --git a/fs/xfs/scrub/common.c b/fs/xfs/scrub/common.c index c881b18dd799..c5cbe245082c 100644 --- a/fs/xfs/scrub/common.c +++ b/fs/xfs/scrub/common.c @@ -722,7 +722,8 @@ xchk_get_inode( error, __return_address); return error; } - if (VFS_I(ip)->i_generation != sc->sm->sm_gen) { + if (VFS_I(ip)->i_generation != sc->sm->sm_gen || + xfs_is_metadata_inode(ip)) { xfs_irele(ip); return -ENOENT; } diff --git a/fs/xfs/scrub/inode_repair.c b/fs/xfs/scrub/inode_repair.c index 3c9ac9e046fd..c036481a2407 100644 --- a/fs/xfs/scrub/inode_repair.c +++ b/fs/xfs/scrub/inode_repair.c @@ -168,6 +168,9 @@ xrep_dinode_flags( flags2 &= ~XFS_DIFLAG2_REFLINK; if (flags2 & XFS_DIFLAG2_REFLINK) flags2 &= ~XFS_DIFLAG2_DAX; + if (!xfs_sb_version_hasmetadir(&mp->m_sb) && + (flags2 & XFS_DIFLAG2_METADATA)) + flags2 &= ~XFS_DIFLAG2_METADATA; dip->di_flags = cpu_to_be16(flags); dip->di_flags2 = cpu_to_be64(flags2); } diff --git a/fs/xfs/scrub/scrub.c b/fs/xfs/scrub/scrub.c index 943dbec82d60..5f176575dad7 100644 --- a/fs/xfs/scrub/scrub.c +++ b/fs/xfs/scrub/scrub.c @@ -184,6 +184,7 @@ xchk_teardown( if (sc->ilock_flags) xfs_iunlock(sc->ip, sc->ilock_flags); if (sc->ip != ip_in && + !xfs_is_metadata_inode(sc->ip) && !xfs_internal_inum(sc->mp, sc->ip->i_ino)) xfs_irele(sc->ip); sc->ip = NULL; diff --git a/fs/xfs/xfs_icache.c b/fs/xfs/xfs_icache.c index 7c459e37396d..2881716e1c9e 100644 --- a/fs/xfs/xfs_icache.c +++ b/fs/xfs/xfs_icache.c @@ -927,7 +927,9 @@ xfs_imeta_iget( if (error) return error; - if (ftype == XFS_DIR3_FT_UNKNOWN || + if ((xfs_sb_version_hasmetadir(&mp->m_sb) && + !xfs_is_metadata_inode(ip)) || + ftype == XFS_DIR3_FT_UNKNOWN || xfs_mode_to_ftype(VFS_I(ip)->i_mode) != ftype) { xfs_irele(ip); return -EFSCORRUPTED; diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c index d2de7cd87b07..6a3a8137af4d 100644 --- a/fs/xfs/xfs_inode.c +++ b/fs/xfs/xfs_inode.c @@ -609,8 +609,13 @@ xfs_lookup( if (error) goto out_free_name; + if (xfs_is_metadata_inode(*ipp)) + goto out_irele; + return 0; +out_irele: + xfs_irele(*ipp); out_free_name: if (ci_name) kmem_free(ci_name->name); @@ -2658,6 +2663,9 @@ void xfs_imeta_irele( struct xfs_inode *ip) { + ASSERT(!xfs_sb_version_hasmetadir(&ip->i_mount->m_sb) || + xfs_is_metadata_inode(ip)); + xfs_irele(ip); } diff --git a/fs/xfs/xfs_itable.c b/fs/xfs/xfs_itable.c index 8c6ca9d364ec..c8aa7c6295c2 100644 --- a/fs/xfs/xfs_itable.c +++ b/fs/xfs/xfs_itable.c @@ -54,6 +54,12 @@ xfs_bulkstat_one_int( if (error) goto out_free; + if (xfs_is_metadata_inode(ip)) { + xfs_iunlock(ip, XFS_ILOCK_SHARED); + xfs_irele(ip); + goto out_free; + } + ASSERT(ip != NULL); ASSERT(ip->i_imap.im_blkno != 0); inode = VFS_I(ip);