Re: [PATCH v3 23/26] xfs: Filter XFS_ATTR_PARENT for getfattr

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Mon, Sep 26, 2022 at 09:49:42PM +0000, Allison Henderson wrote:
> On Fri, 2022-09-23 at 14:45 -0700, Darrick J. Wong wrote:
> > On Wed, Sep 21, 2022 at 10:44:55PM -0700,
> > allison.henderson@xxxxxxxxxx wrote:
> > > From: Allison Henderson <allison.henderson@xxxxxxxxxx>
> > > 
> > > Parent pointers returned to the get_fattr tool cause errors since
> > > the tool cannot parse parent pointers.  Fix this by filtering
> > > parent
> > > parent pointers from xfs_attr_list.
> > 
> > Yes!!  Parent pointers should /never/ be accessible by the standard
> > VFS
> > xattr syscalls, nor should the XFS ATTR_MULTI calls handle them.
> > 
> > Changes to parent pointers are performed via separate syscalls
> > (link/unlink/mknod/creat/etc), and I see you've created a separate
> > parent pointer ioctl later on for userspace to retrieve them.  I
> > think
> > this is the correct access model.
> > 
> > To check that assertion -- getxattr/setxattr/removexattr (and the
> > ATTRMULTI
> > equivalents) are prevented from accessing parent pointers directly
> > because you'd have to be able to set XFS_ATTR_PARENT in
> > xfs_da_args.attr_filter, right?
> Well, you set XFS_IOC_ATTR_PARENT which later translates to
> XFS_ATTR_PARENT.  I do that later in the set, but I see you've already
> commented on it

<nod>

> > 
> > And for the VFS to get/set/remove a parent pointer, XFS would have to
> > provide a struct xattr_handler with ->flags = XFS_ATTR_PARENT, which
> > XFS
> > will never do, right?
> Well, this set does not plan to make any vfs modifications at least :-)
> 
> > 
> > And for ATTR_MULTI to touch a parent pointer, xfs_attr_filter (and
> > the
> > ioctl api) would have to learn about XFS_ATTR_PARENT, which XFS will
> > also never do, right?
> I think we do have to publish that XFS_ATTR_PARENT exists, but I don't
> think we have to implement the XFS_IOC_ATTR_PARENT translation

Agreed.  The fewer symbols that end up in xfs_fs.h the better, because
anything we publish in there becomes a part of the userspace ABI and has
to be supported "forever".

> > If the answers to these three questions are all yes then you're 95%
> > of
> > the way to an RVB, except...
> > 
> > > Signed-off-by: Allison Henderson <allison.henderson@xxxxxxxxxx>
> > > ---
> > >  fs/xfs/libxfs/xfs_da_format.h |  3 +++
> > >  fs/xfs/xfs_attr_list.c        | 47 +++++++++++++++++++++++++++----
> > > ----
> > >  2 files changed, 39 insertions(+), 11 deletions(-)
> > > 
> > > diff --git a/fs/xfs/libxfs/xfs_da_format.h
> > > b/fs/xfs/libxfs/xfs_da_format.h
> > > index b02b67f1999e..e9c323fab6f3 100644
> > > --- a/fs/xfs/libxfs/xfs_da_format.h
> > > +++ b/fs/xfs/libxfs/xfs_da_format.h
> > > @@ -697,6 +697,9 @@ struct xfs_attr3_leafblock {
> > >  #define XFS_ATTR_INCOMPLETE    (1u << XFS_ATTR_INCOMPLETE_BIT)
> > >  #define XFS_ATTR_NSP_ONDISK_MASK \
> > >                         (XFS_ATTR_ROOT | XFS_ATTR_SECURE |
> > > XFS_ATTR_PARENT)
> > > +#define XFS_ATTR_ALL \
> > > +       (XFS_ATTR_LOCAL_BIT | XFS_ATTR_ROOT | XFS_ATTR_SECURE | \
> > > +        XFS_ATTR_PARENT | XFS_ATTR_INCOMPLETE_BIT)
> > >  
> > >  /*
> > >   * Alignment for namelist and valuelist entries (since they are
> > > mixed
> > > diff --git a/fs/xfs/xfs_attr_list.c b/fs/xfs/xfs_attr_list.c
> > > index a51f7f13a352..13de597c4996 100644
> > > --- a/fs/xfs/xfs_attr_list.c
> > > +++ b/fs/xfs/xfs_attr_list.c
> > > @@ -39,6 +39,23 @@ xfs_attr_shortform_compare(const void *a, const
> > > void *b)
> > >         }
> > >  }
> > >  
> > > +/*
> > > + * Returns true or false if the parent attribute should be listed
> > > + */
> > > +static bool
> > > +xfs_attr_filter_parent(
> > > +       struct xfs_attr_list_context    *context,
> > > +       int                             flags)
> > > +{
> > > +       if (!(flags & XFS_ATTR_PARENT))
> > > +               return true;
> > > +
> > > +       if (context->attr_filter & XFS_ATTR_PARENT)
> > > +               return true;
> > > +
> > > +       return false;
> > 
> > ...wouldn't it suffice to do:
> > 
> > static inline bool
> > xfs_attr_filter_listent(
> >         struct xfs_attr_list_context    *context,
> >         int                             flags)
> > {
> >         return context->attr_filter != (flags &
> > XFS_ATTR_NSP_ONDISK_MASK);
> > }
> Almost.... XFS_ATTR_NSP_ONDISK_MASK includes XFS_ATTR_PARENT, so here
> we'd want XFS_ATTR_NSP_ONDISK_MASK & ~XFS_ATTR_PARENT
> 
> Also, XFS_ATTR_NSP_ONDISK_MASK doesnt include XFS_ATTR_LOCAL which was
> previously allowable
> 
> Hmm, how bout just:
> 	return context->attr_filter != flags & (XFS_ATTR_ROOT |
> XFS_ATTR_SECURE | XFS_ATTR_LOCAL)
> 
> I think that preserves the existing behavior

It does, but I think it'll cause conflicts with future online fsck code.

(Let me check my understanding of the xfs_attr_list callers first:

There are /three/ namespaces, but only two of them have defined flag
bits.  "root.*" and "secure.*" have XFS_ATTR_{ROOT,SECURE}, but "user.*"
is recorded by the absence of any XFS_ATTR_ flag.  IOWs, attr_filter
isn't a bit mask of all the namespaces that the caller wants; it's a
cookie value to coordinate between the xfs_attr_list caller and the
supplied putent function.

vfs_listxattr doesn't allow userspace to specify an xattr namespace, so
it returns xattrs from user.*, root.*, and secure.*.  xfs_vn_listxattr
doesn't set attr_filter, and xfs_xattr_put_listent doesn't check.

XFS_IOC_ATTRLIST_BY_HANDLE, on the other hand, requires userspace to
specify which namespace they want to list.  That's why it's the one
caller that sets context.attr_filter, and xfs_ioc_attr_put_listent does
a comparison to filter attributes from other namespaces.

The xattr scrubber calls xfs_attr_list_ilocked to walk every xattr
attached to the file.  For each call to ->put_listent, it uses the
built-in hash information to look up the attr name provided to confirm
that hashed lookups also work.  For /this/ usecase, we want
xfs_attr_list_* to return all xattrs because we need to check all xattr
namespaces.  Hence it doesn't set context.attr_filter, and
xchk_xattr_listent doesn't check it either.

...right?)

((Come to think of it -- shouldn't the ifork and attr leaf block
verifiers check that each attr entry struct have at most one of the
namespace bits set?))

After the addition of parent pointers, xfs_xattr_put_listent needs to
filter out (flags & XFS_ATTR_PARENT) cases.  XFS_IOC_ATTRLIST_BY_HANDLE
won't need any changes if we drop the XFS_IOC_ATTR_PARENT flag, since
there won't be any way for XFS_ATTR_PARENT to be set in attr_filter.
The xattr scrubber still needs to walk every xattr to perform a complete
scan.  Hence it will still not touch context.attr_filter, nor will it
want any filtering to be applied at all.

For the forthcoming totally-doesn't-exist-yet parent pointer online fsck
code, I'd sorta like to reuse that function to walk only the parent
pointers.  I'm not 100% sure I will end up doing that -- the cursoring
code would be useful if I have to use a live inode scan to do the
checking and repair, but OTOH it's a little annoying to deal with the
warts of the xfs_attr_list_context.  But in theory, I'd have
xchk_parent_listent or whatever the function ends up being named ignore
anything that wasn't XFS_ATTR_PARENT.  This mechanism also does not want
the xfs_attr_list_* functions to perform any filtering.

In terms of APIs, xfs_attr_filter_parent doesn't really make a lot of
sense -- if you set context.attr_filter = XFS_ATTR_PARENT, you'll get
all the xattrs (including the non-parent ones), but if you set any other
value (e.g. XFS_ATTR_SECURE), you'll get all the non-parent xattrs,
including the root.* and user.* attrs.  The ->put_listent function still
has to do its own filtering.  Right?

What if xfs_xattr_put_listent exited early if (flags & XFS_ATTR_PARENT)
and we never define a XFS_IOC_ATTR_ROOT?  Won't that suffice to prevent
the userspace xattr APIs from ever returning a parent pointer by
accident?

Looking ahead to the GETPARENTS code, I think the filtering code in
xfs_ioc_attr_put_listent would work for returning only the parent
pointers, right?  Since it does set context.attr_filter to
XFS_ATTR_PARENT on its own?  And you wouldn't need any of the other
changes here?

--D

> > 
> > like how xfs_ioc_attr_put_listent does?  And then...
> > 
> > > +}
> > > +
> > >  #define XFS_ISRESET_CURSOR(cursor) \
> > >         (!((cursor)->initted) && !((cursor)->hashval) && \
> > >          !((cursor)->blkno) && !((cursor)->offset))
> > > @@ -90,11 +107,12 @@ xfs_attr_shortform_list(
> > >                                                                sfe-
> > > >namelen,
> > >                                                                sfe-
> > > >flags)))
> > >                                 return -EFSCORRUPTED;
> > > -                       context->put_listent(context,
> > > -                                            sfe->flags,
> > > -                                            sfe->nameval,
> > > -                                            (int)sfe->namelen,
> > > -                                            (int)sfe->valuelen);
> > > +                       if (xfs_attr_filter_parent(context, sfe-
> > > >flags))
> > > +                               context->put_listent(context,
> > > +                                                    sfe->flags,
> > > +                                                    sfe->nameval,
> > > +                                                    (int)sfe-
> > > >namelen,
> > > +                                                    (int)sfe-
> > > >valuelen);
> > >                         /*
> > >                          * Either search callback finished early or
> > >                          * didn't fit it all in the buffer after
> > > all.
> > > @@ -185,11 +203,12 @@ xfs_attr_shortform_list(
> > >                         error = -EFSCORRUPTED;
> > >                         goto out;
> > >                 }
> > > -               context->put_listent(context,
> > > -                                    sbp->flags,
> > > -                                    sbp->name,
> > > -                                    sbp->namelen,
> > > -                                    sbp->valuelen);
> > > +               if (xfs_attr_filter_parent(context, sbp->flags))
> > > +                       context->put_listent(context,
> > > +                                            sbp->flags,
> > > +                                            sbp->name,
> > > +                                            sbp->namelen,
> > > +                                            sbp->valuelen);
> > >                 if (context->seen_enough)
> > >                         break;
> > >                 cursor->offset++;
> > > @@ -474,8 +493,10 @@ xfs_attr3_leaf_list_int(
> > >                                    !xfs_attr_namecheck(mp, name,
> > > namelen,
> > >                                                        entry-
> > > >flags)))
> > >                         return -EFSCORRUPTED;
> > > -               context->put_listent(context, entry->flags,
> > > +               if (xfs_attr_filter_parent(context, entry->flags))
> > > +                       context->put_listent(context, entry->flags,
> > >                                               name, namelen,
> > > valuelen);
> > > +
> > >                 if (context->seen_enough)
> > >                         break;
> > >                 cursor->offset++;
> > > @@ -539,6 +560,10 @@ xfs_attr_list(
> > >         if (xfs_is_shutdown(dp->i_mount))
> > >                 return -EIO;
> > >  
> > > +       if (context->attr_filter == 0)
> > > +               context->attr_filter =
> > > +                       XFS_ATTR_ALL & ~XFS_ATTR_PARENT;
> > 
> > ...I think this is unnecessary since none of the callers can actually
> > set XFS_ATTR_PARENT in the first place, right?
> > 
> The caller can stuff any bit in there they want, but until now it
> didn't matter because context->attr_filter was unused (getfattr appears
> to just leave it as 0).  So the existing behavior was that requesting
> nothing resulted in everything.  Now we're flipping that (at least
> internally).
> 
> If we use the filter logic above, this would need to turn into
> context->attr_filter = (XFS_ATTR_ROOT | XFS_ATTR_SECURE |
> XFS_ATTR_LOCAL)
> 
> > --D
> > 
> > > +
> > >         lock_mode = xfs_ilock_attr_map_shared(dp);
> > >         error = xfs_attr_list_ilocked(context);
> > >         xfs_iunlock(dp, lock_mode);
> > > -- 
> > > 2.25.1
> > > 
> 



[Index of Archives]     [XFS Filesystem Development (older mail)]     [Linux Filesystem Development]     [Linux Audio Users]     [Yosemite Trails]     [Linux Kernel]     [Linux RAID]     [Linux SCSI]


  Powered by Linux