diff options
author | Darrick J. Wong <darrick.wong@oracle.com> | 2020-10-25 17:16:04 -0700 |
---|---|---|
committer | Darrick J. Wong <darrick.wong@oracle.com> | 2020-10-26 18:32:30 -0700 |
commit | 424996fdf4a8f3e4b0ce3c6348ceee312f77eede (patch) | |
tree | 1c3c0108f3fc36d162cce6eff9ddfb46622b732d /fs | |
parent | 283e6c1eb142b8029c319eec98dc4bd8c8cb819c (diff) |
xfs: enforce metadata inode flag
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@oracle.com>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/xfs/libxfs/xfs_inode_buf.c | 15 | ||||
-rw-r--r-- | fs/xfs/scrub/common.c | 3 | ||||
-rw-r--r-- | fs/xfs/scrub/inode_repair.c | 3 | ||||
-rw-r--r-- | fs/xfs/scrub/scrub.c | 1 | ||||
-rw-r--r-- | fs/xfs/xfs_icache.c | 2 | ||||
-rw-r--r-- | fs/xfs/xfs_inode.c | 10 | ||||
-rw-r--r-- | fs/xfs/xfs_itable.c | 11 |
7 files changed, 44 insertions, 1 deletions
diff --git a/fs/xfs/libxfs/xfs_inode_buf.c b/fs/xfs/libxfs/xfs_inode_buf.c index fd418ed7e087..ca37cc86ddb8 100644 --- a/fs/xfs/libxfs/xfs_inode_buf.c +++ b/fs/xfs/libxfs/xfs_inode_buf.c @@ -538,6 +538,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)) @@ -566,6 +571,16 @@ xfs_dinode_verify( !xfs_sb_version_hasbigtime(&mp->m_sb)) return __this_address; + if (flags2 & XFS_DIFLAG2_METADATA) { + /* Metadata files can only be directories or regular files. */ + if (!S_ISDIR(mode) && !S_ISREG(mode)) + return __this_address; + + /* They must have zero access permissions */ + if (mode & 00777) + return __this_address; + } + return NULL; } diff --git a/fs/xfs/scrub/common.c b/fs/xfs/scrub/common.c index 80eb58a92818..c8ec7abbb74e 100644 --- a/fs/xfs/scrub/common.c +++ b/fs/xfs/scrub/common.c @@ -744,7 +744,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 c075963a4c17..7f7e44c24447 100644 --- a/fs/xfs/scrub/inode_repair.c +++ b/fs/xfs/scrub/inode_repair.c @@ -171,6 +171,9 @@ xrep_dinode_flags( flags2 &= ~XFS_DIFLAG2_DAX; if (!xfs_sb_version_hasbigtime(&mp->m_sb)) flags2 &= ~XFS_DIFLAG2_BIGTIME; + 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 966d9c892232..482cc13d709c 100644 --- a/fs/xfs/scrub/scrub.c +++ b/fs/xfs/scrub/scrub.c @@ -169,6 +169,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 e7c804b90639..126b308dfa5c 100644 --- a/fs/xfs/xfs_icache.c +++ b/fs/xfs/xfs_icache.c @@ -893,6 +893,8 @@ xfs_imeta_iget( goto bad_rele; if (xfs_mode_to_ftype(VFS_I(ip)->i_mode) != ftype) goto bad_rele; + if (xfs_sb_version_hasmetadir(&mp->m_sb) && !xfs_is_metadata_inode(ip)) + goto bad_rele; *ipp = ip; return 0; diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c index 41bc5d947945..2598b4c31ed3 100644 --- a/fs/xfs/xfs_inode.c +++ b/fs/xfs/xfs_inode.c @@ -589,8 +589,15 @@ xfs_lookup( if (error) goto out_free_name; + if (xfs_is_metadata_inode(*ipp)) { + error = -EINVAL; + goto out_irele; + } + return 0; +out_irele: + xfs_irele(*ipp); out_free_name: if (ci_name) kmem_free(ci_name->name); @@ -2755,6 +2762,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 16ca97a7ff00..050e00dc957e 100644 --- a/fs/xfs/xfs_itable.c +++ b/fs/xfs/xfs_itable.c @@ -75,6 +75,17 @@ xfs_bulkstat_one_int( if (error) goto out; + /* + * Inodes marked as being metadata are treated the same as "internal" + * metadata inodes (which are rooted in the superblock). + */ + if (xfs_is_metadata_inode(ip)) { + xfs_iunlock(ip, XFS_ILOCK_SHARED); + xfs_irele(ip); + error = -EINVAL; + goto out_advance; + } + ASSERT(ip != NULL); ASSERT(ip->i_imap.im_blkno != 0); inode = VFS_I(ip); |