Commit ea0b3e81 authored by Darrick J. Wong's avatar Darrick J. Wong

xfs: enforce one namespace per attribute

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: default avatarDarrick J. Wong <djwong@kernel.org>
Reviewed-by: default avatarChristoph Hellwig <hch@lst.de>
parent ffdcc3b8
...@@ -1532,12 +1532,23 @@ xfs_attr_node_get( ...@@ -1532,12 +1532,23 @@ xfs_attr_node_get(
return error; 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. */ /* Returns true if the attribute entry name is valid. */
bool bool
xfs_attr_namecheck( xfs_attr_namecheck(
unsigned int attr_flags,
const void *name, const void *name,
size_t length) 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 * MAXNAMELEN includes the trailing null, but (name/length) leave it
* out, so use >= for the length check. * out, so use >= for the length check.
......
...@@ -560,7 +560,9 @@ enum xfs_attr_update { ...@@ -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(struct xfs_da_args *args, enum xfs_attr_update op);
int xfs_attr_set_iter(struct xfs_attr_intent *attr); int xfs_attr_set_iter(struct xfs_attr_intent *attr);
int xfs_attr_remove_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); 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, void xfs_init_attr_trans(struct xfs_da_args *args, struct xfs_trans_res *tres,
unsigned int *total); unsigned int *total);
......
...@@ -950,6 +950,11 @@ xfs_attr_shortform_to_leaf( ...@@ -950,6 +950,11 @@ xfs_attr_shortform_to_leaf(
nargs.hashval = xfs_da_hashname(sfe->nameval, nargs.hashval = xfs_da_hashname(sfe->nameval,
sfe->namelen); sfe->namelen);
nargs.attr_filter = sfe->flags & XFS_ATTR_NSP_ONDISK_MASK; 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 */ error = xfs_attr3_leaf_lookup_int(bp, &nargs); /* set a->index */
ASSERT(error == -ENOATTR); ASSERT(error == -ENOATTR);
error = xfs_attr3_leaf_add(bp, &nargs); error = xfs_attr3_leaf_add(bp, &nargs);
...@@ -1063,7 +1068,7 @@ xfs_attr_shortform_verify( ...@@ -1063,7 +1068,7 @@ xfs_attr_shortform_verify(
* one namespace flag per xattr, so we can just count the * one namespace flag per xattr, so we can just count the
* bits (i.e. hweight) here. * 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; return __this_address;
sfep = next_sfep; sfep = next_sfep;
......
...@@ -203,14 +203,8 @@ xchk_xattr_actor( ...@@ -203,14 +203,8 @@ xchk_xattr_actor(
return 0; 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? */ /* 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); xchk_fblock_set_corrupt(sc, XFS_ATTR_FORK, args.blkno);
return -ECANCELED; return -ECANCELED;
} }
...@@ -519,6 +513,10 @@ xchk_xattr_rec( ...@@ -519,6 +513,10 @@ xchk_xattr_rec(
xchk_da_set_corrupt(ds, level); xchk_da_set_corrupt(ds, level);
return 0; return 0;
} }
if (!xfs_attr_check_namespace(ent->flags)) {
xchk_da_set_corrupt(ds, level);
return 0;
}
if (ent->flags & XFS_ATTR_LOCAL) { if (ent->flags & XFS_ATTR_LOCAL) {
lentry = (struct xfs_attr_leaf_name_local *) lentry = (struct xfs_attr_leaf_name_local *)
......
...@@ -123,12 +123,10 @@ xrep_xattr_want_salvage( ...@@ -123,12 +123,10 @@ xrep_xattr_want_salvage(
return false; return false;
if (namelen > XATTR_NAME_MAX || namelen <= 0) if (namelen > XATTR_NAME_MAX || namelen <= 0)
return false; return false;
if (!xfs_attr_namecheck(name, namelen)) if (!xfs_attr_namecheck(attr_flags, name, namelen))
return false; return false;
if (valuelen > XATTR_SIZE_MAX || valuelen < 0) if (valuelen > XATTR_SIZE_MAX || valuelen < 0)
return false; return false;
if (hweight32(attr_flags & XFS_ATTR_NSP_ONDISK_MASK) > 1)
return false;
return true; return true;
} }
......
...@@ -492,6 +492,10 @@ xfs_attri_validate( ...@@ -492,6 +492,10 @@ xfs_attri_validate(
if (attrp->alfi_attr_filter & ~XFS_ATTRI_FILTER_MASK) if (attrp->alfi_attr_filter & ~XFS_ATTRI_FILTER_MASK)
return false; return false;
if (!xfs_attr_check_namespace(attrp->alfi_attr_filter &
XFS_ATTR_NSP_ONDISK_MASK))
return false;
switch (op) { switch (op) {
case XFS_ATTRI_OP_FLAGS_SET: case XFS_ATTRI_OP_FLAGS_SET:
case XFS_ATTRI_OP_FLAGS_REPLACE: case XFS_ATTRI_OP_FLAGS_REPLACE:
...@@ -633,7 +637,8 @@ xfs_attr_recover_work( ...@@ -633,7 +637,8 @@ xfs_attr_recover_work(
*/ */
attrp = &attrip->attri_format; attrp = &attrip->attri_format;
if (!xfs_attri_validate(mp, attrp) || 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; return -EFSCORRUPTED;
attr = xfs_attri_recover_work(mp, dfp, attrp, &ip, nv); attr = xfs_attri_recover_work(mp, dfp, attrp, &ip, nv);
...@@ -747,7 +752,8 @@ xfs_attri_validate_name_iovec( ...@@ -747,7 +752,8 @@ xfs_attri_validate_name_iovec(
return NULL; 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, XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp,
attri_formatp, sizeof(*attri_formatp)); attri_formatp, sizeof(*attri_formatp));
XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp,
......
...@@ -82,7 +82,8 @@ xfs_attr_shortform_list( ...@@ -82,7 +82,8 @@ xfs_attr_shortform_list(
(dp->i_af.if_bytes + sf->count * 16) < context->bufsize)) { (dp->i_af.if_bytes + sf->count * 16) < context->bufsize)) {
for (i = 0, sfe = xfs_attr_sf_firstentry(sf); i < sf->count; i++) { for (i = 0, sfe = xfs_attr_sf_firstentry(sf); i < sf->count; i++) {
if (XFS_IS_CORRUPT(context->dp->i_mount, if (XFS_IS_CORRUPT(context->dp->i_mount,
!xfs_attr_namecheck(sfe->nameval, !xfs_attr_namecheck(sfe->flags,
sfe->nameval,
sfe->namelen))) { sfe->namelen))) {
xfs_dirattr_mark_sick(context->dp, XFS_ATTR_FORK); xfs_dirattr_mark_sick(context->dp, XFS_ATTR_FORK);
return -EFSCORRUPTED; return -EFSCORRUPTED;
...@@ -122,7 +123,8 @@ xfs_attr_shortform_list( ...@@ -122,7 +123,8 @@ xfs_attr_shortform_list(
for (i = 0, sfe = xfs_attr_sf_firstentry(sf); i < sf->count; i++) { for (i = 0, sfe = xfs_attr_sf_firstentry(sf); i < sf->count; i++) {
if (unlikely( if (unlikely(
((char *)sfe < (char *)sf) || ((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_CORRUPTION_ERROR("xfs_attr_shortform_list",
XFS_ERRLEVEL_LOW, XFS_ERRLEVEL_LOW,
context->dp->i_mount, sfe, context->dp->i_mount, sfe,
...@@ -177,7 +179,7 @@ xfs_attr_shortform_list( ...@@ -177,7 +179,7 @@ xfs_attr_shortform_list(
cursor->offset = 0; cursor->offset = 0;
} }
if (XFS_IS_CORRUPT(context->dp->i_mount, if (XFS_IS_CORRUPT(context->dp->i_mount,
!xfs_attr_namecheck(sbp->name, !xfs_attr_namecheck(sbp->flags, sbp->name,
sbp->namelen))) { sbp->namelen))) {
xfs_dirattr_mark_sick(context->dp, XFS_ATTR_FORK); xfs_dirattr_mark_sick(context->dp, XFS_ATTR_FORK);
error = -EFSCORRUPTED; error = -EFSCORRUPTED;
...@@ -502,7 +504,8 @@ xfs_attr3_leaf_list_int( ...@@ -502,7 +504,8 @@ xfs_attr3_leaf_list_int(
} }
if (XFS_IS_CORRUPT(context->dp->i_mount, 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); xfs_dirattr_mark_sick(context->dp, XFS_ATTR_FORK);
return -EFSCORRUPTED; return -EFSCORRUPTED;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment