]> www.infradead.org Git - users/hch/xfsprogs.git/commitdiff
xfs: enforce metadata inode flag
authorDarrick J. Wong <djwong@kernel.org>
Wed, 29 May 2024 04:10:57 +0000 (21:10 -0700)
committerDarrick J. Wong <djwong@kernel.org>
Tue, 30 Jul 2024 00:13:16 +0000 (17:13 -0700)
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 <djwong@kernel.org>
libxfs/inode.c
libxfs/xfs_inode_buf.c
libxfs/xfs_inode_buf.h

index 35fc2632e1042544599896c6218290349189901a..c89ccf555c3f1c2a2d8cf3e67e3e2eb3e782424e 100644 (file)
@@ -227,6 +227,8 @@ libxfs_metafile_iget(
 
        if (inode_wrong_type(VFS_I(ip), mode))
                goto bad_rele;
+       if (xfs_has_metadir(mp) && !xfs_is_metadir_inode(ip))
+               goto bad_rele;
 
        *ipp = ip;
        return 0;
index 856659cc3e089ac42ff769e0f8a336077ef5212b..98a39e0716da71133b8f64eae12de1f7ba5021f2 100644 (file)
@@ -480,6 +480,75 @@ xfs_dinode_verify_nrext64(
        return NULL;
 }
 
+/*
+ * Validate all the picky requirements we have for a file that claims to be
+ * filesystem metadata.
+ */
+xfs_failaddr_t
+xfs_dinode_verify_metadir(
+       struct xfs_mount        *mp,
+       struct xfs_dinode       *dip,
+       uint16_t                mode,
+       uint16_t                flags,
+       uint64_t                flags2)
+{
+       if (!xfs_has_metadir(mp))
+               return __this_address;
+
+       /* V5 filesystem only */
+       if (dip->di_version < 3)
+               return __this_address;
+
+       /* V3 inode fields that are always zero */
+       if (dip->di_onlink)
+               return __this_address;
+       if ((flags2 & XFS_DIFLAG2_NREXT64) && dip->di_nrext64_pad)
+               return __this_address;
+       if (!(flags2 & XFS_DIFLAG2_NREXT64) && dip->di_flushiter)
+               return __this_address;
+
+       /* 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 & 0777)
+               return __this_address;
+
+       /* DMAPI event and state masks are zero */
+       if (dip->di_dmevmask || dip->di_dmstate)
+               return __this_address;
+
+       /*
+        * User and group IDs must be zero.  The project ID may be used for
+        * grouping inodes.  Metadata inodes are never accounted to quotas.
+        */
+       if (dip->di_uid || dip->di_gid)
+               return __this_address;
+
+       /* Immutable, sync, noatime, nodump, and nodefrag flags must be set */
+       if (!(flags & XFS_DIFLAG_IMMUTABLE))
+               return __this_address;
+       if (!(flags & XFS_DIFLAG_SYNC))
+               return __this_address;
+       if (!(flags & XFS_DIFLAG_NOATIME))
+               return __this_address;
+       if (!(flags & XFS_DIFLAG_NODUMP))
+               return __this_address;
+       if (!(flags & XFS_DIFLAG_NODEFRAG))
+               return __this_address;
+
+       /* Directories must have nosymlinks flags set */
+       if (S_ISDIR(mode) && !(flags & XFS_DIFLAG_NOSYMLINKS))
+               return __this_address;
+
+       /* dax flags2 must not be set */
+       if (flags2 & XFS_DIFLAG2_DAX)
+               return __this_address;
+
+       return NULL;
+}
+
 xfs_failaddr_t
 xfs_dinode_verify(
        struct xfs_mount        *mp,
@@ -654,6 +723,12 @@ xfs_dinode_verify(
            !xfs_has_bigtime(mp))
                return __this_address;
 
+       if (flags2 & XFS_DIFLAG2_METADATA) {
+               fa = xfs_dinode_verify_metadir(mp, dip, mode, flags, flags2);
+               if (fa)
+                       return fa;
+       }
+
        return NULL;
 }
 
index 585ed5a110af4e72a65783adf91639cc09686ea7..8d43d2641c73280301a6827a076f9363eafe2c01 100644 (file)
@@ -28,6 +28,9 @@ int   xfs_inode_from_disk(struct xfs_inode *ip, struct xfs_dinode *from);
 
 xfs_failaddr_t xfs_dinode_verify(struct xfs_mount *mp, xfs_ino_t ino,
                           struct xfs_dinode *dip);
+xfs_failaddr_t xfs_dinode_verify_metadir(struct xfs_mount *mp,
+               struct xfs_dinode *dip, uint16_t mode, uint16_t flags,
+               uint64_t flags2);
 xfs_failaddr_t xfs_inode_validate_extsize(struct xfs_mount *mp,
                uint32_t extsize, uint16_t mode, uint16_t flags);
 xfs_failaddr_t xfs_inode_validate_cowextsize(struct xfs_mount *mp,