Re: [PATCH 2/3] xattrat: accept empty O_PATH file descriptors
From: Christian Brauner
Date: Tue Jun 30 2026 - 08:33:09 EST
On 2026-06-30 14:06 +0200, Andreas Gruenbacher wrote:
> On Tue, Jun 30, 2026 at 11:12 AM Christian Brauner <brauner@xxxxxxxxxx> wrote:
> > > Right now, the setxattrat(), getxattrat(), listxattrat(), and removexattrat()
> > > system calls fail with -EBADF when dfd is an O_PATH file descriptor, pathname
> > > is an empty string or NULL, and the AT_EMPTY_PATH flag in at_flags is set.
> > > (Regular non-O_PATH file descriptors are accepted.) This is inconsistent with
> > > the behavior of system calls like fstatat() and fchmodat() which do accept
> > > those file descriptors.
> > >
> > > The same operations can also be carried out indirectly by using
> > > "/proc/self/fd/<dfd>" as the pathname, but that is only more cumbersome, and
> > > for no good reason. Fix that by changing the xattrat() system calls to accept
> > > these file descriptors directly.
> > >
> > > We stick with the existing practice of leaving the behavior of the fsetxattr(),
> > > fgetxattr(), flistxattr(), and fremovexattr() system calls unchanged: those
> > > will still reject O_PATH file descriptors.
> > >
> > > Signed-off-by: Andreas Gruenbacher <agruenba@xxxxxxxxxx>
> > >
> > > diff --git a/fs/xattr.c b/fs/xattr.c
> > > index d58979115200..4737554df5fa 100644
> > > --- a/fs/xattr.c
> > > +++ b/fs/xattr.c
> > > @@ -700,7 +700,8 @@ int filename_setxattr(int dfd, struct filename *filename,
> > >
> > > static int path_setxattrat(int dfd, const char __user *pathname,
> > > unsigned int at_flags, const char __user *name,
> > > - const void __user *value, size_t size, int flags)
> > > + const void __user *value, size_t size, int flags,
> > > + fmode_t mask)
> > > {
> > > struct xattr_name kname;
> > > struct kernel_xattr_ctx ctx = {
> > > @@ -725,7 +726,7 @@ static int path_setxattrat(int dfd, const char __user *pathname,
> > >
> > > CLASS(filename_maybe_null, filename)(pathname, at_flags);
> > > if (!filename && dfd >= 0) {
> > > - CLASS(fd, f)(dfd);
> > > + CLASS(fd_except, f)(dfd, mask);
> >
> > So I can live with the O_PATH change, I guess...
> > but I really dislike fd_except. The code should never bother to pass
> > FMODE_PATH directly which is just rather ugly and we've taken pain to
> > make it work without passing it.
> >
> > I also don't yet understand why this would be necessary at all.
>
> So we've got some system calls that should accept O_PATH file descriptors
> ({get,set,list,remove}xattrat), and others that should not
> (f{get,set,list,remove}xattr).
I'm very confused why the f*at() variants are supposed to accept O_PATH
but the f*() aren't? What's the point? glibc has in multiple cases used
the f*at() variant to implement the f*() variant.