Re: [PATCH v2] xattrat: accept empty O_PATH file descriptors
Andreas Gruenbacher <[email protected]>
| Newsgroups | org.kernel.vger.linux-fsdevel,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <CAHc6FU4719z5crxP-ahSXs2qLachSL9GOiv4kXRZRifNxm-U_A@mail.gmail.com> |
On Thu, Jul 30, 2026 at 8:49 PM Christian Brauner <[email protected]> wrote: > On 2026-07-22 17:10 +0200, Andreas Gruenbacher wrote: > > Christian, > > > > here's an updated patch for making the *xattrat() system calls accept > > O_PATH file descriptors. > > > > Compared to the previous version (see the discussion at [*]), this > > version adds a fd_maybe_raw cleanup class that uses either fdget() or > > fdget_raw() depending on which kinds of file descriptors are acceptable. > > That's still a but ugly, but all the alternatives I could come up with > > are much worse. > > > > [*] https://lore.kernel.org/linux-fsdevel/[email protected]/ > > Thank you. How do you feel about? I don't think it's an improvement; see below. > --- > fs/xattr.c | 100 ++++++++++++++++++++++++++++++++++------------------- > 1 file changed, 64 insertions(+), 36 deletions(-) > > diff --git a/fs/xattr.c b/fs/xattr.c > index d58979115200..133bca3371ee 100644 > --- a/fs/xattr.c > +++ b/fs/xattr.c > @@ -698,9 +698,10 @@ int filename_setxattr(int dfd, struct filename *filename, > return error; > } > > -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) > +static int path_setxattrat(int dfd, struct file *dfd_file, > + const char __user *pathname, unsigned int at_flags, > + const char __user *name, const void __user *value, > + size_t size, int flags) > { > struct xattr_name kname; > struct kernel_xattr_ctx ctx = { > @@ -725,11 +726,10 @@ 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); > - if (fd_empty(f)) > + if (!dfd_file) > error = -EBADF; I don't particularly like that the initialization and the error checking are being split up here. The result is the same, though. > else > - error = file_setxattr(fd_file(f), &ctx); > + error = file_setxattr(dfd_file, &ctx); > } else { > error = filename_setxattr(dfd, filename, lookup_flags, &ctx); > } > @@ -756,7 +756,10 @@ SYSCALL_DEFINE6(setxattrat, int, dfd, const char __user *, pathname, unsigned in > if (error) > return error; > > - return path_setxattrat(dfd, pathname, at_flags, name, > + /* setxattrat() accepts O_PATH file descriptors. */ > + CLASS(fd_raw, f)(dfd); On this path, fdget_raw(dfd) will now be called unnecessarily when pathname is not NULL. > + > + return path_setxattrat(dfd, fd_file(f), pathname, at_flags, name, > u64_to_user_ptr(args.value), args.size, > args.flags); > } > @@ -765,21 +768,25 @@ SYSCALL_DEFINE5(setxattr, const char __user *, pathname, > const char __user *, name, const void __user *, value, > size_t, size, int, flags) > { > - return path_setxattrat(AT_FDCWD, pathname, 0, name, value, size, flags); > + return path_setxattrat(AT_FDCWD, NULL, pathname, 0, name, value, size, > + flags); > } > > SYSCALL_DEFINE5(lsetxattr, const char __user *, pathname, > const char __user *, name, const void __user *, value, > size_t, size, int, flags) > { > - return path_setxattrat(AT_FDCWD, pathname, AT_SYMLINK_NOFOLLOW, name, > - value, size, flags); > + return path_setxattrat(AT_FDCWD, NULL, pathname, AT_SYMLINK_NOFOLLOW, > + name, value, size, flags); > } > > SYSCALL_DEFINE5(fsetxattr, int, fd, const char __user *, name, > const void __user *,value, size_t, size, int, flags) > { > - return path_setxattrat(fd, NULL, AT_EMPTY_PATH, name, > + /* fsetxattr() rejects O_PATH file descriptors. */ > + CLASS(fd, f)(fd); > + > + return path_setxattrat(fd, fd_file(f), NULL, AT_EMPTY_PATH, name, > value, size, flags); > } > > @@ -843,7 +850,8 @@ ssize_t filename_getxattr(int dfd, struct filename *filename, > return error; > } > > -static ssize_t path_getxattrat(int dfd, const char __user *pathname, > +static ssize_t path_getxattrat(int dfd, struct file *dfd_file, > + const char __user *pathname, > unsigned int at_flags, const char __user *name, > void __user *value, size_t size) > { > @@ -865,10 +873,9 @@ static ssize_t path_getxattrat(int dfd, const char __user *pathname, > > CLASS(filename_maybe_null, filename)(pathname, at_flags); > if (!filename && dfd >= 0) { > - CLASS(fd, f)(dfd); > - if (fd_empty(f)) > + if (!dfd_file) > return -EBADF; > - return file_getxattr(fd_file(f), &ctx); > + return file_getxattr(dfd_file, &ctx); > } else { > int lookup_flags = 0; > if (!(at_flags & AT_SYMLINK_NOFOLLOW)) > @@ -898,27 +905,34 @@ SYSCALL_DEFINE6(getxattrat, int, dfd, const char __user *, pathname, unsigned in > if (args.flags != 0) > return -EINVAL; > > - return path_getxattrat(dfd, pathname, at_flags, name, > + /* getxattrat() accepts O_PATH file descriptors. */ > + CLASS(fd_raw, f)(dfd); Same here. > + > + return path_getxattrat(dfd, fd_file(f), pathname, at_flags, name, > u64_to_user_ptr(args.value), args.size); > } > > SYSCALL_DEFINE4(getxattr, const char __user *, pathname, > const char __user *, name, void __user *, value, size_t, size) > { > - return path_getxattrat(AT_FDCWD, pathname, 0, name, value, size); > + return path_getxattrat(AT_FDCWD, NULL, pathname, 0, name, value, size); > } > > SYSCALL_DEFINE4(lgetxattr, const char __user *, pathname, > const char __user *, name, void __user *, value, size_t, size) > { > - return path_getxattrat(AT_FDCWD, pathname, AT_SYMLINK_NOFOLLOW, name, > - value, size); > + return path_getxattrat(AT_FDCWD, NULL, pathname, AT_SYMLINK_NOFOLLOW, > + name, value, size); > } > > SYSCALL_DEFINE4(fgetxattr, int, fd, const char __user *, name, > void __user *, value, size_t, size) > { > - return path_getxattrat(fd, NULL, AT_EMPTY_PATH, name, value, size); > + /* fgetxattr() rejects O_PATH file descriptors. */ > + CLASS(fd, f)(fd); > + > + return path_getxattrat(fd, fd_file(f), NULL, AT_EMPTY_PATH, name, value, > + size); > } > > /* > @@ -980,7 +994,8 @@ ssize_t filename_listxattr(int dfd, struct filename *filename, > return error; > } > > -static ssize_t path_listxattrat(int dfd, const char __user *pathname, > +static ssize_t path_listxattrat(int dfd, struct file *dfd_file, > + const char __user *pathname, > unsigned int at_flags, char __user *list, > size_t size) > { > @@ -991,10 +1006,9 @@ static ssize_t path_listxattrat(int dfd, const char __user *pathname, > > CLASS(filename_maybe_null, filename)(pathname, at_flags); > if (!filename) { > - CLASS(fd, f)(dfd); > - if (fd_empty(f)) > + if (!dfd_file) > return -EBADF; > - return file_listxattr(fd_file(f), list, size); > + return file_listxattr(dfd_file, list, size); > } > > lookup_flags = (at_flags & AT_SYMLINK_NOFOLLOW) ? 0 : LOOKUP_FOLLOW; > @@ -1005,24 +1019,31 @@ SYSCALL_DEFINE5(listxattrat, int, dfd, const char __user *, pathname, > unsigned int, at_flags, > char __user *, list, size_t, size) > { > - return path_listxattrat(dfd, pathname, at_flags, list, size); > + /* listxattrat() accepts O_PATH file descriptors. */ > + CLASS(fd_raw, f)(dfd); And here. > + > + return path_listxattrat(dfd, fd_file(f), pathname, at_flags, list, size); > } > > SYSCALL_DEFINE3(listxattr, const char __user *, pathname, char __user *, list, > size_t, size) > { > - return path_listxattrat(AT_FDCWD, pathname, 0, list, size); > + return path_listxattrat(AT_FDCWD, NULL, pathname, 0, list, size); > } > > SYSCALL_DEFINE3(llistxattr, const char __user *, pathname, char __user *, list, > size_t, size) > { > - return path_listxattrat(AT_FDCWD, pathname, AT_SYMLINK_NOFOLLOW, list, size); > + return path_listxattrat(AT_FDCWD, NULL, pathname, AT_SYMLINK_NOFOLLOW, > + list, size); > } > > SYSCALL_DEFINE3(flistxattr, int, fd, char __user *, list, size_t, size) > { > - return path_listxattrat(fd, NULL, AT_EMPTY_PATH, list, size); > + /* flistxattr() rejects O_PATH file descriptors. */ > + CLASS(fd, f)(fd); > + > + return path_listxattrat(fd, fd_file(f), NULL, AT_EMPTY_PATH, list, size); > } > > /* > @@ -1072,7 +1093,8 @@ static int filename_removexattr(int dfd, struct filename *filename, > return error; > } > > -static int path_removexattrat(int dfd, const char __user *pathname, > +static int path_removexattrat(int dfd, struct file *dfd_file, > + const char __user *pathname, > unsigned int at_flags, const char __user *name) > { > struct xattr_name kname; > @@ -1088,10 +1110,9 @@ static int path_removexattrat(int dfd, const char __user *pathname, > > CLASS(filename_maybe_null, filename)(pathname, at_flags); > if (!filename) { > - CLASS(fd, f)(dfd); > - if (fd_empty(f)) > + if (!dfd_file) > return -EBADF; > - return file_removexattr(fd_file(f), &kname); > + return file_removexattr(dfd_file, &kname); > } > lookup_flags = (at_flags & AT_SYMLINK_NOFOLLOW) ? 0 : LOOKUP_FOLLOW; > return filename_removexattr(dfd, filename, lookup_flags, &kname); > @@ -1100,24 +1121,31 @@ static int path_removexattrat(int dfd, const char __user *pathname, > SYSCALL_DEFINE4(removexattrat, int, dfd, const char __user *, pathname, > unsigned int, at_flags, const char __user *, name) > { > - return path_removexattrat(dfd, pathname, at_flags, name); > + /* removexattrat() accepts O_PATH file descriptors. */ > + CLASS(fd_raw, f)(dfd); And here. > + > + return path_removexattrat(dfd, fd_file(f), pathname, at_flags, name); > } > > SYSCALL_DEFINE2(removexattr, const char __user *, pathname, > const char __user *, name) > { > - return path_removexattrat(AT_FDCWD, pathname, 0, name); > + return path_removexattrat(AT_FDCWD, NULL, pathname, 0, name); > } > > SYSCALL_DEFINE2(lremovexattr, const char __user *, pathname, > const char __user *, name) > { > - return path_removexattrat(AT_FDCWD, pathname, AT_SYMLINK_NOFOLLOW, name); > + return path_removexattrat(AT_FDCWD, NULL, pathname, AT_SYMLINK_NOFOLLOW, > + name); > } > > SYSCALL_DEFINE2(fremovexattr, int, fd, const char __user *, name) > { > - return path_removexattrat(fd, NULL, AT_EMPTY_PATH, name); > + /* fremovexattr() rejects O_PATH file descriptors. */ > + CLASS(fd, f)(fd); > + > + return path_removexattrat(fd, fd_file(f), NULL, AT_EMPTY_PATH, name); > } > > int xattr_list_one(char **buffer, ssize_t *remaining_size, const char *name) > Thanks, Andreas