xfs: enforce one namespace per attribute
authorDarrick J. Wong <djwong@kernel.org>
Mon, 22 Apr 2024 16:47:34 +0000 (09:47 -0700)
committerDarrick J. Wong <djwong@kernel.org>
Tue, 23 Apr 2024 14:46:54 +0000 (07:46 -0700)
Create a standardized helper function to enforce one namespace bit per
extended attribute, and refactor all the open-coded hweight logic.  This
function is not a static inline to avoid porting hassles in userspace.

Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
fs/xfs/libxfs/xfs_attr.c
fs/xfs/libxfs/xfs_attr.h
fs/xfs/libxfs/xfs_attr_leaf.c
fs/xfs/scrub/attr.c
fs/xfs/scrub/attr_repair.c
fs/xfs/xfs_attr_item.c
fs/xfs/xfs_attr_list.c

index ba59dab6c56db23dc65fc8ac0d8f499c0e7d31f1..629fb25d149cf8f4a635b400d1a8a70ba4711573 100644 (file)
@@ -1532,12 +1532,23 @@ out_release:
        return error;
 }
 
+/* Enforce that there is at most one namespace bit per attr. */
+inline bool xfs_attr_check_namespace(unsigned int attr_flags)
+{
+       return hweight32(attr_flags & XFS_ATTR_NSP_ONDISK_MASK) < 2;
+}
+
 /* Returns true if the attribute entry name is valid. */
 bool
 xfs_attr_namecheck(
+       unsigned int    attr_flags,
        const void      *name,
        size_t          length)
 {
+       /* Only one namespace bit allowed. */
+       if (!xfs_attr_check_namespace(attr_flags))
+               return false;
+
        /*
         * MAXNAMELEN includes the trailing null, but (name/length) leave it
         * out, so use >= for the length check.
index 79b457adb7bda0acae237ddaf30f17735825bdbb..cd106b0a424fa145ef7a704637d5cb62dd480647 100644 (file)
@@ -560,7 +560,9 @@ enum xfs_attr_update {
 int xfs_attr_set(struct xfs_da_args *args, enum xfs_attr_update op);
 int xfs_attr_set_iter(struct xfs_attr_intent *attr);
 int xfs_attr_remove_iter(struct xfs_attr_intent *attr);
-bool xfs_attr_namecheck(const void *name, size_t length);
+bool xfs_attr_check_namespace(unsigned int attr_flags);
+bool xfs_attr_namecheck(unsigned int attr_flags, const void *name,
+               size_t length);
 int xfs_attr_calc_size(struct xfs_da_args *args, int *local);
 void xfs_init_attr_trans(struct xfs_da_args *args, struct xfs_trans_res *tres,
                         unsigned int *total);
index 17ec5ff5a4e3e4023724bdafe1cc8a35ce8635fd..3b024ab892e68081f29f2b2b4bb2be23da263431 100644 (file)
@@ -950,6 +950,11 @@ xfs_attr_shortform_to_leaf(
                nargs.hashval = xfs_da_hashname(sfe->nameval,
                                                sfe->namelen);
                nargs.attr_filter = sfe->flags & XFS_ATTR_NSP_ONDISK_MASK;
+               if (!xfs_attr_check_namespace(sfe->flags)) {
+                       xfs_da_mark_sick(args);
+                       error = -EFSCORRUPTED;
+                       goto out;
+               }
                error = xfs_attr3_leaf_lookup_int(bp, &nargs); /* set a->index */
                ASSERT(error == -ENOATTR);
                error = xfs_attr3_leaf_add(bp, &nargs);
@@ -1063,7 +1068,7 @@ xfs_attr_shortform_verify(
                 * one namespace flag per xattr, so we can just count the
                 * bits (i.e. hweight) here.
                 */
-               if (hweight8(sfep->flags & XFS_ATTR_NSP_ONDISK_MASK) > 1)
+               if (!xfs_attr_check_namespace(sfep->flags))
                        return __this_address;
 
                sfep = next_sfep;
index fd22d652a63a1281505c584e09726f285fd9f0bf..7789bd2f0950743905571cdb6d85b4bc065dac89 100644 (file)
@@ -203,14 +203,8 @@ xchk_xattr_actor(
                return 0;
        }
 
-       /* Only one namespace bit allowed. */
-       if (hweight32(attr_flags & XFS_ATTR_NSP_ONDISK_MASK) > 1) {
-               xchk_fblock_set_corrupt(sc, XFS_ATTR_FORK, args.blkno);
-               return -ECANCELED;
-       }
-
        /* Does this name make sense? */
-       if (!xfs_attr_namecheck(name, namelen)) {
+       if (!xfs_attr_namecheck(attr_flags, name, namelen)) {
                xchk_fblock_set_corrupt(sc, XFS_ATTR_FORK, args.blkno);
                return -ECANCELED;
        }
@@ -519,6 +513,10 @@ xchk_xattr_rec(
                xchk_da_set_corrupt(ds, level);
                return 0;
        }
+       if (!xfs_attr_check_namespace(ent->flags)) {
+               xchk_da_set_corrupt(ds, level);
+               return 0;
+       }
 
        if (ent->flags & XFS_ATTR_LOCAL) {
                lentry = (struct xfs_attr_leaf_name_local *)
index 3066d662ea13f30c5828fefae84feb346d09a63a..8b89c112c492fa77ac195f8881b665bcfae8b0ee 100644 (file)
@@ -123,12 +123,10 @@ xrep_xattr_want_salvage(
                return false;
        if (namelen > XATTR_NAME_MAX || namelen <= 0)
                return false;
-       if (!xfs_attr_namecheck(name, namelen))
+       if (!xfs_attr_namecheck(attr_flags, name, namelen))
                return false;
        if (valuelen > XATTR_SIZE_MAX || valuelen < 0)
                return false;
-       if (hweight32(attr_flags & XFS_ATTR_NSP_ONDISK_MASK) > 1)
-               return false;
        return true;
 }
 
index 39536303a7b64a2d6f40108be9824897ecf1f32f..a65ac747976800a2457b5a50d6ed0bc641abe799 100644 (file)
@@ -492,6 +492,10 @@ xfs_attri_validate(
        if (attrp->alfi_attr_filter & ~XFS_ATTRI_FILTER_MASK)
                return false;
 
+       if (!xfs_attr_check_namespace(attrp->alfi_attr_filter &
+                                     XFS_ATTR_NSP_ONDISK_MASK))
+               return false;
+
        switch (op) {
        case XFS_ATTRI_OP_FLAGS_SET:
        case XFS_ATTRI_OP_FLAGS_REPLACE:
@@ -633,7 +637,8 @@ xfs_attr_recover_work(
         */
        attrp = &attrip->attri_format;
        if (!xfs_attri_validate(mp, attrp) ||
-           !xfs_attr_namecheck(nv->name.i_addr, nv->name.i_len))
+           !xfs_attr_namecheck(attrp->alfi_attr_filter, nv->name.i_addr,
+                               nv->name.i_len))
                return -EFSCORRUPTED;
 
        attr = xfs_attri_recover_work(mp, dfp, attrp, &ip, nv);
@@ -747,7 +752,8 @@ xfs_attri_validate_name_iovec(
                return NULL;
        }
 
-       if (!xfs_attr_namecheck(iovec->i_addr, name_len)) {
+       if (!xfs_attr_namecheck(attri_formatp->alfi_attr_filter, iovec->i_addr,
+                               name_len)) {
                XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp,
                                attri_formatp, sizeof(*attri_formatp));
                XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp,
index 97c8f3dcfb89dcc1c1eeaa940e34b597b90514d8..903ed46c68872bcb1bed6b41c394c0c2086a9ebd 100644 (file)
@@ -82,7 +82,8 @@ xfs_attr_shortform_list(
             (dp->i_af.if_bytes + sf->count * 16) < context->bufsize)) {
                for (i = 0, sfe = xfs_attr_sf_firstentry(sf); i < sf->count; i++) {
                        if (XFS_IS_CORRUPT(context->dp->i_mount,
-                                          !xfs_attr_namecheck(sfe->nameval,
+                                          !xfs_attr_namecheck(sfe->flags,
+                                                              sfe->nameval,
                                                               sfe->namelen))) {
                                xfs_dirattr_mark_sick(context->dp, XFS_ATTR_FORK);
                                return -EFSCORRUPTED;
@@ -122,7 +123,8 @@ xfs_attr_shortform_list(
        for (i = 0, sfe = xfs_attr_sf_firstentry(sf); i < sf->count; i++) {
                if (unlikely(
                    ((char *)sfe < (char *)sf) ||
-                   ((char *)sfe >= ((char *)sf + dp->i_af.if_bytes)))) {
+                   ((char *)sfe >= ((char *)sf + dp->i_af.if_bytes)) ||
+                   !xfs_attr_check_namespace(sfe->flags))) {
                        XFS_CORRUPTION_ERROR("xfs_attr_shortform_list",
                                             XFS_ERRLEVEL_LOW,
                                             context->dp->i_mount, sfe,
@@ -177,7 +179,7 @@ xfs_attr_shortform_list(
                        cursor->offset = 0;
                }
                if (XFS_IS_CORRUPT(context->dp->i_mount,
-                                  !xfs_attr_namecheck(sbp->name,
+                                  !xfs_attr_namecheck(sbp->flags, sbp->name,
                                                       sbp->namelen))) {
                        xfs_dirattr_mark_sick(context->dp, XFS_ATTR_FORK);
                        error = -EFSCORRUPTED;
@@ -502,7 +504,8 @@ xfs_attr3_leaf_list_int(
                }
 
                if (XFS_IS_CORRUPT(context->dp->i_mount,
-                                  !xfs_attr_namecheck(name, namelen))) {
+                                  !xfs_attr_namecheck(entry->flags, name,
+                                                      namelen))) {
                        xfs_dirattr_mark_sick(context->dp, XFS_ATTR_FORK);
                        return -EFSCORRUPTED;
                }