summaryrefslogtreecommitdiff
path: root/fs
diff options
context:
space:
mode:
authorDarrick J. Wong <darrick.wong@oracle.com>2020-10-25 17:16:04 -0700
committerDarrick J. Wong <darrick.wong@oracle.com>2020-10-26 18:32:30 -0700
commit424996fdf4a8f3e4b0ce3c6348ceee312f77eede (patch)
tree1c3c0108f3fc36d162cce6eff9ddfb46622b732d /fs
parent283e6c1eb142b8029c319eec98dc4bd8c8cb819c (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.c15
-rw-r--r--fs/xfs/scrub/common.c3
-rw-r--r--fs/xfs/scrub/inode_repair.c3
-rw-r--r--fs/xfs/scrub/scrub.c1
-rw-r--r--fs/xfs/xfs_icache.c2
-rw-r--r--fs/xfs/xfs_inode.c10
-rw-r--r--fs/xfs/xfs_itable.c11
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);