From: Darrick J. Wong <djwong@xxxxxxxxxx> Source kernel commit: ea0b3e814741fb64e7785b564ea619578058e0b0 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@xxxxxxxxxx> Reviewed-by: Christoph Hellwig <hch@xxxxxx> --- libxfs/xfs_attr.c | 11 +++++++++++ libxfs/xfs_attr.h | 4 +++- libxfs/xfs_attr_leaf.c | 7 ++++++- repair/attr_repair.c | 7 ++++--- 4 files changed, 24 insertions(+), 5 deletions(-) diff --git a/libxfs/xfs_attr.c b/libxfs/xfs_attr.c index 26674116f..e0a3bc702 100644 --- a/libxfs/xfs_attr.c +++ b/libxfs/xfs_attr.c @@ -1530,12 +1530,23 @@ xfs_attr_node_get( 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. diff --git a/libxfs/xfs_attr.h b/libxfs/xfs_attr.h index 79b457adb..cd106b0a4 100644 --- a/libxfs/xfs_attr.h +++ b/libxfs/xfs_attr.h @@ -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); diff --git a/libxfs/xfs_attr_leaf.c b/libxfs/xfs_attr_leaf.c index 47f2836fb..346f0127d 100644 --- a/libxfs/xfs_attr_leaf.c +++ b/libxfs/xfs_attr_leaf.c @@ -947,6 +947,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); @@ -1060,7 +1065,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; diff --git a/repair/attr_repair.c b/repair/attr_repair.c index 206a97d66..0f2f7a284 100644 --- a/repair/attr_repair.c +++ b/repair/attr_repair.c @@ -292,7 +292,8 @@ process_shortform_attr( } /* namecheck checks for null chars in attr names. */ - if (!libxfs_attr_namecheck(currententry->nameval, + if (!libxfs_attr_namecheck(currententry->flags, + currententry->nameval, currententry->namelen)) { do_warn( _("entry contains illegal character in shortform attribute name\n")); @@ -473,7 +474,7 @@ process_leaf_attr_local( local = xfs_attr3_leaf_name_local(leaf, i); if (local->namelen == 0 || - !libxfs_attr_namecheck(local->nameval, + !libxfs_attr_namecheck(entry->flags, local->nameval, local->namelen)) { do_warn( _("attribute entry %d in attr block %u, inode %" PRIu64 " has bad name (namelen = %d)\n"), @@ -529,7 +530,7 @@ process_leaf_attr_remote( remotep = xfs_attr3_leaf_name_remote(leaf, i); if (remotep->namelen == 0 || - !libxfs_attr_namecheck(remotep->name, + !libxfs_attr_namecheck(entry->flags, remotep->name, remotep->namelen) || be32_to_cpu(entry->hashval) != libxfs_da_hashname((unsigned char *)&remotep->name[0],