public inbox for [email protected]
 help / color / mirror / Atom feed
* [RFC][PATCHES] xattr stuff and interactions with io_uring
@ 2024-10-02  1:10 Al Viro
  2024-10-02  1:22 ` [PATCH 1/9] xattr: switch to CLASS(fd) Al Viro
  2024-11-02  7:28 ` [RFC][PATCHES v2] xattr stuff and interactions with io_uring Al Viro
  0 siblings, 2 replies; 50+ messages in thread
From: Al Viro @ 2024-10-02  1:10 UTC (permalink / raw)
  To: linux-fsdevel; +Cc: Christian Brauner, io-uring, Christian Göttsche

	The series below is a small-scale attempt at sanitizing the
interplay between io_uring and normal syscalls.  It is limited to
xattr-related syscalls and I would like to have a similar approach taken
a lot further than that.

	Right now we have an ad-hoc mess, with io_uring stuff hooked
at different layers to syscall guts.  And it keeps growing and getting
messier.

	As far as I can tell, the general rules are

* we want arguments copied in when request is submitted to io_uring.
Rationale: userland caller should be able to have that stuff on his
stack frame, rather than keeping it around until the request completion.
Fair enough.

* destination for the stuff we want copied _out_ (results of read(),
etc.)  has to stay around until the IO completion.  In other words, such
references remain as userland pointers until the request is executed.
Fair enough.

* the things get less clear-cut where we are talking about bulk data
copied _in_ - write() arguments, for example.  In some cases we do
handle that at request execution time, in some we do not (setsockopt
vs. setxattr vs. write, for example).  Decided on individual basis?

* some argument validation is done when request is submitted; however,
anything related to resolving pathnames (at least - there may be other
such areas) is done only at the time request is processed.  Rationale:
previous requests might alter the state in question and we want the
effects of such changes visible to our request.

* pathnames are copied in at submission time and resolved at execution
time; descriptors are resolved at submission time, except when used in
dirfd role.

	What I propose for xattr stuff (and what could serve as a model
for other cases) is two families of helpers; one takes struct file
reference + whatever other arguments we want, another - dfd + filename +
lookup_flags + other arguments; filename is passed as a struct filename
reference, which is consumed by the helper.

	Normal syscalls are easily expressed via those; io_uring side is
also apparently OK.  Not sure if it's flexible enough, though - e.g.
IORING_OP_MKDIRAT et.al. end up resolving the descriptor _twice_
and can't have it coming from io_uring analogue of descriptor table.
It might make more sense to allow a variant that would have dirfd already
resolved to file reference.  It's not that hard to do on fs/namei.c side
(set_nameidata() and path_init() would have to be taught about that,
and that's more of less it), but... we need to figure out whether it
makes better sense to have descriptor resolution prompt or delayed on
the io_uring side - i.e. at submission or at execution time.

	Anyway, for now I'm following the model we have for do_mkdirat()
et.al.  It's definitely flexible enough on the syscall side; in particular,
rebasing ...xattrat(2) patch on top of that had been trivial.  It also
promises some fairly nice simplifications wrt struct filename vs. audit,
but that's a separate story.

	Currently the branch lives in
git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs.git #work.xattr
Individual patches in followups.

1/9)	switch to CLASS(fd) use.  Obvious.

2/9)	rename struct xattr_ctx to kernel_xattr_ctx
prep from the ...xattrat() series, to reduce the PITA for rebase

3/9)	io_[gs]etxattr_prep(): just use getname()
getname_flags(_, LOOKUP_FOLLOW) is ridiculous.

4/9)	new helper: import_xattr_name()
exact same logics for copying the xattr name in, dealing with
overflows, etc. in a lot of places.  

5/9)	replace do_setxattr() with saner helpers
file_setxattr(file, ctx) and filename_setxattr(dfd, filename, lookup_flags, ctx).
Don't mess with do_setxattr() directly - use those.  In particular, don't
bother with the ESTALE loop in io_uring - it doesn't belong there.

6/9)	replace do_getxattr() with saner helpers
Same on getxattr() side.

7/9)	new helpers: file_listxattr(), filename_listxattr()
Same, except that io_uring doesn't use those, so the helpers are left static.

8/9)	new helpers: file_removexattr(), filename_removexattr()
Ditto

9/9)	fs/xattr: add *at family syscalls
Rebased patch introducing those, with a bunch of fixes folded in so we don't
create bisect hazard there.  Potentially interesting bit is the pathname-handling
logics - getname_xattr(pathname, flags) returns a struct filename reference
if no AT_EMPTY_PATH had been given or if the pathname was non-NULL and turned out
to be non-empty.  With (NULL,AT_EMPTY_PATH) or (empty string, AT_EMPTY_PATH) it
returns NULL (and it tries to skip the allocation using the same trick with
get_user() that vfs_empty_path() uses).  That helper simplifies a lot of things,
and it should be cheap enough to convert fsetxattr(2) et.al. to the unified
path_setxattrat() and its ilk.  IF we get a slowdown on those, we can always
expand and simplify the calls in fsetxattr(2) and friends.

Anyway, comments, review and testing would be very welcome.

^ permalink raw reply	[flat|nested] 50+ messages in thread

* [PATCH 1/9] xattr: switch to CLASS(fd)
  2024-10-02  1:10 [RFC][PATCHES] xattr stuff and interactions with io_uring Al Viro
@ 2024-10-02  1:22 ` Al Viro
  2024-10-02  1:22   ` [PATCH 2/9] fs: rename struct xattr_ctx to kernel_xattr_ctx Al Viro
                     ` (8 more replies)
  2024-11-02  7:28 ` [RFC][PATCHES v2] xattr stuff and interactions with io_uring Al Viro
  1 sibling, 9 replies; 50+ messages in thread
From: Al Viro @ 2024-10-02  1:22 UTC (permalink / raw)
  To: linux-fsdevel; +Cc: brauner, io-uring, cgzones

Signed-off-by: Al Viro <[email protected]>
---
 fs/xattr.c | 35 ++++++++++++++---------------------
 1 file changed, 14 insertions(+), 21 deletions(-)

diff --git a/fs/xattr.c b/fs/xattr.c
index 05ec7e7d9e87..0fc813cb005c 100644
--- a/fs/xattr.c
+++ b/fs/xattr.c
@@ -697,9 +697,9 @@ SYSCALL_DEFINE5(fsetxattr, int, fd, const char __user *, name,
 	int error;
 
 	CLASS(fd, f)(fd);
-	if (!fd_file(f))
-		return -EBADF;
 
+	if (fd_empty(f))
+		return -EBADF;
 	audit_file(fd_file(f));
 	error = setxattr_copy(name, &ctx);
 	if (error)
@@ -809,16 +809,13 @@ SYSCALL_DEFINE4(lgetxattr, const char __user *, pathname,
 SYSCALL_DEFINE4(fgetxattr, int, fd, const char __user *, name,
 		void __user *, value, size_t, size)
 {
-	struct fd f = fdget(fd);
-	ssize_t error = -EBADF;
+	CLASS(fd, f)(fd);
 
-	if (!fd_file(f))
-		return error;
+	if (fd_empty(f))
+		return -EBADF;
 	audit_file(fd_file(f));
-	error = getxattr(file_mnt_idmap(fd_file(f)), fd_file(f)->f_path.dentry,
+	return getxattr(file_mnt_idmap(fd_file(f)), fd_file(f)->f_path.dentry,
 			 name, value, size);
-	fdput(f);
-	return error;
 }
 
 /*
@@ -885,15 +882,12 @@ SYSCALL_DEFINE3(llistxattr, const char __user *, pathname, char __user *, list,
 
 SYSCALL_DEFINE3(flistxattr, int, fd, char __user *, list, size_t, size)
 {
-	struct fd f = fdget(fd);
-	ssize_t error = -EBADF;
+	CLASS(fd, f)(fd);
 
-	if (!fd_file(f))
-		return error;
+	if (fd_empty(f))
+		return -EBADF;
 	audit_file(fd_file(f));
-	error = listxattr(fd_file(f)->f_path.dentry, list, size);
-	fdput(f);
-	return error;
+	return listxattr(fd_file(f)->f_path.dentry, list, size);
 }
 
 /*
@@ -950,12 +944,12 @@ SYSCALL_DEFINE2(lremovexattr, const char __user *, pathname,
 
 SYSCALL_DEFINE2(fremovexattr, int, fd, const char __user *, name)
 {
-	struct fd f = fdget(fd);
+	CLASS(fd, f)(fd);
 	char kname[XATTR_NAME_MAX + 1];
-	int error = -EBADF;
+	int error;
 
-	if (!fd_file(f))
-		return error;
+	if (fd_empty(f))
+		return -EBADF;
 	audit_file(fd_file(f));
 
 	error = strncpy_from_user(kname, name, sizeof(kname));
@@ -970,7 +964,6 @@ SYSCALL_DEFINE2(fremovexattr, int, fd, const char __user *, name)
 				    fd_file(f)->f_path.dentry, kname);
 		mnt_drop_write_file(fd_file(f));
 	}
-	fdput(f);
 	return error;
 }
 
-- 
2.39.5


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH 2/9] fs: rename struct xattr_ctx to kernel_xattr_ctx
  2024-10-02  1:22 ` [PATCH 1/9] xattr: switch to CLASS(fd) Al Viro
@ 2024-10-02  1:22   ` Al Viro
  2024-10-02  5:56     ` Christian Brauner
  2024-10-02  1:22   ` [PATCH 3/9] io_[gs]etxattr_prep(): just use getname() Al Viro
                     ` (7 subsequent siblings)
  8 siblings, 1 reply; 50+ messages in thread
From: Al Viro @ 2024-10-02  1:22 UTC (permalink / raw)
  To: linux-fsdevel; +Cc: brauner, io-uring, cgzones

From: Christian Göttsche <[email protected]>

Rename the struct xattr_ctx to increase distinction with the about to be
added user API struct xattr_args.

No functional change.

Suggested-by: Christian Brauner <[email protected]>
Signed-off-by: Christian Göttsche <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
Reviewed-by: Jan Kara <[email protected]>
Signed-off-by: Christian Brauner <[email protected]>
---
 fs/internal.h    |  8 ++++----
 fs/xattr.c       | 12 ++++++------
 io_uring/xattr.c |  2 +-
 3 files changed, 11 insertions(+), 11 deletions(-)

diff --git a/fs/internal.h b/fs/internal.h
index 8c1b7acbbe8f..81c7a085355c 100644
--- a/fs/internal.h
+++ b/fs/internal.h
@@ -267,7 +267,7 @@ struct xattr_name {
 	char name[XATTR_NAME_MAX + 1];
 };
 
-struct xattr_ctx {
+struct kernel_xattr_ctx {
 	/* Value of attribute */
 	union {
 		const void __user *cvalue;
@@ -283,11 +283,11 @@ struct xattr_ctx {
 
 ssize_t do_getxattr(struct mnt_idmap *idmap,
 		    struct dentry *d,
-		    struct xattr_ctx *ctx);
+		    struct kernel_xattr_ctx *ctx);
 
-int setxattr_copy(const char __user *name, struct xattr_ctx *ctx);
+int setxattr_copy(const char __user *name, struct kernel_xattr_ctx *ctx);
 int do_setxattr(struct mnt_idmap *idmap, struct dentry *dentry,
-		struct xattr_ctx *ctx);
+		struct kernel_xattr_ctx *ctx);
 int may_write_xattr(struct mnt_idmap *idmap, struct inode *inode);
 
 #ifdef CONFIG_FS_POSIX_ACL
diff --git a/fs/xattr.c b/fs/xattr.c
index 0fc813cb005c..1214ae7e71db 100644
--- a/fs/xattr.c
+++ b/fs/xattr.c
@@ -590,7 +590,7 @@ EXPORT_SYMBOL_GPL(vfs_removexattr);
  * Extended attribute SET operations
  */
 
-int setxattr_copy(const char __user *name, struct xattr_ctx *ctx)
+int setxattr_copy(const char __user *name, struct kernel_xattr_ctx *ctx)
 {
 	int error;
 
@@ -620,7 +620,7 @@ int setxattr_copy(const char __user *name, struct xattr_ctx *ctx)
 }
 
 int do_setxattr(struct mnt_idmap *idmap, struct dentry *dentry,
-		struct xattr_ctx *ctx)
+		struct kernel_xattr_ctx *ctx)
 {
 	if (is_posix_acl_xattr(ctx->kname->name))
 		return do_set_acl(idmap, dentry, ctx->kname->name,
@@ -635,7 +635,7 @@ static int path_setxattr(const char __user *pathname,
 			 size_t size, int flags, unsigned int lookup_flags)
 {
 	struct xattr_name kname;
-	struct xattr_ctx ctx = {
+	struct kernel_xattr_ctx ctx = {
 		.cvalue   = value,
 		.kvalue   = NULL,
 		.size     = size,
@@ -687,7 +687,7 @@ SYSCALL_DEFINE5(fsetxattr, int, fd, const char __user *, name,
 		const void __user *,value, size_t, size, int, flags)
 {
 	struct xattr_name kname;
-	struct xattr_ctx ctx = {
+	struct kernel_xattr_ctx ctx = {
 		.cvalue   = value,
 		.kvalue   = NULL,
 		.size     = size,
@@ -720,7 +720,7 @@ SYSCALL_DEFINE5(fsetxattr, int, fd, const char __user *, name,
  */
 ssize_t
 do_getxattr(struct mnt_idmap *idmap, struct dentry *d,
-	struct xattr_ctx *ctx)
+	struct kernel_xattr_ctx *ctx)
 {
 	ssize_t error;
 	char *kname = ctx->kname->name;
@@ -755,7 +755,7 @@ getxattr(struct mnt_idmap *idmap, struct dentry *d,
 {
 	ssize_t error;
 	struct xattr_name kname;
-	struct xattr_ctx ctx = {
+	struct kernel_xattr_ctx ctx = {
 		.value    = value,
 		.kvalue   = NULL,
 		.size     = size,
diff --git a/io_uring/xattr.c b/io_uring/xattr.c
index 6cf41c3bc369..5b4594ede935 100644
--- a/io_uring/xattr.c
+++ b/io_uring/xattr.c
@@ -18,7 +18,7 @@
 
 struct io_xattr {
 	struct file			*file;
-	struct xattr_ctx		ctx;
+	struct kernel_xattr_ctx		ctx;
 	struct filename			*filename;
 };
 
-- 
2.39.5


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH 3/9] io_[gs]etxattr_prep(): just use getname()
  2024-10-02  1:22 ` [PATCH 1/9] xattr: switch to CLASS(fd) Al Viro
  2024-10-02  1:22   ` [PATCH 2/9] fs: rename struct xattr_ctx to kernel_xattr_ctx Al Viro
@ 2024-10-02  1:22   ` Al Viro
  2024-10-02  5:57     ` Christian Brauner
  2024-10-02  1:22   ` [PATCH 4/9] new helper: import_xattr_name() Al Viro
                     ` (6 subsequent siblings)
  8 siblings, 1 reply; 50+ messages in thread
From: Al Viro @ 2024-10-02  1:22 UTC (permalink / raw)
  To: linux-fsdevel; +Cc: brauner, io-uring, cgzones

getname_flags(pathname, LOOKUP_FOLLOW) is obviously bogus - following
trailing symlinks has no impact on how to copy the pathname from userland...

Signed-off-by: Al Viro <[email protected]>
---
 io_uring/xattr.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/io_uring/xattr.c b/io_uring/xattr.c
index 5b4594ede935..04abf0739668 100644
--- a/io_uring/xattr.c
+++ b/io_uring/xattr.c
@@ -96,7 +96,7 @@ int io_getxattr_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
 
 	path = u64_to_user_ptr(READ_ONCE(sqe->addr3));
 
-	ix->filename = getname_flags(path, LOOKUP_FOLLOW);
+	ix->filename = getname(path);
 	if (IS_ERR(ix->filename)) {
 		ret = PTR_ERR(ix->filename);
 		ix->filename = NULL;
@@ -189,7 +189,7 @@ int io_setxattr_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
 
 	path = u64_to_user_ptr(READ_ONCE(sqe->addr3));
 
-	ix->filename = getname_flags(path, LOOKUP_FOLLOW);
+	ix->filename = getname(path);
 	if (IS_ERR(ix->filename)) {
 		ret = PTR_ERR(ix->filename);
 		ix->filename = NULL;
-- 
2.39.5


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH 4/9] new helper: import_xattr_name()
  2024-10-02  1:22 ` [PATCH 1/9] xattr: switch to CLASS(fd) Al Viro
  2024-10-02  1:22   ` [PATCH 2/9] fs: rename struct xattr_ctx to kernel_xattr_ctx Al Viro
  2024-10-02  1:22   ` [PATCH 3/9] io_[gs]etxattr_prep(): just use getname() Al Viro
@ 2024-10-02  1:22   ` Al Viro
  2024-10-02  5:57     ` Christian Brauner
  2024-10-02  1:22   ` [PATCH 5/9] replace do_setxattr() with saner helpers Al Viro
                     ` (5 subsequent siblings)
  8 siblings, 1 reply; 50+ messages in thread
From: Al Viro @ 2024-10-02  1:22 UTC (permalink / raw)
  To: linux-fsdevel; +Cc: brauner, io-uring, cgzones

common logics for marshalling xattr names.

Signed-off-by: Al Viro <[email protected]>
---
 fs/internal.h    |  3 +++
 fs/xattr.c       | 45 +++++++++++++++++++++++----------------------
 io_uring/xattr.c |  7 ++-----
 3 files changed, 28 insertions(+), 27 deletions(-)

diff --git a/fs/internal.h b/fs/internal.h
index 81c7a085355c..b9f5ac4d39fc 100644
--- a/fs/internal.h
+++ b/fs/internal.h
@@ -288,6 +288,9 @@ ssize_t do_getxattr(struct mnt_idmap *idmap,
 int setxattr_copy(const char __user *name, struct kernel_xattr_ctx *ctx);
 int do_setxattr(struct mnt_idmap *idmap, struct dentry *dentry,
 		struct kernel_xattr_ctx *ctx);
+
+int import_xattr_name(struct xattr_name *kname, const char __user *name);
+
 int may_write_xattr(struct mnt_idmap *idmap, struct inode *inode);
 
 #ifdef CONFIG_FS_POSIX_ACL
diff --git a/fs/xattr.c b/fs/xattr.c
index 1214ae7e71db..d8f7c766f28a 100644
--- a/fs/xattr.c
+++ b/fs/xattr.c
@@ -586,6 +586,17 @@ vfs_removexattr(struct mnt_idmap *idmap, struct dentry *dentry,
 }
 EXPORT_SYMBOL_GPL(vfs_removexattr);
 
+int import_xattr_name(struct xattr_name *kname, const char __user *name)
+{
+	int error = strncpy_from_user(kname->name, name,
+					sizeof(kname->name));
+	if (error == 0 || error == sizeof(kname->name))
+		return -ERANGE;
+	if (error < 0)
+		return error;
+	return 0;
+}
+
 /*
  * Extended attribute SET operations
  */
@@ -597,14 +608,10 @@ int setxattr_copy(const char __user *name, struct kernel_xattr_ctx *ctx)
 	if (ctx->flags & ~(XATTR_CREATE|XATTR_REPLACE))
 		return -EINVAL;
 
-	error = strncpy_from_user(ctx->kname->name, name,
-				sizeof(ctx->kname->name));
-	if (error == 0 || error == sizeof(ctx->kname->name))
-		return  -ERANGE;
-	if (error < 0)
+	error = import_xattr_name(ctx->kname, name);
+	if (error)
 		return error;
 
-	error = 0;
 	if (ctx->size) {
 		if (ctx->size > XATTR_SIZE_MAX)
 			return -E2BIG;
@@ -763,10 +770,8 @@ getxattr(struct mnt_idmap *idmap, struct dentry *d,
 		.flags    = 0,
 	};
 
-	error = strncpy_from_user(kname.name, name, sizeof(kname.name));
-	if (error == 0 || error == sizeof(kname.name))
-		error = -ERANGE;
-	if (error < 0)
+	error = import_xattr_name(&kname, name);
+	if (error)
 		return error;
 
 	error =  do_getxattr(idmap, d, &ctx);
@@ -906,12 +911,10 @@ static int path_removexattr(const char __user *pathname,
 {
 	struct path path;
 	int error;
-	char kname[XATTR_NAME_MAX + 1];
+	struct xattr_name kname;
 
-	error = strncpy_from_user(kname, name, sizeof(kname));
-	if (error == 0 || error == sizeof(kname))
-		error = -ERANGE;
-	if (error < 0)
+	error = import_xattr_name(&kname, name);
+	if (error)
 		return error;
 retry:
 	error = user_path_at(AT_FDCWD, pathname, lookup_flags, &path);
@@ -919,7 +922,7 @@ static int path_removexattr(const char __user *pathname,
 		return error;
 	error = mnt_want_write(path.mnt);
 	if (!error) {
-		error = removexattr(mnt_idmap(path.mnt), path.dentry, kname);
+		error = removexattr(mnt_idmap(path.mnt), path.dentry, kname.name);
 		mnt_drop_write(path.mnt);
 	}
 	path_put(&path);
@@ -945,23 +948,21 @@ SYSCALL_DEFINE2(lremovexattr, const char __user *, pathname,
 SYSCALL_DEFINE2(fremovexattr, int, fd, const char __user *, name)
 {
 	CLASS(fd, f)(fd);
-	char kname[XATTR_NAME_MAX + 1];
+	struct xattr_name kname;
 	int error;
 
 	if (fd_empty(f))
 		return -EBADF;
 	audit_file(fd_file(f));
 
-	error = strncpy_from_user(kname, name, sizeof(kname));
-	if (error == 0 || error == sizeof(kname))
-		error = -ERANGE;
-	if (error < 0)
+	error = import_xattr_name(&kname, name);
+	if (error)
 		return error;
 
 	error = mnt_want_write_file(fd_file(f));
 	if (!error) {
 		error = removexattr(file_mnt_idmap(fd_file(f)),
-				    fd_file(f)->f_path.dentry, kname);
+				    fd_file(f)->f_path.dentry, kname.name);
 		mnt_drop_write_file(fd_file(f));
 	}
 	return error;
diff --git a/io_uring/xattr.c b/io_uring/xattr.c
index 04abf0739668..71d9e2569a2f 100644
--- a/io_uring/xattr.c
+++ b/io_uring/xattr.c
@@ -65,11 +65,8 @@ static int __io_getxattr_prep(struct io_kiocb *req,
 	if (!ix->ctx.kname)
 		return -ENOMEM;
 
-	ret = strncpy_from_user(ix->ctx.kname->name, name,
-				sizeof(ix->ctx.kname->name));
-	if (!ret || ret == sizeof(ix->ctx.kname->name))
-		ret = -ERANGE;
-	if (ret < 0) {
+	ret = import_xattr_name(ix->ctx.kname, name);
+	if (ret) {
 		kfree(ix->ctx.kname);
 		return ret;
 	}
-- 
2.39.5


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH 5/9] replace do_setxattr() with saner helpers.
  2024-10-02  1:22 ` [PATCH 1/9] xattr: switch to CLASS(fd) Al Viro
                     ` (2 preceding siblings ...)
  2024-10-02  1:22   ` [PATCH 4/9] new helper: import_xattr_name() Al Viro
@ 2024-10-02  1:22   ` Al Viro
  2024-10-02  1:34     ` Jens Axboe
  2024-10-02  1:22   ` [PATCH 6/9] replace do_getxattr() " Al Viro
                     ` (4 subsequent siblings)
  8 siblings, 1 reply; 50+ messages in thread
From: Al Viro @ 2024-10-02  1:22 UTC (permalink / raw)
  To: linux-fsdevel; +Cc: brauner, io-uring, cgzones

io_uring setxattr logics duplicates stuff from fs/xattr.c; provide
saner helpers (filename_setxattr() and file_setxattr() resp.) and
use them.

Signed-off-by: Al Viro <[email protected]>
---
 fs/internal.h    |  6 ++---
 fs/xattr.c       | 68 ++++++++++++++++++++++++++++++------------------
 io_uring/xattr.c | 32 +++--------------------
 3 files changed, 49 insertions(+), 57 deletions(-)

diff --git a/fs/internal.h b/fs/internal.h
index b9f5ac4d39fc..be7c0da3bcec 100644
--- a/fs/internal.h
+++ b/fs/internal.h
@@ -285,10 +285,10 @@ ssize_t do_getxattr(struct mnt_idmap *idmap,
 		    struct dentry *d,
 		    struct kernel_xattr_ctx *ctx);
 
+int file_setxattr(struct file *file, struct kernel_xattr_ctx *ctx);
+int filename_setxattr(int dfd, struct filename *filename,
+		      unsigned int lookup_flags, struct kernel_xattr_ctx *ctx);
 int setxattr_copy(const char __user *name, struct kernel_xattr_ctx *ctx);
-int do_setxattr(struct mnt_idmap *idmap, struct dentry *dentry,
-		struct kernel_xattr_ctx *ctx);
-
 int import_xattr_name(struct xattr_name *kname, const char __user *name);
 
 int may_write_xattr(struct mnt_idmap *idmap, struct inode *inode);
diff --git a/fs/xattr.c b/fs/xattr.c
index d8f7c766f28a..6326a1ea28e9 100644
--- a/fs/xattr.c
+++ b/fs/xattr.c
@@ -626,7 +626,7 @@ int setxattr_copy(const char __user *name, struct kernel_xattr_ctx *ctx)
 	return error;
 }
 
-int do_setxattr(struct mnt_idmap *idmap, struct dentry *dentry,
+static int do_setxattr(struct mnt_idmap *idmap, struct dentry *dentry,
 		struct kernel_xattr_ctx *ctx)
 {
 	if (is_posix_acl_xattr(ctx->kname->name))
@@ -637,32 +637,31 @@ int do_setxattr(struct mnt_idmap *idmap, struct dentry *dentry,
 			ctx->kvalue, ctx->size, ctx->flags);
 }
 
-static int path_setxattr(const char __user *pathname,
-			 const char __user *name, const void __user *value,
-			 size_t size, int flags, unsigned int lookup_flags)
+int file_setxattr(struct file *f, struct kernel_xattr_ctx *ctx)
+{
+	int error = mnt_want_write_file(f);
+
+	if (!error) {
+		audit_file(f);
+		error = do_setxattr(file_mnt_idmap(f), f->f_path.dentry, ctx);
+		mnt_drop_write_file(f);
+	}
+	return error;
+}
+
+int filename_setxattr(int dfd, struct filename *filename,
+		      unsigned int lookup_flags, struct kernel_xattr_ctx *ctx)
 {
-	struct xattr_name kname;
-	struct kernel_xattr_ctx ctx = {
-		.cvalue   = value,
-		.kvalue   = NULL,
-		.size     = size,
-		.kname    = &kname,
-		.flags    = flags,
-	};
 	struct path path;
 	int error;
 
-	error = setxattr_copy(name, &ctx);
-	if (error)
-		return error;
-
 retry:
-	error = user_path_at(AT_FDCWD, pathname, lookup_flags, &path);
+	error = filename_lookup(dfd, filename, lookup_flags, &path, NULL);
 	if (error)
 		goto out;
 	error = mnt_want_write(path.mnt);
 	if (!error) {
-		error = do_setxattr(mnt_idmap(path.mnt), path.dentry, &ctx);
+		error = do_setxattr(mnt_idmap(path.mnt), path.dentry, ctx);
 		mnt_drop_write(path.mnt);
 	}
 	path_put(&path);
@@ -672,6 +671,30 @@ static int path_setxattr(const char __user *pathname,
 	}
 
 out:
+	putname(filename);
+	return error;
+}
+
+static int path_setxattr(const char __user *pathname,
+			 const char __user *name, const void __user *value,
+			 size_t size, int flags, unsigned int lookup_flags)
+{
+	struct xattr_name kname;
+	struct kernel_xattr_ctx ctx = {
+		.cvalue   = value,
+		.kvalue   = NULL,
+		.size     = size,
+		.kname    = &kname,
+		.flags    = flags,
+	};
+	int error;
+
+	error = setxattr_copy(name, &ctx);
+	if (error)
+		return error;
+
+	error = filename_setxattr(AT_FDCWD, getname(pathname), lookup_flags,
+				  &ctx);
 	kvfree(ctx.kvalue);
 	return error;
 }
@@ -707,17 +730,12 @@ SYSCALL_DEFINE5(fsetxattr, int, fd, const char __user *, name,
 
 	if (fd_empty(f))
 		return -EBADF;
-	audit_file(fd_file(f));
+
 	error = setxattr_copy(name, &ctx);
 	if (error)
 		return error;
 
-	error = mnt_want_write_file(fd_file(f));
-	if (!error) {
-		error = do_setxattr(file_mnt_idmap(fd_file(f)),
-				    fd_file(f)->f_path.dentry, &ctx);
-		mnt_drop_write_file(fd_file(f));
-	}
+	error = file_setxattr(fd_file(f), &ctx);
 	kvfree(ctx.kvalue);
 	return error;
 }
diff --git a/io_uring/xattr.c b/io_uring/xattr.c
index 71d9e2569a2f..7f6bbfd846b9 100644
--- a/io_uring/xattr.c
+++ b/io_uring/xattr.c
@@ -200,28 +200,14 @@ int io_fsetxattr_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
 	return __io_setxattr_prep(req, sqe);
 }
 
-static int __io_setxattr(struct io_kiocb *req, unsigned int issue_flags,
-			const struct path *path)
-{
-	struct io_xattr *ix = io_kiocb_to_cmd(req, struct io_xattr);
-	int ret;
-
-	ret = mnt_want_write(path->mnt);
-	if (!ret) {
-		ret = do_setxattr(mnt_idmap(path->mnt), path->dentry, &ix->ctx);
-		mnt_drop_write(path->mnt);
-	}
-
-	return ret;
-}
-
 int io_fsetxattr(struct io_kiocb *req, unsigned int issue_flags)
 {
+	struct io_xattr *ix = io_kiocb_to_cmd(req, struct io_xattr);
 	int ret;
 
 	WARN_ON_ONCE(issue_flags & IO_URING_F_NONBLOCK);
 
-	ret = __io_setxattr(req, issue_flags, &req->file->f_path);
+	ret = file_setxattr(req->file, &ix->ctx);
 	io_xattr_finish(req, ret);
 	return IOU_OK;
 }
@@ -229,23 +215,11 @@ int io_fsetxattr(struct io_kiocb *req, unsigned int issue_flags)
 int io_setxattr(struct io_kiocb *req, unsigned int issue_flags)
 {
 	struct io_xattr *ix = io_kiocb_to_cmd(req, struct io_xattr);
-	unsigned int lookup_flags = LOOKUP_FOLLOW;
-	struct path path;
 	int ret;
 
 	WARN_ON_ONCE(issue_flags & IO_URING_F_NONBLOCK);
 
-retry:
-	ret = filename_lookup(AT_FDCWD, ix->filename, lookup_flags, &path, NULL);
-	if (!ret) {
-		ret = __io_setxattr(req, issue_flags, &path);
-		path_put(&path);
-		if (retry_estale(ret, lookup_flags)) {
-			lookup_flags |= LOOKUP_REVAL;
-			goto retry;
-		}
-	}
-
+	ret = filename_setxattr(AT_FDCWD, ix->filename, LOOKUP_FOLLOW, &ix->ctx);
 	io_xattr_finish(req, ret);
 	return IOU_OK;
 }
-- 
2.39.5


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH 6/9] replace do_getxattr() with saner helpers.
  2024-10-02  1:22 ` [PATCH 1/9] xattr: switch to CLASS(fd) Al Viro
                     ` (3 preceding siblings ...)
  2024-10-02  1:22   ` [PATCH 5/9] replace do_setxattr() with saner helpers Al Viro
@ 2024-10-02  1:22   ` Al Viro
  2024-10-02  5:59     ` Christian Brauner
  2024-10-02  1:22   ` [PATCH 7/9] new helpers: file_listxattr(), filename_listxattr() Al Viro
                     ` (3 subsequent siblings)
  8 siblings, 1 reply; 50+ messages in thread
From: Al Viro @ 2024-10-02  1:22 UTC (permalink / raw)
  To: linux-fsdevel; +Cc: brauner, io-uring, cgzones

similar to do_setxattr() in the previous commit...

Signed-off-by: Al Viro <[email protected]>
---
 fs/internal.h    |  8 ++---
 fs/xattr.c       | 87 ++++++++++++++++++++++++++++--------------------
 io_uring/xattr.c | 22 ++----------
 3 files changed, 56 insertions(+), 61 deletions(-)

diff --git a/fs/internal.h b/fs/internal.h
index be7c0da3bcec..8001efd1f047 100644
--- a/fs/internal.h
+++ b/fs/internal.h
@@ -280,11 +280,9 @@ struct kernel_xattr_ctx {
 	unsigned int flags;
 };
 
-
-ssize_t do_getxattr(struct mnt_idmap *idmap,
-		    struct dentry *d,
-		    struct kernel_xattr_ctx *ctx);
-
+ssize_t file_getxattr(struct file *file, struct kernel_xattr_ctx *ctx);
+ssize_t filename_getxattr(int dfd, struct filename *filename,
+			  unsigned int lookup_flags, struct kernel_xattr_ctx *ctx);
 int file_setxattr(struct file *file, struct kernel_xattr_ctx *ctx);
 int filename_setxattr(int dfd, struct filename *filename,
 		      unsigned int lookup_flags, struct kernel_xattr_ctx *ctx);
diff --git a/fs/xattr.c b/fs/xattr.c
index 6326a1ea28e9..a0e304c65d51 100644
--- a/fs/xattr.c
+++ b/fs/xattr.c
@@ -743,27 +743,28 @@ SYSCALL_DEFINE5(fsetxattr, int, fd, const char __user *, name,
 /*
  * Extended attribute GET operations
  */
-ssize_t
+static ssize_t
 do_getxattr(struct mnt_idmap *idmap, struct dentry *d,
 	struct kernel_xattr_ctx *ctx)
 {
 	ssize_t error;
 	char *kname = ctx->kname->name;
+	void *kvalue = NULL;
 
 	if (ctx->size) {
 		if (ctx->size > XATTR_SIZE_MAX)
 			ctx->size = XATTR_SIZE_MAX;
-		ctx->kvalue = kvzalloc(ctx->size, GFP_KERNEL);
-		if (!ctx->kvalue)
+		kvalue = kvzalloc(ctx->size, GFP_KERNEL);
+		if (!kvalue)
 			return -ENOMEM;
 	}
 
-	if (is_posix_acl_xattr(ctx->kname->name))
-		error = do_get_acl(idmap, d, kname, ctx->kvalue, ctx->size);
+	if (is_posix_acl_xattr(kname))
+		error = do_get_acl(idmap, d, kname, kvalue, ctx->size);
 	else
-		error = vfs_getxattr(idmap, d, kname, ctx->kvalue, ctx->size);
+		error = vfs_getxattr(idmap, d, kname, kvalue, ctx->size);
 	if (error > 0) {
-		if (ctx->size && copy_to_user(ctx->value, ctx->kvalue, error))
+		if (ctx->size && copy_to_user(ctx->value, kvalue, error))
 			error = -EFAULT;
 	} else if (error == -ERANGE && ctx->size >= XATTR_SIZE_MAX) {
 		/* The file system tried to returned a value bigger
@@ -771,52 +772,55 @@ do_getxattr(struct mnt_idmap *idmap, struct dentry *d,
 		error = -E2BIG;
 	}
 
+	kvfree(kvalue);
 	return error;
 }
 
-static ssize_t
-getxattr(struct mnt_idmap *idmap, struct dentry *d,
-	 const char __user *name, void __user *value, size_t size)
+ssize_t file_getxattr(struct file *f, struct kernel_xattr_ctx *ctx)
 {
-	ssize_t error;
-	struct xattr_name kname;
-	struct kernel_xattr_ctx ctx = {
-		.value    = value,
-		.kvalue   = NULL,
-		.size     = size,
-		.kname    = &kname,
-		.flags    = 0,
-	};
-
-	error = import_xattr_name(&kname, name);
-	if (error)
-		return error;
-
-	error =  do_getxattr(idmap, d, &ctx);
-
-	kvfree(ctx.kvalue);
-	return error;
+	audit_file(f);
+	return do_getxattr(file_mnt_idmap(f), f->f_path.dentry, ctx);
 }
 
-static ssize_t path_getxattr(const char __user *pathname,
-			     const char __user *name, void __user *value,
-			     size_t size, unsigned int lookup_flags)
+ssize_t filename_getxattr(int dfd, struct filename *filename,
+			  unsigned int lookup_flags, struct kernel_xattr_ctx *ctx)
 {
 	struct path path;
 	ssize_t error;
 retry:
-	error = user_path_at(AT_FDCWD, pathname, lookup_flags, &path);
+	error = filename_lookup(dfd, filename, lookup_flags, &path, NULL);
 	if (error)
-		return error;
-	error = getxattr(mnt_idmap(path.mnt), path.dentry, name, value, size);
+		goto out;
+	error = do_getxattr(mnt_idmap(path.mnt), path.dentry, ctx);
 	path_put(&path);
 	if (retry_estale(error, lookup_flags)) {
 		lookup_flags |= LOOKUP_REVAL;
 		goto retry;
 	}
+out:
+	putname(filename);
 	return error;
 }
 
+static ssize_t path_getxattr(const char __user *pathname,
+			     const char __user *name, void __user *value,
+			     size_t size, unsigned int lookup_flags)
+{
+	ssize_t error;
+	struct xattr_name kname;
+	struct kernel_xattr_ctx ctx = {
+		.value    = value,
+		.size     = size,
+		.kname    = &kname,
+		.flags    = 0,
+	};
+
+	error = import_xattr_name(&kname, name);
+	if (error)
+		return error;
+	return filename_getxattr(AT_FDCWD, getname(pathname), lookup_flags, &ctx);
+}
+
 SYSCALL_DEFINE4(getxattr, const char __user *, pathname,
 		const char __user *, name, void __user *, value, size_t, size)
 {
@@ -832,13 +836,22 @@ SYSCALL_DEFINE4(lgetxattr, const char __user *, pathname,
 SYSCALL_DEFINE4(fgetxattr, int, fd, const char __user *, name,
 		void __user *, value, size_t, size)
 {
+	ssize_t error;
+	struct xattr_name kname;
+	struct kernel_xattr_ctx ctx = {
+		.value    = value,
+		.size     = size,
+		.kname    = &kname,
+		.flags    = 0,
+	};
 	CLASS(fd, f)(fd);
 
 	if (fd_empty(f))
 		return -EBADF;
-	audit_file(fd_file(f));
-	return getxattr(file_mnt_idmap(fd_file(f)), fd_file(f)->f_path.dentry,
-			 name, value, size);
+	error = import_xattr_name(&kname, name);
+	if (error)
+		return error;
+	return file_getxattr(fd_file(f), &ctx);
 }
 
 /*
diff --git a/io_uring/xattr.c b/io_uring/xattr.c
index 7f6bbfd846b9..0eaeaed39649 100644
--- a/io_uring/xattr.c
+++ b/io_uring/xattr.c
@@ -54,7 +54,7 @@ static int __io_getxattr_prep(struct io_kiocb *req,
 	ix->filename = NULL;
 	ix->ctx.kvalue = NULL;
 	name = u64_to_user_ptr(READ_ONCE(sqe->addr));
-	ix->ctx.cvalue = u64_to_user_ptr(READ_ONCE(sqe->addr2));
+	ix->ctx.value = u64_to_user_ptr(READ_ONCE(sqe->addr2));
 	ix->ctx.size = READ_ONCE(sqe->len);
 	ix->ctx.flags = READ_ONCE(sqe->xattr_flags);
 
@@ -109,10 +109,7 @@ int io_fgetxattr(struct io_kiocb *req, unsigned int issue_flags)
 
 	WARN_ON_ONCE(issue_flags & IO_URING_F_NONBLOCK);
 
-	ret = do_getxattr(file_mnt_idmap(req->file),
-			req->file->f_path.dentry,
-			&ix->ctx);
-
+	ret = file_getxattr(req->file, &ix->ctx);
 	io_xattr_finish(req, ret);
 	return IOU_OK;
 }
@@ -120,24 +117,11 @@ int io_fgetxattr(struct io_kiocb *req, unsigned int issue_flags)
 int io_getxattr(struct io_kiocb *req, unsigned int issue_flags)
 {
 	struct io_xattr *ix = io_kiocb_to_cmd(req, struct io_xattr);
-	unsigned int lookup_flags = LOOKUP_FOLLOW;
-	struct path path;
 	int ret;
 
 	WARN_ON_ONCE(issue_flags & IO_URING_F_NONBLOCK);
 
-retry:
-	ret = filename_lookup(AT_FDCWD, ix->filename, lookup_flags, &path, NULL);
-	if (!ret) {
-		ret = do_getxattr(mnt_idmap(path.mnt), path.dentry, &ix->ctx);
-
-		path_put(&path);
-		if (retry_estale(ret, lookup_flags)) {
-			lookup_flags |= LOOKUP_REVAL;
-			goto retry;
-		}
-	}
-
+	ret = filename_getxattr(AT_FDCWD, ix->filename, LOOKUP_FOLLOW, &ix->ctx);
 	io_xattr_finish(req, ret);
 	return IOU_OK;
 }
-- 
2.39.5


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH 7/9] new helpers: file_listxattr(), filename_listxattr()
  2024-10-02  1:22 ` [PATCH 1/9] xattr: switch to CLASS(fd) Al Viro
                     ` (4 preceding siblings ...)
  2024-10-02  1:22   ` [PATCH 6/9] replace do_getxattr() " Al Viro
@ 2024-10-02  1:22   ` Al Viro
  2024-10-02  6:00     ` Christian Brauner
  2024-10-02  1:22   ` [PATCH 8/9] new helpers: file_removexattr(), filename_removexattr() Al Viro
                     ` (2 subsequent siblings)
  8 siblings, 1 reply; 50+ messages in thread
From: Al Viro @ 2024-10-02  1:22 UTC (permalink / raw)
  To: linux-fsdevel; +Cc: brauner, io-uring, cgzones

switch path_listxattr() and flistxattr(2) to those

Signed-off-by: Al Viro <[email protected]>
---
 fs/xattr.c | 29 +++++++++++++++++++++++------
 1 file changed, 23 insertions(+), 6 deletions(-)

diff --git a/fs/xattr.c b/fs/xattr.c
index a0e304c65d51..0a1da16f74b1 100644
--- a/fs/xattr.c
+++ b/fs/xattr.c
@@ -886,24 +886,42 @@ listxattr(struct dentry *d, char __user *list, size_t size)
 	return error;
 }
 
-static ssize_t path_listxattr(const char __user *pathname, char __user *list,
-			      size_t size, unsigned int lookup_flags)
+static
+ssize_t file_listxattr(struct file *f, char __user *list, size_t size)
+{
+	audit_file(f);
+	return listxattr(f->f_path.dentry, list, size);
+}
+
+static
+ssize_t filename_listxattr(int dfd, struct filename *filename,
+			   unsigned int lookup_flags,
+			   char __user *list, size_t size)
 {
 	struct path path;
 	ssize_t error;
 retry:
-	error = user_path_at(AT_FDCWD, pathname, lookup_flags, &path);
+	error = filename_lookup(dfd, filename, lookup_flags, &path, NULL);
 	if (error)
-		return error;
+		goto out;
 	error = listxattr(path.dentry, list, size);
 	path_put(&path);
 	if (retry_estale(error, lookup_flags)) {
 		lookup_flags |= LOOKUP_REVAL;
 		goto retry;
 	}
+out:
+	putname(filename);
 	return error;
 }
 
+static ssize_t path_listxattr(const char __user *pathname, char __user *list,
+			      size_t size, unsigned int lookup_flags)
+{
+	return filename_listxattr(AT_FDCWD, getname(pathname), lookup_flags,
+				  list, size);
+}
+
 SYSCALL_DEFINE3(listxattr, const char __user *, pathname, char __user *, list,
 		size_t, size)
 {
@@ -922,8 +940,7 @@ SYSCALL_DEFINE3(flistxattr, int, fd, char __user *, list, size_t, size)
 
 	if (fd_empty(f))
 		return -EBADF;
-	audit_file(fd_file(f));
-	return listxattr(fd_file(f)->f_path.dentry, list, size);
+	return file_listxattr(fd_file(f), list, size);
 }
 
 /*
-- 
2.39.5


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH 8/9] new helpers: file_removexattr(), filename_removexattr()
  2024-10-02  1:22 ` [PATCH 1/9] xattr: switch to CLASS(fd) Al Viro
                     ` (5 preceding siblings ...)
  2024-10-02  1:22   ` [PATCH 7/9] new helpers: file_listxattr(), filename_listxattr() Al Viro
@ 2024-10-02  1:22   ` Al Viro
  2024-10-02  6:01     ` Christian Brauner
  2024-10-02  1:22   ` [PATCH 9/9] fs/xattr: add *at family syscalls Al Viro
  2024-10-02  5:56   ` [PATCH 1/9] xattr: switch to CLASS(fd) Christian Brauner
  8 siblings, 1 reply; 50+ messages in thread
From: Al Viro @ 2024-10-02  1:22 UTC (permalink / raw)
  To: linux-fsdevel; +Cc: brauner, io-uring, cgzones

switch path_removexattrat() and fremovexattr(2) to those

Signed-off-by: Al Viro <[email protected]>
---
 fs/xattr.c | 52 ++++++++++++++++++++++++++++++++++------------------
 1 file changed, 34 insertions(+), 18 deletions(-)

diff --git a/fs/xattr.c b/fs/xattr.c
index 0a1da16f74b1..6f87f23c0e84 100644
--- a/fs/xattr.c
+++ b/fs/xattr.c
@@ -954,23 +954,32 @@ removexattr(struct mnt_idmap *idmap, struct dentry *d, const char *name)
 	return vfs_removexattr(idmap, d, name);
 }
 
-static int path_removexattr(const char __user *pathname,
-			    const char __user *name, unsigned int lookup_flags)
+static int file_removexattr(struct file *f, struct xattr_name *kname)
+{
+	int error = mnt_want_write_file(f);
+
+	if (!error) {
+		audit_file(f);
+		error = removexattr(file_mnt_idmap(f),
+				    f->f_path.dentry, kname->name);
+		mnt_drop_write_file(f);
+	}
+	return error;
+}
+
+static int filename_removexattr(int dfd, struct filename *filename,
+				unsigned int lookup_flags, struct xattr_name *kname)
 {
 	struct path path;
 	int error;
-	struct xattr_name kname;
 
-	error = import_xattr_name(&kname, name);
-	if (error)
-		return error;
 retry:
-	error = user_path_at(AT_FDCWD, pathname, lookup_flags, &path);
+	error = filename_lookup(dfd, filename, lookup_flags, &path, NULL);
 	if (error)
-		return error;
+		goto out;
 	error = mnt_want_write(path.mnt);
 	if (!error) {
-		error = removexattr(mnt_idmap(path.mnt), path.dentry, kname.name);
+		error = removexattr(mnt_idmap(path.mnt), path.dentry, kname->name);
 		mnt_drop_write(path.mnt);
 	}
 	path_put(&path);
@@ -978,9 +987,24 @@ static int path_removexattr(const char __user *pathname,
 		lookup_flags |= LOOKUP_REVAL;
 		goto retry;
 	}
+out:
+	putname(filename);
 	return error;
 }
 
+static int path_removexattr(const char __user *pathname,
+			    const char __user *name, unsigned int lookup_flags)
+{
+	struct xattr_name kname;
+	int error;
+
+	error = import_xattr_name(&kname, name);
+	if (error)
+		return error;
+	return filename_removexattr(AT_FDCWD, getname(pathname), lookup_flags,
+				    &kname);
+}
+
 SYSCALL_DEFINE2(removexattr, const char __user *, pathname,
 		const char __user *, name)
 {
@@ -1001,19 +1025,11 @@ SYSCALL_DEFINE2(fremovexattr, int, fd, const char __user *, name)
 
 	if (fd_empty(f))
 		return -EBADF;
-	audit_file(fd_file(f));
 
 	error = import_xattr_name(&kname, name);
 	if (error)
 		return error;
-
-	error = mnt_want_write_file(fd_file(f));
-	if (!error) {
-		error = removexattr(file_mnt_idmap(fd_file(f)),
-				    fd_file(f)->f_path.dentry, kname.name);
-		mnt_drop_write_file(fd_file(f));
-	}
-	return error;
+	return file_removexattr(fd_file(f), &kname);
 }
 
 int xattr_list_one(char **buffer, ssize_t *remaining_size, const char *name)
-- 
2.39.5


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH 9/9] fs/xattr: add *at family syscalls
  2024-10-02  1:22 ` [PATCH 1/9] xattr: switch to CLASS(fd) Al Viro
                     ` (6 preceding siblings ...)
  2024-10-02  1:22   ` [PATCH 8/9] new helpers: file_removexattr(), filename_removexattr() Al Viro
@ 2024-10-02  1:22   ` Al Viro
  2024-10-02  6:03     ` Christian Brauner
  2024-10-02  5:56   ` [PATCH 1/9] xattr: switch to CLASS(fd) Christian Brauner
  8 siblings, 1 reply; 50+ messages in thread
From: Al Viro @ 2024-10-02  1:22 UTC (permalink / raw)
  To: linux-fsdevel; +Cc: brauner, io-uring, cgzones

From: Christian Göttsche <[email protected]>

Add the four syscalls setxattrat(), getxattrat(), listxattrat() and
removexattrat().  Those can be used to operate on extended attributes,
especially security related ones, either relative to a pinned directory
or on a file descriptor without read access, avoiding a
/proc/<pid>/fd/<fd> detour, requiring a mounted procfs.

One use case will be setfiles(8) setting SELinux file contexts
("security.selinux") without race conditions and without a file
descriptor opened with read access requiring SELinux read permission.

Use the do_{name}at() pattern from fs/open.c.

Pass the value of the extended attribute, its length, and for
setxattrat(2) the command (XATTR_CREATE or XATTR_REPLACE) via an added
struct xattr_args to not exceed six syscall arguments and not
merging the AT_* and XATTR_* flags.

[AV: fixes by Christian Brauner folded in, the entire thing rebased on
top of {filename,file}_...xattr() primitives, treatment of empty
pathnames regularized.  As the result, AT_EMPTY_PATH+NULL handling
is cheap, so f...(2) can use it]

Signed-off-by: Christian Göttsche <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
Reviewed-by: Arnd Bergmann <[email protected]>
CC: [email protected]
CC: [email protected]
CC: [email protected]
CC: [email protected]
CC: [email protected]
CC: [email protected]
CC: [email protected]
CC: [email protected]
CC: [email protected]
CC: [email protected]
CC: [email protected]
CC: [email protected]
CC: [email protected]
CC: [email protected]
CC: [email protected]
CC: [email protected]
CC: [email protected]
CC: [email protected]
[brauner: slight tweaks]
Signed-off-by: Christian Brauner <[email protected]>
---
 arch/alpha/kernel/syscalls/syscall.tbl      |   4 +
 arch/arm/tools/syscall.tbl                  |   4 +
 arch/m68k/kernel/syscalls/syscall.tbl       |   4 +
 arch/microblaze/kernel/syscalls/syscall.tbl |   4 +
 arch/mips/kernel/syscalls/syscall_n32.tbl   |   4 +
 arch/mips/kernel/syscalls/syscall_n64.tbl   |   4 +
 arch/mips/kernel/syscalls/syscall_o32.tbl   |   4 +
 arch/parisc/kernel/syscalls/syscall.tbl     |   4 +
 arch/powerpc/kernel/syscalls/syscall.tbl    |   4 +
 arch/s390/kernel/syscalls/syscall.tbl       |   4 +
 arch/sh/kernel/syscalls/syscall.tbl         |   4 +
 arch/sparc/kernel/syscalls/syscall.tbl      |   4 +
 arch/x86/entry/syscalls/syscall_32.tbl      |   4 +
 arch/x86/entry/syscalls/syscall_64.tbl      |   4 +
 arch/xtensa/kernel/syscalls/syscall.tbl     |   4 +
 fs/xattr.c                                  | 271 ++++++++++++++------
 include/asm-generic/audit_change_attr.h     |   6 +
 include/linux/syscalls.h                    |  13 +
 include/linux/xattr.h                       |   4 +
 include/uapi/asm-generic/unistd.h           |  11 +-
 include/uapi/linux/xattr.h                  |   7 +
 21 files changed, 286 insertions(+), 86 deletions(-)

diff --git a/arch/alpha/kernel/syscalls/syscall.tbl b/arch/alpha/kernel/syscalls/syscall.tbl
index 74720667fe09..c59d53d6d3f3 100644
--- a/arch/alpha/kernel/syscalls/syscall.tbl
+++ b/arch/alpha/kernel/syscalls/syscall.tbl
@@ -502,3 +502,7 @@
 570	common	lsm_set_self_attr		sys_lsm_set_self_attr
 571	common	lsm_list_modules		sys_lsm_list_modules
 572	common  mseal				sys_mseal
+573	common	setxattrat			sys_setxattrat
+574	common	getxattrat			sys_getxattrat
+575	common	listxattrat			sys_listxattrat
+576	common	removexattrat			sys_removexattrat
diff --git a/arch/arm/tools/syscall.tbl b/arch/arm/tools/syscall.tbl
index 23c98203c40f..49eeb2ad8dbd 100644
--- a/arch/arm/tools/syscall.tbl
+++ b/arch/arm/tools/syscall.tbl
@@ -477,3 +477,7 @@
 460	common	lsm_set_self_attr		sys_lsm_set_self_attr
 461	common	lsm_list_modules		sys_lsm_list_modules
 462	common	mseal				sys_mseal
+463	common	setxattrat			sys_setxattrat
+464	common	getxattrat			sys_getxattrat
+465	common	listxattrat			sys_listxattrat
+466	common	removexattrat			sys_removexattrat
diff --git a/arch/m68k/kernel/syscalls/syscall.tbl b/arch/m68k/kernel/syscalls/syscall.tbl
index 22a3cbd4c602..f5ed71f1910d 100644
--- a/arch/m68k/kernel/syscalls/syscall.tbl
+++ b/arch/m68k/kernel/syscalls/syscall.tbl
@@ -462,3 +462,7 @@
 460	common	lsm_set_self_attr		sys_lsm_set_self_attr
 461	common	lsm_list_modules		sys_lsm_list_modules
 462	common	mseal				sys_mseal
+463	common	setxattrat			sys_setxattrat
+464	common	getxattrat			sys_getxattrat
+465	common	listxattrat			sys_listxattrat
+466	common	removexattrat			sys_removexattrat
diff --git a/arch/microblaze/kernel/syscalls/syscall.tbl b/arch/microblaze/kernel/syscalls/syscall.tbl
index 2b81a6bd78b2..680f568b77f2 100644
--- a/arch/microblaze/kernel/syscalls/syscall.tbl
+++ b/arch/microblaze/kernel/syscalls/syscall.tbl
@@ -468,3 +468,7 @@
 460	common	lsm_set_self_attr		sys_lsm_set_self_attr
 461	common	lsm_list_modules		sys_lsm_list_modules
 462	common	mseal				sys_mseal
+463	common	setxattrat			sys_setxattrat
+464	common	getxattrat			sys_getxattrat
+465	common	listxattrat			sys_listxattrat
+466	common	removexattrat			sys_removexattrat
diff --git a/arch/mips/kernel/syscalls/syscall_n32.tbl b/arch/mips/kernel/syscalls/syscall_n32.tbl
index 953f5b7dc723..0b9b7e25b69a 100644
--- a/arch/mips/kernel/syscalls/syscall_n32.tbl
+++ b/arch/mips/kernel/syscalls/syscall_n32.tbl
@@ -401,3 +401,7 @@
 460	n32	lsm_set_self_attr		sys_lsm_set_self_attr
 461	n32	lsm_list_modules		sys_lsm_list_modules
 462	n32	mseal				sys_mseal
+463	n32	setxattrat			sys_setxattrat
+464	n32	getxattrat			sys_getxattrat
+465	n32	listxattrat			sys_listxattrat
+466	n32	removexattrat			sys_removexattrat
diff --git a/arch/mips/kernel/syscalls/syscall_n64.tbl b/arch/mips/kernel/syscalls/syscall_n64.tbl
index 1464c6be6eb3..c844cd5cda62 100644
--- a/arch/mips/kernel/syscalls/syscall_n64.tbl
+++ b/arch/mips/kernel/syscalls/syscall_n64.tbl
@@ -377,3 +377,7 @@
 460	n64	lsm_set_self_attr		sys_lsm_set_self_attr
 461	n64	lsm_list_modules		sys_lsm_list_modules
 462	n64	mseal				sys_mseal
+463	n64	setxattrat			sys_setxattrat
+464	n64	getxattrat			sys_getxattrat
+465	n64	listxattrat			sys_listxattrat
+466	n64	removexattrat			sys_removexattrat
diff --git a/arch/mips/kernel/syscalls/syscall_o32.tbl b/arch/mips/kernel/syscalls/syscall_o32.tbl
index 2439a2491cff..349b8aad1159 100644
--- a/arch/mips/kernel/syscalls/syscall_o32.tbl
+++ b/arch/mips/kernel/syscalls/syscall_o32.tbl
@@ -450,3 +450,7 @@
 460	o32	lsm_set_self_attr		sys_lsm_set_self_attr
 461	o32	lsm_list_modules		sys_lsm_list_modules
 462	o32	mseal				sys_mseal
+463	o32	setxattrat			sys_setxattrat
+464	o32	getxattrat			sys_getxattrat
+465	o32	listxattrat			sys_listxattrat
+466	o32	removexattrat			sys_removexattrat
diff --git a/arch/parisc/kernel/syscalls/syscall.tbl b/arch/parisc/kernel/syscalls/syscall.tbl
index 66dc406b12e4..d9fc94c86965 100644
--- a/arch/parisc/kernel/syscalls/syscall.tbl
+++ b/arch/parisc/kernel/syscalls/syscall.tbl
@@ -461,3 +461,7 @@
 460	common	lsm_set_self_attr		sys_lsm_set_self_attr
 461	common	lsm_list_modules		sys_lsm_list_modules
 462	common	mseal				sys_mseal
+463	common	setxattrat			sys_setxattrat
+464	common	getxattrat			sys_getxattrat
+465	common	listxattrat			sys_listxattrat
+466	common	removexattrat			sys_removexattrat
diff --git a/arch/powerpc/kernel/syscalls/syscall.tbl b/arch/powerpc/kernel/syscalls/syscall.tbl
index ebae8415dfbb..d8b4ab78bef0 100644
--- a/arch/powerpc/kernel/syscalls/syscall.tbl
+++ b/arch/powerpc/kernel/syscalls/syscall.tbl
@@ -553,3 +553,7 @@
 460	common	lsm_set_self_attr		sys_lsm_set_self_attr
 461	common	lsm_list_modules		sys_lsm_list_modules
 462	common	mseal				sys_mseal
+463	common	setxattrat			sys_setxattrat
+464	common	getxattrat			sys_getxattrat
+465	common	listxattrat			sys_listxattrat
+466	common	removexattrat			sys_removexattrat
diff --git a/arch/s390/kernel/syscalls/syscall.tbl b/arch/s390/kernel/syscalls/syscall.tbl
index 01071182763e..e9115b4d8b63 100644
--- a/arch/s390/kernel/syscalls/syscall.tbl
+++ b/arch/s390/kernel/syscalls/syscall.tbl
@@ -465,3 +465,7 @@
 460  common	lsm_set_self_attr	sys_lsm_set_self_attr		sys_lsm_set_self_attr
 461  common	lsm_list_modules	sys_lsm_list_modules		sys_lsm_list_modules
 462  common	mseal			sys_mseal			sys_mseal
+463  common	setxattrat		sys_setxattrat			sys_setxattrat
+464  common	getxattrat		sys_getxattrat			sys_getxattrat
+465  common	listxattrat		sys_listxattrat			sys_listxattrat
+466  common	removexattrat		sys_removexattrat		sys_removexattrat
diff --git a/arch/sh/kernel/syscalls/syscall.tbl b/arch/sh/kernel/syscalls/syscall.tbl
index c55fd7696d40..c8cad33bf250 100644
--- a/arch/sh/kernel/syscalls/syscall.tbl
+++ b/arch/sh/kernel/syscalls/syscall.tbl
@@ -466,3 +466,7 @@
 460	common	lsm_set_self_attr		sys_lsm_set_self_attr
 461	common	lsm_list_modules		sys_lsm_list_modules
 462	common	mseal				sys_mseal
+463	common	setxattrat			sys_setxattrat
+464	common	getxattrat			sys_getxattrat
+465	common	listxattrat			sys_listxattrat
+466	common	removexattrat			sys_removexattrat
diff --git a/arch/sparc/kernel/syscalls/syscall.tbl b/arch/sparc/kernel/syscalls/syscall.tbl
index cfdfb3707c16..727f99d333b3 100644
--- a/arch/sparc/kernel/syscalls/syscall.tbl
+++ b/arch/sparc/kernel/syscalls/syscall.tbl
@@ -508,3 +508,7 @@
 460	common	lsm_set_self_attr		sys_lsm_set_self_attr
 461	common	lsm_list_modules		sys_lsm_list_modules
 462	common	mseal 				sys_mseal
+463	common	setxattrat			sys_setxattrat
+464	common	getxattrat			sys_getxattrat
+465	common	listxattrat			sys_listxattrat
+466	common	removexattrat			sys_removexattrat
diff --git a/arch/x86/entry/syscalls/syscall_32.tbl b/arch/x86/entry/syscalls/syscall_32.tbl
index 534c74b14fab..4d0fb2fba7e2 100644
--- a/arch/x86/entry/syscalls/syscall_32.tbl
+++ b/arch/x86/entry/syscalls/syscall_32.tbl
@@ -468,3 +468,7 @@
 460	i386	lsm_set_self_attr	sys_lsm_set_self_attr
 461	i386	lsm_list_modules	sys_lsm_list_modules
 462	i386	mseal 			sys_mseal
+463	i386	setxattrat		sys_setxattrat
+464	i386	getxattrat		sys_getxattrat
+465	i386	listxattrat		sys_listxattrat
+466	i386	removexattrat		sys_removexattrat
diff --git a/arch/x86/entry/syscalls/syscall_64.tbl b/arch/x86/entry/syscalls/syscall_64.tbl
index 7093ee21c0d1..5eb708bff1c7 100644
--- a/arch/x86/entry/syscalls/syscall_64.tbl
+++ b/arch/x86/entry/syscalls/syscall_64.tbl
@@ -386,6 +386,10 @@
 460	common	lsm_set_self_attr	sys_lsm_set_self_attr
 461	common	lsm_list_modules	sys_lsm_list_modules
 462 	common  mseal			sys_mseal
+463	common	setxattrat		sys_setxattrat
+464	common	getxattrat		sys_getxattrat
+465	common	listxattrat		sys_listxattrat
+466	common	removexattrat		sys_removexattrat
 
 #
 # Due to a historical design error, certain syscalls are numbered differently
diff --git a/arch/xtensa/kernel/syscalls/syscall.tbl b/arch/xtensa/kernel/syscalls/syscall.tbl
index 67083fc1b2f5..37effc1b134e 100644
--- a/arch/xtensa/kernel/syscalls/syscall.tbl
+++ b/arch/xtensa/kernel/syscalls/syscall.tbl
@@ -433,3 +433,7 @@
 460	common	lsm_set_self_attr		sys_lsm_set_self_attr
 461	common	lsm_list_modules		sys_lsm_list_modules
 462	common	mseal 				sys_mseal
+463	common	setxattrat			sys_setxattrat
+464	common	getxattrat			sys_getxattrat
+465	common	listxattrat			sys_listxattrat
+466	common	removexattrat			sys_removexattrat
diff --git a/fs/xattr.c b/fs/xattr.c
index 6f87f23c0e84..59cdb524412e 100644
--- a/fs/xattr.c
+++ b/fs/xattr.c
@@ -597,6 +597,32 @@ int import_xattr_name(struct xattr_name *kname, const char __user *name)
 	return 0;
 }
 
+static struct filename *getname_xattr(const char __user *pathname,
+				      unsigned int at_flags)
+{
+	struct filename *name;
+	char c;
+
+	if (!(at_flags & AT_EMPTY_PATH))
+		return getname(pathname);
+
+	if (!pathname)
+		return NULL;
+
+	/* try to save on allocations; will suck on s390 and um, though */
+	if (get_user(c, pathname))
+		return ERR_PTR(-EFAULT);
+	if (!c)
+		return NULL;
+
+	name = getname_flags(pathname, LOOKUP_EMPTY);
+	if (!IS_ERR(name) && !(name->name[0])) {
+		putname(name);
+		name = NULL;
+	}
+	return name;
+}
+
 /*
  * Extended attribute SET operations
  */
@@ -675,69 +701,90 @@ int filename_setxattr(int dfd, struct filename *filename,
 	return error;
 }
 
-static int path_setxattr(const char __user *pathname,
-			 const char __user *name, const void __user *value,
-			 size_t size, int flags, unsigned int lookup_flags)
+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)
 {
 	struct xattr_name kname;
 	struct kernel_xattr_ctx ctx = {
-		.cvalue   = value,
-		.kvalue   = NULL,
-		.size     = size,
-		.kname    = &kname,
-		.flags    = flags,
+		.cvalue	= value,
+		.kvalue	= NULL,
+		.size	= size,
+		.kname	= &kname,
+		.flags	= flags,
 	};
+	struct filename *filename;
+	unsigned int lookup_flags = 0;
 	int error;
 
+	if ((at_flags & ~(AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH)) != 0)
+		return -EINVAL;
+
+	if (at_flags & AT_SYMLINK_NOFOLLOW)
+		lookup_flags = LOOKUP_FOLLOW;
+
 	error = setxattr_copy(name, &ctx);
 	if (error)
 		return error;
 
-	error = filename_setxattr(AT_FDCWD, getname(pathname), lookup_flags,
-				  &ctx);
+	filename = getname_xattr(pathname, at_flags);
+	if (!filename) {
+		CLASS(fd, f)(dfd);
+		if (fd_empty(f))
+			error = -EBADF;
+		else
+			error = file_setxattr(fd_file(f), &ctx);
+	} else {
+		error = filename_setxattr(dfd, filename, lookup_flags, &ctx);
+	}
 	kvfree(ctx.kvalue);
 	return error;
 }
 
+SYSCALL_DEFINE6(setxattrat, int, dfd, const char __user *, pathname, unsigned int, at_flags,
+		const char __user *, name, const struct xattr_args __user *, uargs,
+		size_t, usize)
+{
+	struct xattr_args args = {};
+	int error;
+
+	BUILD_BUG_ON(sizeof(struct xattr_args) < XATTR_ARGS_SIZE_VER0);
+	BUILD_BUG_ON(sizeof(struct xattr_args) != XATTR_ARGS_SIZE_LATEST);
+
+	if (unlikely(usize < XATTR_ARGS_SIZE_VER0))
+		return -EINVAL;
+	if (usize > PAGE_SIZE)
+		return -E2BIG;
+
+	error = copy_struct_from_user(&args, sizeof(args), uargs, usize);
+	if (error)
+		return error;
+
+	return path_setxattrat(dfd, pathname, at_flags, name,
+			       u64_to_user_ptr(args.value), args.size,
+			       args.flags);
+}
+
 SYSCALL_DEFINE5(setxattr, const char __user *, pathname,
 		const char __user *, name, const void __user *, value,
 		size_t, size, int, flags)
 {
-	return path_setxattr(pathname, name, value, size, flags, LOOKUP_FOLLOW);
+	return path_setxattrat(AT_FDCWD, 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_setxattr(pathname, name, value, size, flags, 0);
+	return path_setxattrat(AT_FDCWD, 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)
 {
-	struct xattr_name kname;
-	struct kernel_xattr_ctx ctx = {
-		.cvalue   = value,
-		.kvalue   = NULL,
-		.size     = size,
-		.kname    = &kname,
-		.flags    = flags,
-	};
-	int error;
-
-	CLASS(fd, f)(fd);
-
-	if (fd_empty(f))
-		return -EBADF;
-
-	error = setxattr_copy(name, &ctx);
-	if (error)
-		return error;
-
-	error = file_setxattr(fd_file(f), &ctx);
-	kvfree(ctx.kvalue);
-	return error;
+	return path_setxattrat(fd, NULL, AT_EMPTY_PATH, name,
+			       value, size, flags);
 }
 
 /*
@@ -802,11 +849,10 @@ ssize_t filename_getxattr(int dfd, struct filename *filename,
 	return error;
 }
 
-static ssize_t path_getxattr(const char __user *pathname,
-			     const char __user *name, void __user *value,
-			     size_t size, unsigned int lookup_flags)
+static ssize_t path_getxattrat(int dfd, const char __user *pathname,
+			       unsigned int at_flags, const char __user *name,
+			       void __user *value, size_t size)
 {
-	ssize_t error;
 	struct xattr_name kname;
 	struct kernel_xattr_ctx ctx = {
 		.value    = value,
@@ -814,44 +860,72 @@ static ssize_t path_getxattr(const char __user *pathname,
 		.kname    = &kname,
 		.flags    = 0,
 	};
+	struct filename *filename;
+	ssize_t error;
+
+	if ((at_flags & ~(AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH)) != 0)
+		return -EINVAL;
 
 	error = import_xattr_name(&kname, name);
 	if (error)
 		return error;
-	return filename_getxattr(AT_FDCWD, getname(pathname), lookup_flags, &ctx);
+
+	filename = getname_xattr(pathname, at_flags);
+	if (!filename) {
+		CLASS(fd, f)(dfd);
+		if (fd_empty(f))
+			return -EBADF;
+		return file_getxattr(fd_file(f), &ctx);
+	} else {
+		int lookup_flags = 0;
+		if (at_flags & AT_SYMLINK_NOFOLLOW)
+			lookup_flags = LOOKUP_FOLLOW;
+		return filename_getxattr(dfd, filename, lookup_flags, &ctx);
+	}
+}
+
+SYSCALL_DEFINE6(getxattrat, int, dfd, const char __user *, pathname, unsigned int, at_flags,
+		const char __user *, name, struct xattr_args __user *, uargs, size_t, usize)
+{
+	struct xattr_args args = {};
+	int error;
+
+	BUILD_BUG_ON(sizeof(struct xattr_args) < XATTR_ARGS_SIZE_VER0);
+	BUILD_BUG_ON(sizeof(struct xattr_args) != XATTR_ARGS_SIZE_LATEST);
+
+	if (unlikely(usize < XATTR_ARGS_SIZE_VER0))
+		return -EINVAL;
+	if (usize > PAGE_SIZE)
+		return -E2BIG;
+
+	error = copy_struct_from_user(&args, sizeof(args), uargs, usize);
+	if (error)
+		return error;
+
+	if (args.flags != 0)
+		return -EINVAL;
+
+	return path_getxattrat(dfd, 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_getxattr(pathname, name, value, size, LOOKUP_FOLLOW);
+	return path_getxattrat(AT_FDCWD, pathname, 0, name, value, size);
 }
 
 SYSCALL_DEFINE4(lgetxattr, const char __user *, pathname,
 		const char __user *, name, void __user *, value, size_t, size)
 {
-	return path_getxattr(pathname, name, value, size, 0);
+	return path_getxattrat(AT_FDCWD, pathname, AT_SYMLINK_NOFOLLOW, name,
+			       value, size);
 }
 
 SYSCALL_DEFINE4(fgetxattr, int, fd, const char __user *, name,
 		void __user *, value, size_t, size)
 {
-	ssize_t error;
-	struct xattr_name kname;
-	struct kernel_xattr_ctx ctx = {
-		.value    = value,
-		.size     = size,
-		.kname    = &kname,
-		.flags    = 0,
-	};
-	CLASS(fd, f)(fd);
-
-	if (fd_empty(f))
-		return -EBADF;
-	error = import_xattr_name(&kname, name);
-	if (error)
-		return error;
-	return file_getxattr(fd_file(f), &ctx);
+	return path_getxattrat(fd, NULL, AT_EMPTY_PATH, name, value, size);
 }
 
 /*
@@ -915,32 +989,50 @@ ssize_t filename_listxattr(int dfd, struct filename *filename,
 	return error;
 }
 
-static ssize_t path_listxattr(const char __user *pathname, char __user *list,
-			      size_t size, unsigned int lookup_flags)
+static ssize_t path_listxattrat(int dfd, const char __user *pathname,
+				unsigned int at_flags, char __user *list,
+				size_t size)
+{
+	struct filename *filename;
+	int lookup_flags;
+
+	if ((at_flags & ~(AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH)) != 0)
+		return -EINVAL;
+
+	filename = getname_xattr(pathname, at_flags);
+	if (!filename) {
+		CLASS(fd, f)(dfd);
+		if (fd_empty(f))
+			return -EBADF;
+		return file_listxattr(fd_file(f), list, size);
+	}
+
+	lookup_flags = (at_flags & AT_SYMLINK_NOFOLLOW) ? 0 : LOOKUP_FOLLOW;
+	return filename_listxattr(dfd, filename, lookup_flags, list, size);
+}
+
+SYSCALL_DEFINE5(listxattrat, int, dfd, const char __user *, pathname,
+		unsigned int, at_flags,
+		char __user *, list, size_t, size)
 {
-	return filename_listxattr(AT_FDCWD, getname(pathname), lookup_flags,
-				  list, size);
+	return path_listxattrat(dfd, pathname, at_flags, list, size);
 }
 
 SYSCALL_DEFINE3(listxattr, const char __user *, pathname, char __user *, list,
 		size_t, size)
 {
-	return path_listxattr(pathname, list, size, LOOKUP_FOLLOW);
+	return path_listxattrat(AT_FDCWD, pathname, 0, list, size);
 }
 
 SYSCALL_DEFINE3(llistxattr, const char __user *, pathname, char __user *, list,
 		size_t, size)
 {
-	return path_listxattr(pathname, list, size, 0);
+	return path_listxattrat(AT_FDCWD, pathname, AT_SYMLINK_NOFOLLOW, list, size);
 }
 
 SYSCALL_DEFINE3(flistxattr, int, fd, char __user *, list, size_t, size)
 {
-	CLASS(fd, f)(fd);
-
-	if (fd_empty(f))
-		return -EBADF;
-	return file_listxattr(fd_file(f), list, size);
+	return path_listxattrat(fd, NULL, AT_EMPTY_PATH, list, size);
 }
 
 /*
@@ -992,44 +1084,53 @@ static int filename_removexattr(int dfd, struct filename *filename,
 	return error;
 }
 
-static int path_removexattr(const char __user *pathname,
-			    const char __user *name, unsigned int lookup_flags)
+static int path_removexattrat(int dfd, const char __user *pathname,
+			      unsigned int at_flags, const char __user *name)
 {
 	struct xattr_name kname;
+	struct filename *filename;
+	unsigned int lookup_flags;
 	int error;
 
+	if ((at_flags & ~(AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH)) != 0)
+		return -EINVAL;
+
 	error = import_xattr_name(&kname, name);
 	if (error)
 		return error;
-	return filename_removexattr(AT_FDCWD, getname(pathname), lookup_flags,
-				    &kname);
+
+	filename = getname_xattr(pathname, at_flags);
+	if (!filename) {
+		CLASS(fd, f)(dfd);
+		if (fd_empty(f))
+			return -EBADF;
+		return file_removexattr(fd_file(f), &kname);
+	}
+	lookup_flags = (at_flags & AT_SYMLINK_NOFOLLOW) ? 0 : LOOKUP_FOLLOW;
+	return filename_removexattr(dfd, filename, lookup_flags, &kname);
+}
+
+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);
 }
 
 SYSCALL_DEFINE2(removexattr, const char __user *, pathname,
 		const char __user *, name)
 {
-	return path_removexattr(pathname, name, LOOKUP_FOLLOW);
+	return path_removexattrat(AT_FDCWD, pathname, 0, name);
 }
 
 SYSCALL_DEFINE2(lremovexattr, const char __user *, pathname,
 		const char __user *, name)
 {
-	return path_removexattr(pathname, name, 0);
+	return path_removexattrat(AT_FDCWD, pathname, AT_SYMLINK_NOFOLLOW, name);
 }
 
 SYSCALL_DEFINE2(fremovexattr, int, fd, const char __user *, name)
 {
-	CLASS(fd, f)(fd);
-	struct xattr_name kname;
-	int error;
-
-	if (fd_empty(f))
-		return -EBADF;
-
-	error = import_xattr_name(&kname, name);
-	if (error)
-		return error;
-	return file_removexattr(fd_file(f), &kname);
+	return path_removexattrat(fd, NULL, AT_EMPTY_PATH, name);
 }
 
 int xattr_list_one(char **buffer, ssize_t *remaining_size, const char *name)
diff --git a/include/asm-generic/audit_change_attr.h b/include/asm-generic/audit_change_attr.h
index 331670807cf0..cc840537885f 100644
--- a/include/asm-generic/audit_change_attr.h
+++ b/include/asm-generic/audit_change_attr.h
@@ -11,9 +11,15 @@ __NR_lchown,
 __NR_fchown,
 #endif
 __NR_setxattr,
+#ifdef __NR_setxattrat
+__NR_setxattrat,
+#endif
 __NR_lsetxattr,
 __NR_fsetxattr,
 __NR_removexattr,
+#ifdef __NR_removexattrat
+__NR_removexattrat,
+#endif
 __NR_lremovexattr,
 __NR_fremovexattr,
 #ifdef __NR_fchownat
diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h
index 5758104921e6..c6333204d451 100644
--- a/include/linux/syscalls.h
+++ b/include/linux/syscalls.h
@@ -77,6 +77,7 @@ struct cachestat_range;
 struct cachestat;
 struct statmount;
 struct mnt_id_req;
+struct xattr_args;
 
 #include <linux/types.h>
 #include <linux/aio_abi.h>
@@ -338,23 +339,35 @@ asmlinkage long sys_io_uring_register(unsigned int fd, unsigned int op,
 				void __user *arg, unsigned int nr_args);
 asmlinkage long sys_setxattr(const char __user *path, const char __user *name,
 			     const void __user *value, size_t size, int flags);
+asmlinkage long sys_setxattrat(int dfd, const char __user *path, unsigned int at_flags,
+			       const char __user *name,
+			       const struct xattr_args __user *args, size_t size);
 asmlinkage long sys_lsetxattr(const char __user *path, const char __user *name,
 			      const void __user *value, size_t size, int flags);
 asmlinkage long sys_fsetxattr(int fd, const char __user *name,
 			      const void __user *value, size_t size, int flags);
 asmlinkage long sys_getxattr(const char __user *path, const char __user *name,
 			     void __user *value, size_t size);
+asmlinkage long sys_getxattrat(int dfd, const char __user *path, unsigned int at_flags,
+			       const char __user *name,
+			       struct xattr_args __user *args, size_t size);
 asmlinkage long sys_lgetxattr(const char __user *path, const char __user *name,
 			      void __user *value, size_t size);
 asmlinkage long sys_fgetxattr(int fd, const char __user *name,
 			      void __user *value, size_t size);
 asmlinkage long sys_listxattr(const char __user *path, char __user *list,
 			      size_t size);
+asmlinkage long sys_listxattrat(int dfd, const char __user *path,
+				unsigned int at_flags,
+				char __user *list, size_t size);
 asmlinkage long sys_llistxattr(const char __user *path, char __user *list,
 			       size_t size);
 asmlinkage long sys_flistxattr(int fd, char __user *list, size_t size);
 asmlinkage long sys_removexattr(const char __user *path,
 				const char __user *name);
+asmlinkage long sys_removexattrat(int dfd, const char __user *path,
+				  unsigned int at_flags,
+				  const char __user *name);
 asmlinkage long sys_lremovexattr(const char __user *path,
 				 const char __user *name);
 asmlinkage long sys_fremovexattr(int fd, const char __user *name);
diff --git a/include/linux/xattr.h b/include/linux/xattr.h
index d20051865800..86b0d47984a1 100644
--- a/include/linux/xattr.h
+++ b/include/linux/xattr.h
@@ -19,6 +19,10 @@
 #include <linux/user_namespace.h>
 #include <uapi/linux/xattr.h>
 
+/* List of all open_how "versions". */
+#define XATTR_ARGS_SIZE_VER0	16 /* sizeof first published struct */
+#define XATTR_ARGS_SIZE_LATEST	XATTR_ARGS_SIZE_VER0
+
 struct inode;
 struct dentry;
 
diff --git a/include/uapi/asm-generic/unistd.h b/include/uapi/asm-generic/unistd.h
index 5bf6148cac2b..88dc393c2bca 100644
--- a/include/uapi/asm-generic/unistd.h
+++ b/include/uapi/asm-generic/unistd.h
@@ -841,8 +841,17 @@ __SYSCALL(__NR_lsm_list_modules, sys_lsm_list_modules)
 #define __NR_mseal 462
 __SYSCALL(__NR_mseal, sys_mseal)
 
+#define __NR_setxattrat 463
+__SYSCALL(__NR_setxattrat, sys_setxattrat)
+#define __NR_getxattrat 464
+__SYSCALL(__NR_getxattrat, sys_getxattrat)
+#define __NR_listxattrat 465
+__SYSCALL(__NR_listxattrat, sys_listxattrat)
+#define __NR_removexattrat 466
+__SYSCALL(__NR_removexattrat, sys_removexattrat)
+
 #undef __NR_syscalls
-#define __NR_syscalls 463
+#define __NR_syscalls 467
 
 /*
  * 32 bit systems traditionally used different
diff --git a/include/uapi/linux/xattr.h b/include/uapi/linux/xattr.h
index 9463db2dfa9d..9854f9cff3c6 100644
--- a/include/uapi/linux/xattr.h
+++ b/include/uapi/linux/xattr.h
@@ -11,6 +11,7 @@
 */
 
 #include <linux/libc-compat.h>
+#include <linux/types.h>
 
 #ifndef _UAPI_LINUX_XATTR_H
 #define _UAPI_LINUX_XATTR_H
@@ -20,6 +21,12 @@
 
 #define XATTR_CREATE	0x1	/* set value, fail if attr already exists */
 #define XATTR_REPLACE	0x2	/* set value, fail if attr does not exist */
+
+struct xattr_args {
+	__aligned_u64 __user value;
+	__u32 size;
+	__u32 flags;
+};
 #endif
 
 /* Namespaces */
-- 
2.39.5


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* Re: [PATCH 5/9] replace do_setxattr() with saner helpers.
  2024-10-02  1:22   ` [PATCH 5/9] replace do_setxattr() with saner helpers Al Viro
@ 2024-10-02  1:34     ` Jens Axboe
  2024-10-02  2:08       ` Al Viro
  0 siblings, 1 reply; 50+ messages in thread
From: Jens Axboe @ 2024-10-02  1:34 UTC (permalink / raw)
  To: Al Viro, linux-fsdevel; +Cc: brauner, io-uring, cgzones

On 10/1/24 7:22 PM, Al Viro wrote:
> diff --git a/io_uring/xattr.c b/io_uring/xattr.c
> index 71d9e2569a2f..7f6bbfd846b9 100644
> --- a/io_uring/xattr.c
> +++ b/io_uring/xattr.c
>  int io_fsetxattr(struct io_kiocb *req, unsigned int issue_flags)
>  {
> +	struct io_xattr *ix = io_kiocb_to_cmd(req, struct io_xattr);
>  	int ret;
>  
>  	WARN_ON_ONCE(issue_flags & IO_URING_F_NONBLOCK);
>  
> -	ret = __io_setxattr(req, issue_flags, &req->file->f_path);
> +	ret = file_setxattr(req->file, &ix->ctx);
>  	io_xattr_finish(req, ret);
>  	return IOU_OK;

This and ... ->

> -retry:
> -	ret = filename_lookup(AT_FDCWD, ix->filename, lookup_flags, &path, NULL);
> -	if (!ret) {
> -		ret = __io_setxattr(req, issue_flags, &path);
> -		path_put(&path);
> -		if (retry_estale(ret, lookup_flags)) {
> -			lookup_flags |= LOOKUP_REVAL;
> -			goto retry;
> -		}
> -	}
> -
> +	ret = filename_setxattr(AT_FDCWD, ix->filename, LOOKUP_FOLLOW, &ix->ctx);
>  	io_xattr_finish(req, ret);
>  	return IOU_OK;

this looks like it needs an ix->filename = NULL, as
filename_{s,g}xattr() drops the reference. The previous internal helper
did not, and hence the cleanup always did it. But should work fine if
->filename is just zeroed.

Otherwise looks good. I've skimmed the other patches and didn't see
anything odd, I'll take a closer look tomorrow.

-- 
Jens Axboe

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 5/9] replace do_setxattr() with saner helpers.
  2024-10-02  1:34     ` Jens Axboe
@ 2024-10-02  2:08       ` Al Viro
  2024-10-02 18:00         ` Jens Axboe
  0 siblings, 1 reply; 50+ messages in thread
From: Al Viro @ 2024-10-02  2:08 UTC (permalink / raw)
  To: Jens Axboe; +Cc: linux-fsdevel, brauner, io-uring, cgzones

On Tue, Oct 01, 2024 at 07:34:12PM -0600, Jens Axboe wrote:

> > -retry:
> > -	ret = filename_lookup(AT_FDCWD, ix->filename, lookup_flags, &path, NULL);
> > -	if (!ret) {
> > -		ret = __io_setxattr(req, issue_flags, &path);
> > -		path_put(&path);
> > -		if (retry_estale(ret, lookup_flags)) {
> > -			lookup_flags |= LOOKUP_REVAL;
> > -			goto retry;
> > -		}
> > -	}
> > -
> > +	ret = filename_setxattr(AT_FDCWD, ix->filename, LOOKUP_FOLLOW, &ix->ctx);
> >  	io_xattr_finish(req, ret);
> >  	return IOU_OK;
> 
> this looks like it needs an ix->filename = NULL, as
> filename_{s,g}xattr() drops the reference. The previous internal helper
> did not, and hence the cleanup always did it. But should work fine if
> ->filename is just zeroed.
> 
> Otherwise looks good. I've skimmed the other patches and didn't see
> anything odd, I'll take a closer look tomorrow.

Hmm...  I wonder if we would be better off with file{,name}_setxattr()
doing kvfree(cxt->kvalue) - it makes things easier both on the syscall
and on io_uring side.

I've added minimal fixes (zeroing ix->filename after filename_[sg]etxattr())
to 5/9 and 6/9 *and* added a followup calling conventions change at the end
of the branch.  See #work.xattr2 in the same tree; FWIW, the followup
cleanup is below; note that putname(ERR_PTR(-Ewhatever)) is an explicit
no-op, so there's no need to zero on getname() failures.

commit 67896be9ac99b3fdef82708dd06e720332c13cdc
Author: Al Viro <[email protected]>
Date:   Tue Oct 1 22:03:16 2024 -0400

    saner calling conventions for file{,name}_setxattr()
    
    Have them consume ctx->kvalue.  That simplifies both the path_setxattrat()
    and io_uring side of things - there io_xattr_finish() is just left to
    free the xattr name and that's it.
    
    Signed-off-by: Al Viro <[email protected]>

diff --git a/fs/xattr.c b/fs/xattr.c
index 59cdb524412e..6bb656941bce 100644
--- a/fs/xattr.c
+++ b/fs/xattr.c
@@ -672,6 +672,7 @@ int file_setxattr(struct file *f, struct kernel_xattr_ctx *ctx)
 		error = do_setxattr(file_mnt_idmap(f), f->f_path.dentry, ctx);
 		mnt_drop_write_file(f);
 	}
+	kvfree(ctx->kvalue);
 	return error;
 }
 
@@ -697,6 +698,7 @@ int filename_setxattr(int dfd, struct filename *filename,
 	}
 
 out:
+	kvfree(ctx->kvalue);
 	putname(filename);
 	return error;
 }
@@ -731,14 +733,10 @@ static int path_setxattrat(int dfd, const char __user *pathname,
 	if (!filename) {
 		CLASS(fd, f)(dfd);
 		if (fd_empty(f))
-			error = -EBADF;
-		else
-			error = file_setxattr(fd_file(f), &ctx);
-	} else {
-		error = filename_setxattr(dfd, filename, lookup_flags, &ctx);
+			return -EBADF;
+		return file_setxattr(fd_file(f), &ctx);
 	}
-	kvfree(ctx.kvalue);
-	return error;
+	return filename_setxattr(dfd, filename, lookup_flags, &ctx);
 }
 
 SYSCALL_DEFINE6(setxattrat, int, dfd, const char __user *, pathname, unsigned int, at_flags,
diff --git a/io_uring/xattr.c b/io_uring/xattr.c
index 90277246dbea..9f3ea12f628d 100644
--- a/io_uring/xattr.c
+++ b/io_uring/xattr.c
@@ -35,9 +35,10 @@ void io_xattr_cleanup(struct io_kiocb *req)
 
 static void io_xattr_finish(struct io_kiocb *req, int ret)
 {
-	req->flags &= ~REQ_F_NEED_CLEANUP;
+	struct io_xattr *ix = io_kiocb_to_cmd(req, struct io_xattr);
 
-	io_xattr_cleanup(req);
+	kfree(ix->ctx.kname);
+	req->flags &= ~REQ_F_NEED_CLEANUP;
 	io_req_set_res(req, ret, 0);
 }
 
@@ -94,12 +95,9 @@ int io_getxattr_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
 	path = u64_to_user_ptr(READ_ONCE(sqe->addr3));
 
 	ix->filename = getname(path);
-	if (IS_ERR(ix->filename)) {
-		ret = PTR_ERR(ix->filename);
-		ix->filename = NULL;
-	}
-
-	return ret;
+	if (IS_ERR(ix->filename))
+		return PTR_ERR(ix->filename);
+	return 0;
 }
 
 int io_fgetxattr(struct io_kiocb *req, unsigned int issue_flags)
@@ -122,7 +120,6 @@ int io_getxattr(struct io_kiocb *req, unsigned int issue_flags)
 	WARN_ON_ONCE(issue_flags & IO_URING_F_NONBLOCK);
 
 	ret = filename_getxattr(AT_FDCWD, ix->filename, LOOKUP_FOLLOW, &ix->ctx);
-	ix->filename = NULL;
 	io_xattr_finish(req, ret);
 	return IOU_OK;
 }
@@ -172,12 +169,9 @@ int io_setxattr_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
 	path = u64_to_user_ptr(READ_ONCE(sqe->addr3));
 
 	ix->filename = getname(path);
-	if (IS_ERR(ix->filename)) {
-		ret = PTR_ERR(ix->filename);
-		ix->filename = NULL;
-	}
-
-	return ret;
+	if (IS_ERR(ix->filename))
+		return PTR_ERR(ix->filename);
+	return 0;
 }
 
 int io_fsetxattr_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
@@ -205,7 +199,6 @@ int io_setxattr(struct io_kiocb *req, unsigned int issue_flags)
 	WARN_ON_ONCE(issue_flags & IO_URING_F_NONBLOCK);
 
 	ret = filename_setxattr(AT_FDCWD, ix->filename, LOOKUP_FOLLOW, &ix->ctx);
-	ix->filename = NULL;
 	io_xattr_finish(req, ret);
 	return IOU_OK;
 }

^ permalink raw reply related	[flat|nested] 50+ messages in thread

* Re: [PATCH 1/9] xattr: switch to CLASS(fd)
  2024-10-02  1:22 ` [PATCH 1/9] xattr: switch to CLASS(fd) Al Viro
                     ` (7 preceding siblings ...)
  2024-10-02  1:22   ` [PATCH 9/9] fs/xattr: add *at family syscalls Al Viro
@ 2024-10-02  5:56   ` Christian Brauner
  8 siblings, 0 replies; 50+ messages in thread
From: Christian Brauner @ 2024-10-02  5:56 UTC (permalink / raw)
  To: Al Viro; +Cc: linux-fsdevel, io-uring, cgzones

On Wed, Oct 02, 2024 at 02:22:22AM GMT, Al Viro wrote:
> Signed-off-by: Al Viro <[email protected]>
> ---

Reviewed-by: Christian Brauner <[email protected]>

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 2/9] fs: rename struct xattr_ctx to kernel_xattr_ctx
  2024-10-02  1:22   ` [PATCH 2/9] fs: rename struct xattr_ctx to kernel_xattr_ctx Al Viro
@ 2024-10-02  5:56     ` Christian Brauner
  0 siblings, 0 replies; 50+ messages in thread
From: Christian Brauner @ 2024-10-02  5:56 UTC (permalink / raw)
  To: Al Viro; +Cc: linux-fsdevel, io-uring, cgzones

On Wed, Oct 02, 2024 at 02:22:23AM GMT, Al Viro wrote:
> From: Christian Göttsche <[email protected]>
> 
> Rename the struct xattr_ctx to increase distinction with the about to be
> added user API struct xattr_args.
> 
> No functional change.
> 
> Suggested-by: Christian Brauner <[email protected]>
> Signed-off-by: Christian Göttsche <[email protected]>
> Link: https://lore.kernel.org/r/[email protected]
> Reviewed-by: Jan Kara <[email protected]>
> Signed-off-by: Christian Brauner <[email protected]>
> ---

Reviewed-by: Christian Brauner <[email protected]>

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 3/9] io_[gs]etxattr_prep(): just use getname()
  2024-10-02  1:22   ` [PATCH 3/9] io_[gs]etxattr_prep(): just use getname() Al Viro
@ 2024-10-02  5:57     ` Christian Brauner
  0 siblings, 0 replies; 50+ messages in thread
From: Christian Brauner @ 2024-10-02  5:57 UTC (permalink / raw)
  To: Al Viro; +Cc: linux-fsdevel, io-uring, cgzones

On Wed, Oct 02, 2024 at 02:22:24AM GMT, Al Viro wrote:
> getname_flags(pathname, LOOKUP_FOLLOW) is obviously bogus - following
> trailing symlinks has no impact on how to copy the pathname from userland...
> 
> Signed-off-by: Al Viro <[email protected]>
> ---

Reviewed-by: Christian Brauner <[email protected]>

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 4/9] new helper: import_xattr_name()
  2024-10-02  1:22   ` [PATCH 4/9] new helper: import_xattr_name() Al Viro
@ 2024-10-02  5:57     ` Christian Brauner
  0 siblings, 0 replies; 50+ messages in thread
From: Christian Brauner @ 2024-10-02  5:57 UTC (permalink / raw)
  To: Al Viro; +Cc: linux-fsdevel, io-uring, cgzones

On Wed, Oct 02, 2024 at 02:22:25AM GMT, Al Viro wrote:
> common logics for marshalling xattr names.
> 
> Signed-off-by: Al Viro <[email protected]>
> ---

Reviewed-by: Christian Brauner <[email protected]>

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 6/9] replace do_getxattr() with saner helpers.
  2024-10-02  1:22   ` [PATCH 6/9] replace do_getxattr() " Al Viro
@ 2024-10-02  5:59     ` Christian Brauner
  0 siblings, 0 replies; 50+ messages in thread
From: Christian Brauner @ 2024-10-02  5:59 UTC (permalink / raw)
  To: Al Viro; +Cc: linux-fsdevel, io-uring, cgzones

On Wed, Oct 02, 2024 at 02:22:27AM GMT, Al Viro wrote:
> similar to do_setxattr() in the previous commit...
> 
> Signed-off-by: Al Viro <[email protected]>
> ---

Reviewed-by: Christian Brauner <[email protected]>

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 7/9] new helpers: file_listxattr(), filename_listxattr()
  2024-10-02  1:22   ` [PATCH 7/9] new helpers: file_listxattr(), filename_listxattr() Al Viro
@ 2024-10-02  6:00     ` Christian Brauner
  0 siblings, 0 replies; 50+ messages in thread
From: Christian Brauner @ 2024-10-02  6:00 UTC (permalink / raw)
  To: Al Viro; +Cc: linux-fsdevel, io-uring, cgzones

On Wed, Oct 02, 2024 at 02:22:28AM GMT, Al Viro wrote:
> switch path_listxattr() and flistxattr(2) to those
> 
> Signed-off-by: Al Viro <[email protected]>
> ---

Reviewed-by: Christian Brauner <[email protected]>

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 8/9] new helpers: file_removexattr(), filename_removexattr()
  2024-10-02  1:22   ` [PATCH 8/9] new helpers: file_removexattr(), filename_removexattr() Al Viro
@ 2024-10-02  6:01     ` Christian Brauner
  0 siblings, 0 replies; 50+ messages in thread
From: Christian Brauner @ 2024-10-02  6:01 UTC (permalink / raw)
  To: Al Viro; +Cc: linux-fsdevel, io-uring, cgzones

On Wed, Oct 02, 2024 at 02:22:29AM GMT, Al Viro wrote:
> switch path_removexattrat() and fremovexattr(2) to those
> 
> Signed-off-by: Al Viro <[email protected]>
> ---

Reviewed-by: Christian Brauner <[email protected]>

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 9/9] fs/xattr: add *at family syscalls
  2024-10-02  1:22   ` [PATCH 9/9] fs/xattr: add *at family syscalls Al Viro
@ 2024-10-02  6:03     ` Christian Brauner
  0 siblings, 0 replies; 50+ messages in thread
From: Christian Brauner @ 2024-10-02  6:03 UTC (permalink / raw)
  To: Al Viro; +Cc: linux-fsdevel, io-uring, cgzones

On Wed, Oct 02, 2024 at 02:22:30AM GMT, Al Viro wrote:
> From: Christian Göttsche <[email protected]>
> 
> Add the four syscalls setxattrat(), getxattrat(), listxattrat() and
> removexattrat().  Those can be used to operate on extended attributes,
> especially security related ones, either relative to a pinned directory
> or on a file descriptor without read access, avoiding a
> /proc/<pid>/fd/<fd> detour, requiring a mounted procfs.
> 
> One use case will be setfiles(8) setting SELinux file contexts
> ("security.selinux") without race conditions and without a file
> descriptor opened with read access requiring SELinux read permission.
> 
> Use the do_{name}at() pattern from fs/open.c.
> 
> Pass the value of the extended attribute, its length, and for
> setxattrat(2) the command (XATTR_CREATE or XATTR_REPLACE) via an added
> struct xattr_args to not exceed six syscall arguments and not
> merging the AT_* and XATTR_* flags.
> 
> [AV: fixes by Christian Brauner folded in, the entire thing rebased on
> top of {filename,file}_...xattr() primitives, treatment of empty
> pathnames regularized.  As the result, AT_EMPTY_PATH+NULL handling
> is cheap, so f...(2) can use it]
> 
> Signed-off-by: Christian Göttsche <[email protected]>
> Link: https://lore.kernel.org/r/[email protected]
> Reviewed-by: Arnd Bergmann <[email protected]>
> CC: [email protected]
> CC: [email protected]
> CC: [email protected]
> CC: [email protected]
> CC: [email protected]
> CC: [email protected]
> CC: [email protected]
> CC: [email protected]
> CC: [email protected]
> CC: [email protected]
> CC: [email protected]
> CC: [email protected]
> CC: [email protected]
> CC: [email protected]
> CC: [email protected]
> CC: [email protected]
> CC: [email protected]
> CC: [email protected]
> [brauner: slight tweaks]
> Signed-off-by: Christian Brauner <[email protected]>
> ---

Reviewed-by: Christian Brauner <[email protected]>

>  arch/alpha/kernel/syscalls/syscall.tbl      |   4 +
>  arch/arm/tools/syscall.tbl                  |   4 +
>  arch/m68k/kernel/syscalls/syscall.tbl       |   4 +
>  arch/microblaze/kernel/syscalls/syscall.tbl |   4 +
>  arch/mips/kernel/syscalls/syscall_n32.tbl   |   4 +
>  arch/mips/kernel/syscalls/syscall_n64.tbl   |   4 +
>  arch/mips/kernel/syscalls/syscall_o32.tbl   |   4 +
>  arch/parisc/kernel/syscalls/syscall.tbl     |   4 +
>  arch/powerpc/kernel/syscalls/syscall.tbl    |   4 +
>  arch/s390/kernel/syscalls/syscall.tbl       |   4 +
>  arch/sh/kernel/syscalls/syscall.tbl         |   4 +
>  arch/sparc/kernel/syscalls/syscall.tbl      |   4 +
>  arch/x86/entry/syscalls/syscall_32.tbl      |   4 +
>  arch/x86/entry/syscalls/syscall_64.tbl      |   4 +
>  arch/xtensa/kernel/syscalls/syscall.tbl     |   4 +
>  fs/xattr.c                                  | 271 ++++++++++++++------
>  include/asm-generic/audit_change_attr.h     |   6 +
>  include/linux/syscalls.h                    |  13 +
>  include/linux/xattr.h                       |   4 +
>  include/uapi/asm-generic/unistd.h           |  11 +-
>  include/uapi/linux/xattr.h                  |   7 +
>  21 files changed, 286 insertions(+), 86 deletions(-)
> 
> diff --git a/arch/alpha/kernel/syscalls/syscall.tbl b/arch/alpha/kernel/syscalls/syscall.tbl
> index 74720667fe09..c59d53d6d3f3 100644
> --- a/arch/alpha/kernel/syscalls/syscall.tbl
> +++ b/arch/alpha/kernel/syscalls/syscall.tbl
> @@ -502,3 +502,7 @@
>  570	common	lsm_set_self_attr		sys_lsm_set_self_attr
>  571	common	lsm_list_modules		sys_lsm_list_modules
>  572	common  mseal				sys_mseal
> +573	common	setxattrat			sys_setxattrat
> +574	common	getxattrat			sys_getxattrat
> +575	common	listxattrat			sys_listxattrat
> +576	common	removexattrat			sys_removexattrat
> diff --git a/arch/arm/tools/syscall.tbl b/arch/arm/tools/syscall.tbl
> index 23c98203c40f..49eeb2ad8dbd 100644
> --- a/arch/arm/tools/syscall.tbl
> +++ b/arch/arm/tools/syscall.tbl
> @@ -477,3 +477,7 @@
>  460	common	lsm_set_self_attr		sys_lsm_set_self_attr
>  461	common	lsm_list_modules		sys_lsm_list_modules
>  462	common	mseal				sys_mseal
> +463	common	setxattrat			sys_setxattrat
> +464	common	getxattrat			sys_getxattrat
> +465	common	listxattrat			sys_listxattrat
> +466	common	removexattrat			sys_removexattrat
> diff --git a/arch/m68k/kernel/syscalls/syscall.tbl b/arch/m68k/kernel/syscalls/syscall.tbl
> index 22a3cbd4c602..f5ed71f1910d 100644
> --- a/arch/m68k/kernel/syscalls/syscall.tbl
> +++ b/arch/m68k/kernel/syscalls/syscall.tbl
> @@ -462,3 +462,7 @@
>  460	common	lsm_set_self_attr		sys_lsm_set_self_attr
>  461	common	lsm_list_modules		sys_lsm_list_modules
>  462	common	mseal				sys_mseal
> +463	common	setxattrat			sys_setxattrat
> +464	common	getxattrat			sys_getxattrat
> +465	common	listxattrat			sys_listxattrat
> +466	common	removexattrat			sys_removexattrat
> diff --git a/arch/microblaze/kernel/syscalls/syscall.tbl b/arch/microblaze/kernel/syscalls/syscall.tbl
> index 2b81a6bd78b2..680f568b77f2 100644
> --- a/arch/microblaze/kernel/syscalls/syscall.tbl
> +++ b/arch/microblaze/kernel/syscalls/syscall.tbl
> @@ -468,3 +468,7 @@
>  460	common	lsm_set_self_attr		sys_lsm_set_self_attr
>  461	common	lsm_list_modules		sys_lsm_list_modules
>  462	common	mseal				sys_mseal
> +463	common	setxattrat			sys_setxattrat
> +464	common	getxattrat			sys_getxattrat
> +465	common	listxattrat			sys_listxattrat
> +466	common	removexattrat			sys_removexattrat
> diff --git a/arch/mips/kernel/syscalls/syscall_n32.tbl b/arch/mips/kernel/syscalls/syscall_n32.tbl
> index 953f5b7dc723..0b9b7e25b69a 100644
> --- a/arch/mips/kernel/syscalls/syscall_n32.tbl
> +++ b/arch/mips/kernel/syscalls/syscall_n32.tbl
> @@ -401,3 +401,7 @@
>  460	n32	lsm_set_self_attr		sys_lsm_set_self_attr
>  461	n32	lsm_list_modules		sys_lsm_list_modules
>  462	n32	mseal				sys_mseal
> +463	n32	setxattrat			sys_setxattrat
> +464	n32	getxattrat			sys_getxattrat
> +465	n32	listxattrat			sys_listxattrat
> +466	n32	removexattrat			sys_removexattrat
> diff --git a/arch/mips/kernel/syscalls/syscall_n64.tbl b/arch/mips/kernel/syscalls/syscall_n64.tbl
> index 1464c6be6eb3..c844cd5cda62 100644
> --- a/arch/mips/kernel/syscalls/syscall_n64.tbl
> +++ b/arch/mips/kernel/syscalls/syscall_n64.tbl
> @@ -377,3 +377,7 @@
>  460	n64	lsm_set_self_attr		sys_lsm_set_self_attr
>  461	n64	lsm_list_modules		sys_lsm_list_modules
>  462	n64	mseal				sys_mseal
> +463	n64	setxattrat			sys_setxattrat
> +464	n64	getxattrat			sys_getxattrat
> +465	n64	listxattrat			sys_listxattrat
> +466	n64	removexattrat			sys_removexattrat
> diff --git a/arch/mips/kernel/syscalls/syscall_o32.tbl b/arch/mips/kernel/syscalls/syscall_o32.tbl
> index 2439a2491cff..349b8aad1159 100644
> --- a/arch/mips/kernel/syscalls/syscall_o32.tbl
> +++ b/arch/mips/kernel/syscalls/syscall_o32.tbl
> @@ -450,3 +450,7 @@
>  460	o32	lsm_set_self_attr		sys_lsm_set_self_attr
>  461	o32	lsm_list_modules		sys_lsm_list_modules
>  462	o32	mseal				sys_mseal
> +463	o32	setxattrat			sys_setxattrat
> +464	o32	getxattrat			sys_getxattrat
> +465	o32	listxattrat			sys_listxattrat
> +466	o32	removexattrat			sys_removexattrat
> diff --git a/arch/parisc/kernel/syscalls/syscall.tbl b/arch/parisc/kernel/syscalls/syscall.tbl
> index 66dc406b12e4..d9fc94c86965 100644
> --- a/arch/parisc/kernel/syscalls/syscall.tbl
> +++ b/arch/parisc/kernel/syscalls/syscall.tbl
> @@ -461,3 +461,7 @@
>  460	common	lsm_set_self_attr		sys_lsm_set_self_attr
>  461	common	lsm_list_modules		sys_lsm_list_modules
>  462	common	mseal				sys_mseal
> +463	common	setxattrat			sys_setxattrat
> +464	common	getxattrat			sys_getxattrat
> +465	common	listxattrat			sys_listxattrat
> +466	common	removexattrat			sys_removexattrat
> diff --git a/arch/powerpc/kernel/syscalls/syscall.tbl b/arch/powerpc/kernel/syscalls/syscall.tbl
> index ebae8415dfbb..d8b4ab78bef0 100644
> --- a/arch/powerpc/kernel/syscalls/syscall.tbl
> +++ b/arch/powerpc/kernel/syscalls/syscall.tbl
> @@ -553,3 +553,7 @@
>  460	common	lsm_set_self_attr		sys_lsm_set_self_attr
>  461	common	lsm_list_modules		sys_lsm_list_modules
>  462	common	mseal				sys_mseal
> +463	common	setxattrat			sys_setxattrat
> +464	common	getxattrat			sys_getxattrat
> +465	common	listxattrat			sys_listxattrat
> +466	common	removexattrat			sys_removexattrat
> diff --git a/arch/s390/kernel/syscalls/syscall.tbl b/arch/s390/kernel/syscalls/syscall.tbl
> index 01071182763e..e9115b4d8b63 100644
> --- a/arch/s390/kernel/syscalls/syscall.tbl
> +++ b/arch/s390/kernel/syscalls/syscall.tbl
> @@ -465,3 +465,7 @@
>  460  common	lsm_set_self_attr	sys_lsm_set_self_attr		sys_lsm_set_self_attr
>  461  common	lsm_list_modules	sys_lsm_list_modules		sys_lsm_list_modules
>  462  common	mseal			sys_mseal			sys_mseal
> +463  common	setxattrat		sys_setxattrat			sys_setxattrat
> +464  common	getxattrat		sys_getxattrat			sys_getxattrat
> +465  common	listxattrat		sys_listxattrat			sys_listxattrat
> +466  common	removexattrat		sys_removexattrat		sys_removexattrat
> diff --git a/arch/sh/kernel/syscalls/syscall.tbl b/arch/sh/kernel/syscalls/syscall.tbl
> index c55fd7696d40..c8cad33bf250 100644
> --- a/arch/sh/kernel/syscalls/syscall.tbl
> +++ b/arch/sh/kernel/syscalls/syscall.tbl
> @@ -466,3 +466,7 @@
>  460	common	lsm_set_self_attr		sys_lsm_set_self_attr
>  461	common	lsm_list_modules		sys_lsm_list_modules
>  462	common	mseal				sys_mseal
> +463	common	setxattrat			sys_setxattrat
> +464	common	getxattrat			sys_getxattrat
> +465	common	listxattrat			sys_listxattrat
> +466	common	removexattrat			sys_removexattrat
> diff --git a/arch/sparc/kernel/syscalls/syscall.tbl b/arch/sparc/kernel/syscalls/syscall.tbl
> index cfdfb3707c16..727f99d333b3 100644
> --- a/arch/sparc/kernel/syscalls/syscall.tbl
> +++ b/arch/sparc/kernel/syscalls/syscall.tbl
> @@ -508,3 +508,7 @@
>  460	common	lsm_set_self_attr		sys_lsm_set_self_attr
>  461	common	lsm_list_modules		sys_lsm_list_modules
>  462	common	mseal 				sys_mseal
> +463	common	setxattrat			sys_setxattrat
> +464	common	getxattrat			sys_getxattrat
> +465	common	listxattrat			sys_listxattrat
> +466	common	removexattrat			sys_removexattrat
> diff --git a/arch/x86/entry/syscalls/syscall_32.tbl b/arch/x86/entry/syscalls/syscall_32.tbl
> index 534c74b14fab..4d0fb2fba7e2 100644
> --- a/arch/x86/entry/syscalls/syscall_32.tbl
> +++ b/arch/x86/entry/syscalls/syscall_32.tbl
> @@ -468,3 +468,7 @@
>  460	i386	lsm_set_self_attr	sys_lsm_set_self_attr
>  461	i386	lsm_list_modules	sys_lsm_list_modules
>  462	i386	mseal 			sys_mseal
> +463	i386	setxattrat		sys_setxattrat
> +464	i386	getxattrat		sys_getxattrat
> +465	i386	listxattrat		sys_listxattrat
> +466	i386	removexattrat		sys_removexattrat
> diff --git a/arch/x86/entry/syscalls/syscall_64.tbl b/arch/x86/entry/syscalls/syscall_64.tbl
> index 7093ee21c0d1..5eb708bff1c7 100644
> --- a/arch/x86/entry/syscalls/syscall_64.tbl
> +++ b/arch/x86/entry/syscalls/syscall_64.tbl
> @@ -386,6 +386,10 @@
>  460	common	lsm_set_self_attr	sys_lsm_set_self_attr
>  461	common	lsm_list_modules	sys_lsm_list_modules
>  462 	common  mseal			sys_mseal
> +463	common	setxattrat		sys_setxattrat
> +464	common	getxattrat		sys_getxattrat
> +465	common	listxattrat		sys_listxattrat
> +466	common	removexattrat		sys_removexattrat
>  
>  #
>  # Due to a historical design error, certain syscalls are numbered differently
> diff --git a/arch/xtensa/kernel/syscalls/syscall.tbl b/arch/xtensa/kernel/syscalls/syscall.tbl
> index 67083fc1b2f5..37effc1b134e 100644
> --- a/arch/xtensa/kernel/syscalls/syscall.tbl
> +++ b/arch/xtensa/kernel/syscalls/syscall.tbl
> @@ -433,3 +433,7 @@
>  460	common	lsm_set_self_attr		sys_lsm_set_self_attr
>  461	common	lsm_list_modules		sys_lsm_list_modules
>  462	common	mseal 				sys_mseal
> +463	common	setxattrat			sys_setxattrat
> +464	common	getxattrat			sys_getxattrat
> +465	common	listxattrat			sys_listxattrat
> +466	common	removexattrat			sys_removexattrat
> diff --git a/fs/xattr.c b/fs/xattr.c
> index 6f87f23c0e84..59cdb524412e 100644
> --- a/fs/xattr.c
> +++ b/fs/xattr.c
> @@ -597,6 +597,32 @@ int import_xattr_name(struct xattr_name *kname, const char __user *name)
>  	return 0;
>  }
>  
> +static struct filename *getname_xattr(const char __user *pathname,
> +				      unsigned int at_flags)
> +{
> +	struct filename *name;
> +	char c;
> +
> +	if (!(at_flags & AT_EMPTY_PATH))
> +		return getname(pathname);
> +
> +	if (!pathname)
> +		return NULL;
> +
> +	/* try to save on allocations; will suck on s390 and um, though */
> +	if (get_user(c, pathname))
> +		return ERR_PTR(-EFAULT);
> +	if (!c)
> +		return NULL;
> +
> +	name = getname_flags(pathname, LOOKUP_EMPTY);
> +	if (!IS_ERR(name) && !(name->name[0])) {
> +		putname(name);
> +		name = NULL;
> +	}
> +	return name;
> +}
> +
>  /*
>   * Extended attribute SET operations
>   */
> @@ -675,69 +701,90 @@ int filename_setxattr(int dfd, struct filename *filename,
>  	return error;
>  }
>  
> -static int path_setxattr(const char __user *pathname,
> -			 const char __user *name, const void __user *value,
> -			 size_t size, int flags, unsigned int lookup_flags)
> +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)
>  {
>  	struct xattr_name kname;
>  	struct kernel_xattr_ctx ctx = {
> -		.cvalue   = value,
> -		.kvalue   = NULL,
> -		.size     = size,
> -		.kname    = &kname,
> -		.flags    = flags,
> +		.cvalue	= value,
> +		.kvalue	= NULL,
> +		.size	= size,
> +		.kname	= &kname,
> +		.flags	= flags,
>  	};
> +	struct filename *filename;
> +	unsigned int lookup_flags = 0;
>  	int error;
>  
> +	if ((at_flags & ~(AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH)) != 0)
> +		return -EINVAL;
> +
> +	if (at_flags & AT_SYMLINK_NOFOLLOW)
> +		lookup_flags = LOOKUP_FOLLOW;
> +
>  	error = setxattr_copy(name, &ctx);
>  	if (error)
>  		return error;
>  
> -	error = filename_setxattr(AT_FDCWD, getname(pathname), lookup_flags,
> -				  &ctx);
> +	filename = getname_xattr(pathname, at_flags);
> +	if (!filename) {
> +		CLASS(fd, f)(dfd);
> +		if (fd_empty(f))
> +			error = -EBADF;
> +		else
> +			error = file_setxattr(fd_file(f), &ctx);
> +	} else {
> +		error = filename_setxattr(dfd, filename, lookup_flags, &ctx);
> +	}
>  	kvfree(ctx.kvalue);
>  	return error;
>  }
>  
> +SYSCALL_DEFINE6(setxattrat, int, dfd, const char __user *, pathname, unsigned int, at_flags,
> +		const char __user *, name, const struct xattr_args __user *, uargs,
> +		size_t, usize)
> +{
> +	struct xattr_args args = {};
> +	int error;
> +
> +	BUILD_BUG_ON(sizeof(struct xattr_args) < XATTR_ARGS_SIZE_VER0);
> +	BUILD_BUG_ON(sizeof(struct xattr_args) != XATTR_ARGS_SIZE_LATEST);
> +
> +	if (unlikely(usize < XATTR_ARGS_SIZE_VER0))
> +		return -EINVAL;
> +	if (usize > PAGE_SIZE)
> +		return -E2BIG;
> +
> +	error = copy_struct_from_user(&args, sizeof(args), uargs, usize);
> +	if (error)
> +		return error;
> +
> +	return path_setxattrat(dfd, pathname, at_flags, name,
> +			       u64_to_user_ptr(args.value), args.size,
> +			       args.flags);
> +}
> +
>  SYSCALL_DEFINE5(setxattr, const char __user *, pathname,
>  		const char __user *, name, const void __user *, value,
>  		size_t, size, int, flags)
>  {
> -	return path_setxattr(pathname, name, value, size, flags, LOOKUP_FOLLOW);
> +	return path_setxattrat(AT_FDCWD, 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_setxattr(pathname, name, value, size, flags, 0);
> +	return path_setxattrat(AT_FDCWD, 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)
>  {
> -	struct xattr_name kname;
> -	struct kernel_xattr_ctx ctx = {
> -		.cvalue   = value,
> -		.kvalue   = NULL,
> -		.size     = size,
> -		.kname    = &kname,
> -		.flags    = flags,
> -	};
> -	int error;
> -
> -	CLASS(fd, f)(fd);
> -
> -	if (fd_empty(f))
> -		return -EBADF;
> -
> -	error = setxattr_copy(name, &ctx);
> -	if (error)
> -		return error;
> -
> -	error = file_setxattr(fd_file(f), &ctx);
> -	kvfree(ctx.kvalue);
> -	return error;
> +	return path_setxattrat(fd, NULL, AT_EMPTY_PATH, name,
> +			       value, size, flags);
>  }
>  
>  /*
> @@ -802,11 +849,10 @@ ssize_t filename_getxattr(int dfd, struct filename *filename,
>  	return error;
>  }
>  
> -static ssize_t path_getxattr(const char __user *pathname,
> -			     const char __user *name, void __user *value,
> -			     size_t size, unsigned int lookup_flags)
> +static ssize_t path_getxattrat(int dfd, const char __user *pathname,
> +			       unsigned int at_flags, const char __user *name,
> +			       void __user *value, size_t size)
>  {
> -	ssize_t error;
>  	struct xattr_name kname;
>  	struct kernel_xattr_ctx ctx = {
>  		.value    = value,
> @@ -814,44 +860,72 @@ static ssize_t path_getxattr(const char __user *pathname,
>  		.kname    = &kname,
>  		.flags    = 0,
>  	};
> +	struct filename *filename;
> +	ssize_t error;
> +
> +	if ((at_flags & ~(AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH)) != 0)
> +		return -EINVAL;
>  
>  	error = import_xattr_name(&kname, name);
>  	if (error)
>  		return error;
> -	return filename_getxattr(AT_FDCWD, getname(pathname), lookup_flags, &ctx);
> +
> +	filename = getname_xattr(pathname, at_flags);
> +	if (!filename) {
> +		CLASS(fd, f)(dfd);
> +		if (fd_empty(f))
> +			return -EBADF;
> +		return file_getxattr(fd_file(f), &ctx);
> +	} else {
> +		int lookup_flags = 0;
> +		if (at_flags & AT_SYMLINK_NOFOLLOW)
> +			lookup_flags = LOOKUP_FOLLOW;
> +		return filename_getxattr(dfd, filename, lookup_flags, &ctx);
> +	}
> +}
> +
> +SYSCALL_DEFINE6(getxattrat, int, dfd, const char __user *, pathname, unsigned int, at_flags,
> +		const char __user *, name, struct xattr_args __user *, uargs, size_t, usize)
> +{
> +	struct xattr_args args = {};
> +	int error;
> +
> +	BUILD_BUG_ON(sizeof(struct xattr_args) < XATTR_ARGS_SIZE_VER0);
> +	BUILD_BUG_ON(sizeof(struct xattr_args) != XATTR_ARGS_SIZE_LATEST);
> +
> +	if (unlikely(usize < XATTR_ARGS_SIZE_VER0))
> +		return -EINVAL;
> +	if (usize > PAGE_SIZE)
> +		return -E2BIG;
> +
> +	error = copy_struct_from_user(&args, sizeof(args), uargs, usize);
> +	if (error)
> +		return error;
> +
> +	if (args.flags != 0)
> +		return -EINVAL;
> +
> +	return path_getxattrat(dfd, 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_getxattr(pathname, name, value, size, LOOKUP_FOLLOW);
> +	return path_getxattrat(AT_FDCWD, pathname, 0, name, value, size);
>  }
>  
>  SYSCALL_DEFINE4(lgetxattr, const char __user *, pathname,
>  		const char __user *, name, void __user *, value, size_t, size)
>  {
> -	return path_getxattr(pathname, name, value, size, 0);
> +	return path_getxattrat(AT_FDCWD, pathname, AT_SYMLINK_NOFOLLOW, name,
> +			       value, size);
>  }
>  
>  SYSCALL_DEFINE4(fgetxattr, int, fd, const char __user *, name,
>  		void __user *, value, size_t, size)
>  {
> -	ssize_t error;
> -	struct xattr_name kname;
> -	struct kernel_xattr_ctx ctx = {
> -		.value    = value,
> -		.size     = size,
> -		.kname    = &kname,
> -		.flags    = 0,
> -	};
> -	CLASS(fd, f)(fd);
> -
> -	if (fd_empty(f))
> -		return -EBADF;
> -	error = import_xattr_name(&kname, name);
> -	if (error)
> -		return error;
> -	return file_getxattr(fd_file(f), &ctx);
> +	return path_getxattrat(fd, NULL, AT_EMPTY_PATH, name, value, size);
>  }
>  
>  /*
> @@ -915,32 +989,50 @@ ssize_t filename_listxattr(int dfd, struct filename *filename,
>  	return error;
>  }
>  
> -static ssize_t path_listxattr(const char __user *pathname, char __user *list,
> -			      size_t size, unsigned int lookup_flags)
> +static ssize_t path_listxattrat(int dfd, const char __user *pathname,
> +				unsigned int at_flags, char __user *list,
> +				size_t size)
> +{
> +	struct filename *filename;
> +	int lookup_flags;
> +
> +	if ((at_flags & ~(AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH)) != 0)
> +		return -EINVAL;
> +
> +	filename = getname_xattr(pathname, at_flags);
> +	if (!filename) {
> +		CLASS(fd, f)(dfd);
> +		if (fd_empty(f))
> +			return -EBADF;
> +		return file_listxattr(fd_file(f), list, size);
> +	}
> +
> +	lookup_flags = (at_flags & AT_SYMLINK_NOFOLLOW) ? 0 : LOOKUP_FOLLOW;
> +	return filename_listxattr(dfd, filename, lookup_flags, list, size);
> +}
> +
> +SYSCALL_DEFINE5(listxattrat, int, dfd, const char __user *, pathname,
> +		unsigned int, at_flags,
> +		char __user *, list, size_t, size)
>  {
> -	return filename_listxattr(AT_FDCWD, getname(pathname), lookup_flags,
> -				  list, size);
> +	return path_listxattrat(dfd, pathname, at_flags, list, size);
>  }
>  
>  SYSCALL_DEFINE3(listxattr, const char __user *, pathname, char __user *, list,
>  		size_t, size)
>  {
> -	return path_listxattr(pathname, list, size, LOOKUP_FOLLOW);
> +	return path_listxattrat(AT_FDCWD, pathname, 0, list, size);
>  }
>  
>  SYSCALL_DEFINE3(llistxattr, const char __user *, pathname, char __user *, list,
>  		size_t, size)
>  {
> -	return path_listxattr(pathname, list, size, 0);
> +	return path_listxattrat(AT_FDCWD, pathname, AT_SYMLINK_NOFOLLOW, list, size);
>  }
>  
>  SYSCALL_DEFINE3(flistxattr, int, fd, char __user *, list, size_t, size)
>  {
> -	CLASS(fd, f)(fd);
> -
> -	if (fd_empty(f))
> -		return -EBADF;
> -	return file_listxattr(fd_file(f), list, size);
> +	return path_listxattrat(fd, NULL, AT_EMPTY_PATH, list, size);
>  }
>  
>  /*
> @@ -992,44 +1084,53 @@ static int filename_removexattr(int dfd, struct filename *filename,
>  	return error;
>  }
>  
> -static int path_removexattr(const char __user *pathname,
> -			    const char __user *name, unsigned int lookup_flags)
> +static int path_removexattrat(int dfd, const char __user *pathname,
> +			      unsigned int at_flags, const char __user *name)
>  {
>  	struct xattr_name kname;
> +	struct filename *filename;
> +	unsigned int lookup_flags;
>  	int error;
>  
> +	if ((at_flags & ~(AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH)) != 0)
> +		return -EINVAL;
> +
>  	error = import_xattr_name(&kname, name);
>  	if (error)
>  		return error;
> -	return filename_removexattr(AT_FDCWD, getname(pathname), lookup_flags,
> -				    &kname);
> +
> +	filename = getname_xattr(pathname, at_flags);
> +	if (!filename) {
> +		CLASS(fd, f)(dfd);
> +		if (fd_empty(f))
> +			return -EBADF;
> +		return file_removexattr(fd_file(f), &kname);
> +	}
> +	lookup_flags = (at_flags & AT_SYMLINK_NOFOLLOW) ? 0 : LOOKUP_FOLLOW;
> +	return filename_removexattr(dfd, filename, lookup_flags, &kname);
> +}
> +
> +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);
>  }
>  
>  SYSCALL_DEFINE2(removexattr, const char __user *, pathname,
>  		const char __user *, name)
>  {
> -	return path_removexattr(pathname, name, LOOKUP_FOLLOW);
> +	return path_removexattrat(AT_FDCWD, pathname, 0, name);
>  }
>  
>  SYSCALL_DEFINE2(lremovexattr, const char __user *, pathname,
>  		const char __user *, name)
>  {
> -	return path_removexattr(pathname, name, 0);
> +	return path_removexattrat(AT_FDCWD, pathname, AT_SYMLINK_NOFOLLOW, name);
>  }
>  
>  SYSCALL_DEFINE2(fremovexattr, int, fd, const char __user *, name)
>  {
> -	CLASS(fd, f)(fd);
> -	struct xattr_name kname;
> -	int error;
> -
> -	if (fd_empty(f))
> -		return -EBADF;
> -
> -	error = import_xattr_name(&kname, name);
> -	if (error)
> -		return error;
> -	return file_removexattr(fd_file(f), &kname);
> +	return path_removexattrat(fd, NULL, AT_EMPTY_PATH, name);
>  }
>  
>  int xattr_list_one(char **buffer, ssize_t *remaining_size, const char *name)
> diff --git a/include/asm-generic/audit_change_attr.h b/include/asm-generic/audit_change_attr.h
> index 331670807cf0..cc840537885f 100644
> --- a/include/asm-generic/audit_change_attr.h
> +++ b/include/asm-generic/audit_change_attr.h
> @@ -11,9 +11,15 @@ __NR_lchown,
>  __NR_fchown,
>  #endif
>  __NR_setxattr,
> +#ifdef __NR_setxattrat
> +__NR_setxattrat,
> +#endif
>  __NR_lsetxattr,
>  __NR_fsetxattr,
>  __NR_removexattr,
> +#ifdef __NR_removexattrat
> +__NR_removexattrat,
> +#endif
>  __NR_lremovexattr,
>  __NR_fremovexattr,
>  #ifdef __NR_fchownat
> diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h
> index 5758104921e6..c6333204d451 100644
> --- a/include/linux/syscalls.h
> +++ b/include/linux/syscalls.h
> @@ -77,6 +77,7 @@ struct cachestat_range;
>  struct cachestat;
>  struct statmount;
>  struct mnt_id_req;
> +struct xattr_args;
>  
>  #include <linux/types.h>
>  #include <linux/aio_abi.h>
> @@ -338,23 +339,35 @@ asmlinkage long sys_io_uring_register(unsigned int fd, unsigned int op,
>  				void __user *arg, unsigned int nr_args);
>  asmlinkage long sys_setxattr(const char __user *path, const char __user *name,
>  			     const void __user *value, size_t size, int flags);
> +asmlinkage long sys_setxattrat(int dfd, const char __user *path, unsigned int at_flags,
> +			       const char __user *name,
> +			       const struct xattr_args __user *args, size_t size);
>  asmlinkage long sys_lsetxattr(const char __user *path, const char __user *name,
>  			      const void __user *value, size_t size, int flags);
>  asmlinkage long sys_fsetxattr(int fd, const char __user *name,
>  			      const void __user *value, size_t size, int flags);
>  asmlinkage long sys_getxattr(const char __user *path, const char __user *name,
>  			     void __user *value, size_t size);
> +asmlinkage long sys_getxattrat(int dfd, const char __user *path, unsigned int at_flags,
> +			       const char __user *name,
> +			       struct xattr_args __user *args, size_t size);
>  asmlinkage long sys_lgetxattr(const char __user *path, const char __user *name,
>  			      void __user *value, size_t size);
>  asmlinkage long sys_fgetxattr(int fd, const char __user *name,
>  			      void __user *value, size_t size);
>  asmlinkage long sys_listxattr(const char __user *path, char __user *list,
>  			      size_t size);
> +asmlinkage long sys_listxattrat(int dfd, const char __user *path,
> +				unsigned int at_flags,
> +				char __user *list, size_t size);
>  asmlinkage long sys_llistxattr(const char __user *path, char __user *list,
>  			       size_t size);
>  asmlinkage long sys_flistxattr(int fd, char __user *list, size_t size);
>  asmlinkage long sys_removexattr(const char __user *path,
>  				const char __user *name);
> +asmlinkage long sys_removexattrat(int dfd, const char __user *path,
> +				  unsigned int at_flags,
> +				  const char __user *name);
>  asmlinkage long sys_lremovexattr(const char __user *path,
>  				 const char __user *name);
>  asmlinkage long sys_fremovexattr(int fd, const char __user *name);
> diff --git a/include/linux/xattr.h b/include/linux/xattr.h
> index d20051865800..86b0d47984a1 100644
> --- a/include/linux/xattr.h
> +++ b/include/linux/xattr.h
> @@ -19,6 +19,10 @@
>  #include <linux/user_namespace.h>
>  #include <uapi/linux/xattr.h>
>  
> +/* List of all open_how "versions". */
> +#define XATTR_ARGS_SIZE_VER0	16 /* sizeof first published struct */
> +#define XATTR_ARGS_SIZE_LATEST	XATTR_ARGS_SIZE_VER0
> +
>  struct inode;
>  struct dentry;
>  
> diff --git a/include/uapi/asm-generic/unistd.h b/include/uapi/asm-generic/unistd.h
> index 5bf6148cac2b..88dc393c2bca 100644
> --- a/include/uapi/asm-generic/unistd.h
> +++ b/include/uapi/asm-generic/unistd.h
> @@ -841,8 +841,17 @@ __SYSCALL(__NR_lsm_list_modules, sys_lsm_list_modules)
>  #define __NR_mseal 462
>  __SYSCALL(__NR_mseal, sys_mseal)
>  
> +#define __NR_setxattrat 463
> +__SYSCALL(__NR_setxattrat, sys_setxattrat)
> +#define __NR_getxattrat 464
> +__SYSCALL(__NR_getxattrat, sys_getxattrat)
> +#define __NR_listxattrat 465
> +__SYSCALL(__NR_listxattrat, sys_listxattrat)
> +#define __NR_removexattrat 466
> +__SYSCALL(__NR_removexattrat, sys_removexattrat)
> +
>  #undef __NR_syscalls
> -#define __NR_syscalls 463
> +#define __NR_syscalls 467
>  
>  /*
>   * 32 bit systems traditionally used different
> diff --git a/include/uapi/linux/xattr.h b/include/uapi/linux/xattr.h
> index 9463db2dfa9d..9854f9cff3c6 100644
> --- a/include/uapi/linux/xattr.h
> +++ b/include/uapi/linux/xattr.h
> @@ -11,6 +11,7 @@
>  */
>  
>  #include <linux/libc-compat.h>
> +#include <linux/types.h>
>  
>  #ifndef _UAPI_LINUX_XATTR_H
>  #define _UAPI_LINUX_XATTR_H
> @@ -20,6 +21,12 @@
>  
>  #define XATTR_CREATE	0x1	/* set value, fail if attr already exists */
>  #define XATTR_REPLACE	0x2	/* set value, fail if attr does not exist */
> +
> +struct xattr_args {
> +	__aligned_u64 __user value;
> +	__u32 size;
> +	__u32 flags;
> +};
>  #endif
>  
>  /* Namespaces */
> -- 
> 2.39.5
> 

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 5/9] replace do_setxattr() with saner helpers.
  2024-10-02  2:08       ` Al Viro
@ 2024-10-02 18:00         ` Jens Axboe
  2024-10-02 21:19           ` Al Viro
  0 siblings, 1 reply; 50+ messages in thread
From: Jens Axboe @ 2024-10-02 18:00 UTC (permalink / raw)
  To: Al Viro; +Cc: linux-fsdevel, brauner, io-uring, cgzones

On 10/1/24 8:08 PM, Al Viro wrote:
> On Tue, Oct 01, 2024 at 07:34:12PM -0600, Jens Axboe wrote:
> 
>>> -retry:
>>> -	ret = filename_lookup(AT_FDCWD, ix->filename, lookup_flags, &path, NULL);
>>> -	if (!ret) {
>>> -		ret = __io_setxattr(req, issue_flags, &path);
>>> -		path_put(&path);
>>> -		if (retry_estale(ret, lookup_flags)) {
>>> -			lookup_flags |= LOOKUP_REVAL;
>>> -			goto retry;
>>> -		}
>>> -	}
>>> -
>>> +	ret = filename_setxattr(AT_FDCWD, ix->filename, LOOKUP_FOLLOW, &ix->ctx);
>>>  	io_xattr_finish(req, ret);
>>>  	return IOU_OK;
>>
>> this looks like it needs an ix->filename = NULL, as
>> filename_{s,g}xattr() drops the reference. The previous internal helper
>> did not, and hence the cleanup always did it. But should work fine if
>> ->filename is just zeroed.
>>
>> Otherwise looks good. I've skimmed the other patches and didn't see
>> anything odd, I'll take a closer look tomorrow.
> 
> Hmm...  I wonder if we would be better off with file{,name}_setxattr()
> doing kvfree(cxt->kvalue) - it makes things easier both on the syscall
> and on io_uring side.
> 
> I've added minimal fixes (zeroing ix->filename after filename_[sg]etxattr())
> to 5/9 and 6/9 *and* added a followup calling conventions change at the end
> of the branch.  See #work.xattr2 in the same tree; FWIW, the followup
> cleanup is below; note that putname(ERR_PTR(-Ewhatever)) is an explicit
> no-op, so there's no need to zero on getname() failures.

Looks good to me, thanks Al!

-- 
Jens Axboe

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 5/9] replace do_setxattr() with saner helpers.
  2024-10-02 18:00         ` Jens Axboe
@ 2024-10-02 21:19           ` Al Viro
  2024-10-02 22:55             ` Jens Axboe
  0 siblings, 1 reply; 50+ messages in thread
From: Al Viro @ 2024-10-02 21:19 UTC (permalink / raw)
  To: Jens Axboe; +Cc: linux-fsdevel, brauner, io-uring, cgzones

On Wed, Oct 02, 2024 at 12:00:45PM -0600, Jens Axboe wrote:
> On 10/1/24 8:08 PM, Al Viro wrote:
> > On Tue, Oct 01, 2024 at 07:34:12PM -0600, Jens Axboe wrote:
> > 
> >>> -retry:
> >>> -	ret = filename_lookup(AT_FDCWD, ix->filename, lookup_flags, &path, NULL);
> >>> -	if (!ret) {
> >>> -		ret = __io_setxattr(req, issue_flags, &path);
> >>> -		path_put(&path);
> >>> -		if (retry_estale(ret, lookup_flags)) {
> >>> -			lookup_flags |= LOOKUP_REVAL;
> >>> -			goto retry;
> >>> -		}
> >>> -	}
> >>> -
> >>> +	ret = filename_setxattr(AT_FDCWD, ix->filename, LOOKUP_FOLLOW, &ix->ctx);
> >>>  	io_xattr_finish(req, ret);
> >>>  	return IOU_OK;
> >>
> >> this looks like it needs an ix->filename = NULL, as
> >> filename_{s,g}xattr() drops the reference. The previous internal helper
> >> did not, and hence the cleanup always did it. But should work fine if
> >> ->filename is just zeroed.
> >>
> >> Otherwise looks good. I've skimmed the other patches and didn't see
> >> anything odd, I'll take a closer look tomorrow.
> > 
> > Hmm...  I wonder if we would be better off with file{,name}_setxattr()
> > doing kvfree(cxt->kvalue) - it makes things easier both on the syscall
> > and on io_uring side.
> > 
> > I've added minimal fixes (zeroing ix->filename after filename_[sg]etxattr())
> > to 5/9 and 6/9 *and* added a followup calling conventions change at the end
> > of the branch.  See #work.xattr2 in the same tree; FWIW, the followup
> > cleanup is below; note that putname(ERR_PTR(-Ewhatever)) is an explicit
> > no-op, so there's no need to zero on getname() failures.
> 
> Looks good to me, thanks Al!

I'm still not sure if the calling conventions change is right - in the current
form the last commit in there leaks ctx.kvalue in -EBADF case.  It's easy to
fix up, but... as far as I'm concerned, a large part of the point of the
exercise is to come up with the right model for the calling conventions
for that family of APIs.

I really want to get rid of that ad-hoc crap.  If we are to have what amounts
to the alternative syscall interface, we'd better get it right.  I'm perfectly
fine with having a set of "this is what the syscall is doing past marshalling
arguments" primitives, but let's make sure they are properly documented and
do not have landmines for callers to step into...

Questions on the io_uring side:
	* you usually reject REQ_F_FIXED_FILE for ...at() at ->prep() time.
Fine, but... what's the point of doing that in IORING_OP_FGETXATTR case?
Or IORING_OP_GETXATTR, for that matter, since you pass AT_FDCWD anyway...
Am I missing something subtle here?
	* what's to guarantee that pointers fetched by io_file_get_fixed()
called from io_assing_file() will stay valid?  You do not bump the struct
file refcount in this case, after all; what's to prevent unregistration
from the main thread while the worker is getting through your request?
Is that what the break on node->refs in the loop in io_rsrc_node_ref_zero()
is about?  Or am I barking at the wrong tree here?  I realize that I'm about
the last person to complain about the lack of documentation, but...

	FWIW, my impression is that you have a list of nodes corresponding
to overall resource states (which includes the file reference table) and
have each borrow bump the use count on the node corresponding to the current
state (at the tail of the list?)
	Each removal adds new node to the tail of the list, sticks the
file reference there and tries to trigger io_rsrc_node_ref_zero() (which,
for some reason, takes node instead of the node->ctx, even though it
doesn't give a rat's arse about anything else in its argument).
	If there are nodes at the head of the list with zero use count,
that takes them out, stopping at the first in-use node.  File reference
stashed in a node is dropped when it's taken out.

	If the above is more or less correct (and I'm pretty sure that it
misses quite a few critical points), the rules would be equivalent to
	+ there is a use count associated with the table state.
	+ before we borrow a file reference from the table, we must bump
that use count (see the call of __io_req_set_rsrc_node() in
io_file_get_fixed()) and arrange for dropping it once we are done with
the reference (io_put_rsrc_node() when freeing request, in io_free_batch_list())
	+ any removals from the table will switch to new state; dropping
the removed reference is guaranteed to be delayed until use counts on
all earlier states drop to zero.

	How far are those rules from being accurate and how incomplete
they are?  I hadn't looked into the quiescence-related stuff, which might
or might not be relevant...

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 5/9] replace do_setxattr() with saner helpers.
  2024-10-02 21:19           ` Al Viro
@ 2024-10-02 22:55             ` Jens Axboe
  2024-10-06  5:28               ` Al Viro
  0 siblings, 1 reply; 50+ messages in thread
From: Jens Axboe @ 2024-10-02 22:55 UTC (permalink / raw)
  To: Al Viro; +Cc: linux-fsdevel, brauner, io-uring, cgzones

On 10/2/24 3:19 PM, Al Viro wrote:
> On Wed, Oct 02, 2024 at 12:00:45PM -0600, Jens Axboe wrote:
>> On 10/1/24 8:08 PM, Al Viro wrote:
>>> On Tue, Oct 01, 2024 at 07:34:12PM -0600, Jens Axboe wrote:
>>>
>>>>> -retry:
>>>>> -	ret = filename_lookup(AT_FDCWD, ix->filename, lookup_flags, &path, NULL);
>>>>> -	if (!ret) {
>>>>> -		ret = __io_setxattr(req, issue_flags, &path);
>>>>> -		path_put(&path);
>>>>> -		if (retry_estale(ret, lookup_flags)) {
>>>>> -			lookup_flags |= LOOKUP_REVAL;
>>>>> -			goto retry;
>>>>> -		}
>>>>> -	}
>>>>> -
>>>>> +	ret = filename_setxattr(AT_FDCWD, ix->filename, LOOKUP_FOLLOW, &ix->ctx);
>>>>>  	io_xattr_finish(req, ret);
>>>>>  	return IOU_OK;
>>>>
>>>> this looks like it needs an ix->filename = NULL, as
>>>> filename_{s,g}xattr() drops the reference. The previous internal helper
>>>> did not, and hence the cleanup always did it. But should work fine if
>>>> ->filename is just zeroed.
>>>>
>>>> Otherwise looks good. I've skimmed the other patches and didn't see
>>>> anything odd, I'll take a closer look tomorrow.
>>>
>>> Hmm...  I wonder if we would be better off with file{,name}_setxattr()
>>> doing kvfree(cxt->kvalue) - it makes things easier both on the syscall
>>> and on io_uring side.
>>>
>>> I've added minimal fixes (zeroing ix->filename after filename_[sg]etxattr())
>>> to 5/9 and 6/9 *and* added a followup calling conventions change at the end
>>> of the branch.  See #work.xattr2 in the same tree; FWIW, the followup
>>> cleanup is below; note that putname(ERR_PTR(-Ewhatever)) is an explicit
>>> no-op, so there's no need to zero on getname() failures.
>>
>> Looks good to me, thanks Al!
> 
> I'm still not sure if the calling conventions change is right - in the
> current form the last commit in there leaks ctx.kvalue in -EBADF case.
> It's easy to fix up, but... as far as I'm concerned, a large part of
> the point of the exercise is to come up with the right model for the
> calling conventions for that family of APIs.

The reason I liked the putname() is that it's unconditional - the caller
can rely on it being put, regardless of the return value. So I'd say the
same should be true for ctx.kvalue, and if not, the caller should still
free it. That's the path of least surprise - no leak for the least
tested error path, and no UAF in the success case.

For the put case, most other abstractions end up being something ala:

helper(struct file *file, ...)
{
	actual actions
}

regular_sys_call(int fd, ...)
{
	struct fd f;
	int ret = -EBADF;

	f = fdget(fd);
	if (f.file) {
		ret = helper(f.file, ...);
		fdput(f();
	}

	return ret;
}

where io_uring will use helper(), and where the file reference is
assumed to be valid for helper() and helper() will not put a reference
to it.

That's a bit different than your putname() case, but I think as long as
it's consistent regardless of return value, then either approach is
fine. Maybe just add a comment about that? At least for the consistent
case, if it blows up, it'll blow up instantly rather than be a surprise
down the line for "case x,y,z doesn't put it" or "case x,y,z always puts
in, normal one does not".

> I really want to get rid of that ad-hoc crap.  If we are to have what
> amounts to the alternative syscall interface, we'd better get it
> right.  I'm perfectly fine with having a set of "this is what the
> syscall is doing past marshalling arguments" primitives, but let's
> make sure they are properly documented and do not have landmines for
> callers to step into...

Fully agree.

> Questions on the io_uring side:
> 	* you usually reject REQ_F_FIXED_FILE for ...at() at ->prep() time.
> Fine, but... what's the point of doing that in IORING_OP_FGETXATTR case?
> Or IORING_OP_GETXATTR, for that matter, since you pass AT_FDCWD anyway...
> Am I missing something subtle here?

Right, it could be allowed for fgetxattr on the io_uring side. Anything
that passes in a struct file would be fair game to enable it on.
Anything that passes in a path (eg a non-fd value), it obviously
wouldn't make sense anyway.

> 	* what's to guarantee that pointers fetched by io_file_get_fixed()
> called from io_assing_file() will stay valid?  You do not bump the struct
> file refcount in this case, after all; what's to prevent unregistration
> from the main thread while the worker is getting through your request?
> Is that what the break on node->refs in the loop in io_rsrc_node_ref_zero()
> is about?  Or am I barking at the wrong tree here?  I realize that I'm about
> the last person to complain about the lack of documentation, but...
> 
> 	FWIW, my impression is that you have a list of nodes corresponding
> to overall resource states (which includes the file reference table) and
> have each borrow bump the use count on the node corresponding to the current
> state (at the tail of the list?)
> 	Each removal adds new node to the tail of the list, sticks the
> file reference there and tries to trigger io_rsrc_node_ref_zero() (which,
> for some reason, takes node instead of the node->ctx, even though it
> doesn't give a rat's arse about anything else in its argument).
> 	If there are nodes at the head of the list with zero use count,
> that takes them out, stopping at the first in-use node.  File reference
> stashed in a node is dropped when it's taken out.
> 
> 	If the above is more or less correct (and I'm pretty sure that it
> misses quite a few critical points), the rules would be equivalent to
> 	+ there is a use count associated with the table state.
> 	+ before we borrow a file reference from the table, we must bump
> that use count (see the call of __io_req_set_rsrc_node() in
> io_file_get_fixed()) and arrange for dropping it once we are done with
> the reference (io_put_rsrc_node() when freeing request, in io_free_batch_list())
> 	+ any removals from the table will switch to new state; dropping
> the removed reference is guaranteed to be delayed until use counts on
> all earlier states drop to zero.
> 
> 	How far are those rules from being accurate and how incomplete
> they are?  I hadn't looked into the quiescence-related stuff, which might
> or might not be relevant...

That is pretty darn accurate. The ordering of the rsrc nodes and the
break ensure that it stays valid until anything using it has completed.
And yes it would be nice to document that code a bit, but honestly I'd
much rather just make it more obviously referenced if that can be done
cheaply enough. For now, I'll add some comments, and hope you do the
same on your side! Because I don't ever remember seeing an Al comment.
Great emails, for sure, but not really comments.

-- 
Jens Axboe

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 5/9] replace do_setxattr() with saner helpers.
  2024-10-02 22:55             ` Jens Axboe
@ 2024-10-06  5:28               ` Al Viro
  2024-10-07 18:09                 ` Jens Axboe
  0 siblings, 1 reply; 50+ messages in thread
From: Al Viro @ 2024-10-06  5:28 UTC (permalink / raw)
  To: Jens Axboe; +Cc: linux-fsdevel, brauner, io-uring, cgzones

On Wed, Oct 02, 2024 at 04:55:22PM -0600, Jens Axboe wrote:

> The reason I liked the putname() is that it's unconditional - the caller
> can rely on it being put, regardless of the return value. So I'd say the
> same should be true for ctx.kvalue, and if not, the caller should still
> free it. That's the path of least surprise - no leak for the least
> tested error path, and no UAF in the success case.

The problem with ctx.kvalue is that on the syscall side there's a case when
we do not call either file_setxattr() or filename_setxattr() - -EBADF.
And it's a lot more convenient to do setxattr_copy() first, so we end
up with a lovely landmine:
        filename = getname_xattr(pathname, at_flags);
	if (!filename) {
		CLASS(fd, f)(dfd);
		if (fd_empty(f)) {
			kfree(ctx.kvalue); // lest we leak
			return -EBADF;
		}
		return file_setxattr(fd_file(f), &ctx);
	}
	return filename_setxattr(dfd, filename, lookup_flags, &ctx);

That's asking for trouble, obviously.  So I think we ought to consume
filename (in filename_...()) case, leave struct file reference alone
(we have to - it might have been borrowed rather than cloned) and leave
->kvalue unchanged.  Yes, it ends up being more clumsy, but at least
it's consistent between the cases...

As for consuming filename...  On the syscall side it allows things like
SYSCALL_DEFINE3(mkdirat, int, dfd, const char __user *, pathname, umode_t, mode)
{
        return do_mkdirat(dfd, getname(pathname), mode);
}  
which is better than the alternatives - I mean, that's
SYSCALL_DEFINE3(mkdirat, int, dfd, const char __user *, pathname, umode_t, mode)
{
	struct filename *filename = getname(pathname);
	int res = do_mkdirat(dfd, filename, mode);
	putname(filename);
	return ret;
}  
or 
SYSCALL_DEFINE3(mkdirat, int, dfd, const char __user *, pathname, umode_t, mode)
{
	struct filename *filename __free(putname) = getname(pathname);
	return do_mkdirat(dfd, filename, mode);
}
and both stink, if for different reasons ;-/  Having those things consume
(unconditionally) is better, IMO.

Hell knows; let's go with what I described above for now and see where it leads
when more such helpers are regularized.

> That's a bit different than your putname() case, but I think as long as
> it's consistent regardless of return value, then either approach is
> fine. Maybe just add a comment about that? At least for the consistent
> case, if it blows up, it'll blow up instantly rather than be a surprise
> down the line for "case x,y,z doesn't put it" or "case x,y,z always puts
> in, normal one does not".

Obviously.

> > Questions on the io_uring side:
> > 	* you usually reject REQ_F_FIXED_FILE for ...at() at ->prep() time.
> > Fine, but... what's the point of doing that in IORING_OP_FGETXATTR case?
> > Or IORING_OP_GETXATTR, for that matter, since you pass AT_FDCWD anyway...
> > Am I missing something subtle here?
> 
> Right, it could be allowed for fgetxattr on the io_uring side. Anything
> that passes in a struct file would be fair game to enable it on.
> Anything that passes in a path (eg a non-fd value), it obviously
> wouldn't make sense anyway.

OK, done and force-pushed into #work.xattr.

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 5/9] replace do_setxattr() with saner helpers.
  2024-10-06  5:28               ` Al Viro
@ 2024-10-07 18:09                 ` Jens Axboe
  2024-10-07 18:20                   ` Jens Axboe
  0 siblings, 1 reply; 50+ messages in thread
From: Jens Axboe @ 2024-10-07 18:09 UTC (permalink / raw)
  To: Al Viro; +Cc: linux-fsdevel, brauner, io-uring, cgzones

On 10/5/24 11:28 PM, Al Viro wrote:
> On Wed, Oct 02, 2024 at 04:55:22PM -0600, Jens Axboe wrote:
> 
>> The reason I liked the putname() is that it's unconditional - the caller
>> can rely on it being put, regardless of the return value. So I'd say the
>> same should be true for ctx.kvalue, and if not, the caller should still
>> free it. That's the path of least surprise - no leak for the least
>> tested error path, and no UAF in the success case.
> 
> The problem with ctx.kvalue is that on the syscall side there's a case when
> we do not call either file_setxattr() or filename_setxattr() - -EBADF.
> And it's a lot more convenient to do setxattr_copy() first, so we end
> up with a lovely landmine:
>         filename = getname_xattr(pathname, at_flags);
> 	if (!filename) {
> 		CLASS(fd, f)(dfd);
> 		if (fd_empty(f)) {
> 			kfree(ctx.kvalue); // lest we leak
> 			return -EBADF;
> 		}
> 		return file_setxattr(fd_file(f), &ctx);
> 	}
> 	return filename_setxattr(dfd, filename, lookup_flags, &ctx);
> 
> That's asking for trouble, obviously.  So I think we ought to consume
> filename (in filename_...()) case, leave struct file reference alone
> (we have to - it might have been borrowed rather than cloned) and leave
> ->kvalue unchanged.  Yes, it ends up being more clumsy, but at least
> it's consistent between the cases...
> 
> As for consuming filename...  On the syscall side it allows things like
> SYSCALL_DEFINE3(mkdirat, int, dfd, const char __user *, pathname, umode_t, mode)
> {
>         return do_mkdirat(dfd, getname(pathname), mode);
> }  
> which is better than the alternatives - I mean, that's
> SYSCALL_DEFINE3(mkdirat, int, dfd, const char __user *, pathname, umode_t, mode)
> {
> 	struct filename *filename = getname(pathname);
> 	int res = do_mkdirat(dfd, filename, mode);
> 	putname(filename);
> 	return ret;
> }  
> or 
> SYSCALL_DEFINE3(mkdirat, int, dfd, const char __user *, pathname, umode_t, mode)
> {
> 	struct filename *filename __free(putname) = getname(pathname);
> 	return do_mkdirat(dfd, filename, mode);
> }
> and both stink, if for different reasons ;-/  Having those things consume
> (unconditionally) is better, IMO.
> 
> Hell knows; let's go with what I described above for now and see where
> it leads when more such helpers are regularized.

Sounds like a plan.

>>> Questions on the io_uring side:
>>> 	* you usually reject REQ_F_FIXED_FILE for ...at() at ->prep() time.
>>> Fine, but... what's the point of doing that in IORING_OP_FGETXATTR case?
>>> Or IORING_OP_GETXATTR, for that matter, since you pass AT_FDCWD anyway...
>>> Am I missing something subtle here?
>>
>> Right, it could be allowed for fgetxattr on the io_uring side. Anything
>> that passes in a struct file would be fair game to enable it on.
>> Anything that passes in a path (eg a non-fd value), it obviously
>> wouldn't make sense anyway.
> 
> OK, done and force-pushed into #work.xattr.

I just checked, and while I think this is fine to do for the 'fd' taking
{s,g}etxattr, I don't think the path taking ones should allow
IOSQE_FIXED_FILE being set. It's nonsensical, as they don't take a file
descriptor. So I'd prefer if we kept it to just the f* variants. I can
just make this tweak in my io_uring 6.12 branch and get it upstream this
week, that'll take it out of your hands.

What do you think?

-- 
Jens Axboe

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 5/9] replace do_setxattr() with saner helpers.
  2024-10-07 18:09                 ` Jens Axboe
@ 2024-10-07 18:20                   ` Jens Axboe
  2024-10-07 21:20                     ` Al Viro
  0 siblings, 1 reply; 50+ messages in thread
From: Jens Axboe @ 2024-10-07 18:20 UTC (permalink / raw)
  To: Al Viro; +Cc: linux-fsdevel, brauner, io-uring, cgzones

On 10/7/24 12:09 PM, Jens Axboe wrote:
>>>> Questions on the io_uring side:
>>>> 	* you usually reject REQ_F_FIXED_FILE for ...at() at ->prep() time.
>>>> Fine, but... what's the point of doing that in IORING_OP_FGETXATTR case?
>>>> Or IORING_OP_GETXATTR, for that matter, since you pass AT_FDCWD anyway...
>>>> Am I missing something subtle here?
>>>
>>> Right, it could be allowed for fgetxattr on the io_uring side. Anything
>>> that passes in a struct file would be fair game to enable it on.
>>> Anything that passes in a path (eg a non-fd value), it obviously
>>> wouldn't make sense anyway.
>>
>> OK, done and force-pushed into #work.xattr.
> 
> I just checked, and while I think this is fine to do for the 'fd' taking
> {s,g}etxattr, I don't think the path taking ones should allow
> IOSQE_FIXED_FILE being set. It's nonsensical, as they don't take a file
> descriptor. So I'd prefer if we kept it to just the f* variants. I can
> just make this tweak in my io_uring 6.12 branch and get it upstream this
> week, that'll take it out of your hands.
> 
> What do you think?

Like the below. You can update yours if you want, or I can shove this
into 6.12, whatever is the easiest for you.


diff --git a/io_uring/xattr.c b/io_uring/xattr.c
index 6cf41c3bc369..4b68c282c91a 100644
--- a/io_uring/xattr.c
+++ b/io_uring/xattr.c
@@ -48,9 +48,6 @@ static int __io_getxattr_prep(struct io_kiocb *req,
 	const char __user *name;
 	int ret;
 
-	if (unlikely(req->flags & REQ_F_FIXED_FILE))
-		return -EBADF;
-
 	ix->filename = NULL;
 	ix->ctx.kvalue = NULL;
 	name = u64_to_user_ptr(READ_ONCE(sqe->addr));
@@ -90,6 +87,9 @@ int io_getxattr_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
 	const char __user *path;
 	int ret;
 
+	if (unlikely(req->flags & REQ_F_FIXED_FILE))
+		return -EBADF;
+
 	ret = __io_getxattr_prep(req, sqe);
 	if (ret)
 		return ret;
@@ -152,9 +152,6 @@ static int __io_setxattr_prep(struct io_kiocb *req,
 	const char __user *name;
 	int ret;
 
-	if (unlikely(req->flags & REQ_F_FIXED_FILE))
-		return -EBADF;
-
 	ix->filename = NULL;
 	name = u64_to_user_ptr(READ_ONCE(sqe->addr));
 	ix->ctx.cvalue = u64_to_user_ptr(READ_ONCE(sqe->addr2));
@@ -183,6 +180,9 @@ int io_setxattr_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
 	const char __user *path;
 	int ret;
 
+	if (unlikely(req->flags & REQ_F_FIXED_FILE))
+		return -EBADF;
+
 	ret = __io_setxattr_prep(req, sqe);
 	if (ret)
 		return ret;

-- 
Jens Axboe

^ permalink raw reply related	[flat|nested] 50+ messages in thread

* Re: [PATCH 5/9] replace do_setxattr() with saner helpers.
  2024-10-07 18:20                   ` Jens Axboe
@ 2024-10-07 21:20                     ` Al Viro
  2024-10-07 22:29                       ` Jens Axboe
  0 siblings, 1 reply; 50+ messages in thread
From: Al Viro @ 2024-10-07 21:20 UTC (permalink / raw)
  To: Jens Axboe; +Cc: linux-fsdevel, brauner, io-uring, cgzones

On Mon, Oct 07, 2024 at 12:20:20PM -0600, Jens Axboe wrote:
> On 10/7/24 12:09 PM, Jens Axboe wrote:
> >>>> Questions on the io_uring side:
> >>>> 	* you usually reject REQ_F_FIXED_FILE for ...at() at ->prep() time.
> >>>> Fine, but... what's the point of doing that in IORING_OP_FGETXATTR case?
> >>>> Or IORING_OP_GETXATTR, for that matter, since you pass AT_FDCWD anyway...
> >>>> Am I missing something subtle here?
> >>>
> >>> Right, it could be allowed for fgetxattr on the io_uring side. Anything
> >>> that passes in a struct file would be fair game to enable it on.
> >>> Anything that passes in a path (eg a non-fd value), it obviously
> >>> wouldn't make sense anyway.
> >>
> >> OK, done and force-pushed into #work.xattr.
> > 
> > I just checked, and while I think this is fine to do for the 'fd' taking
> > {s,g}etxattr, I don't think the path taking ones should allow
> > IOSQE_FIXED_FILE being set. It's nonsensical, as they don't take a file
> > descriptor. So I'd prefer if we kept it to just the f* variants. I can
> > just make this tweak in my io_uring 6.12 branch and get it upstream this
> > week, that'll take it out of your hands.
> > 
> > What do you think?
> 
> Like the below. You can update yours if you want, or I can shove this
> into 6.12, whatever is the easiest for you.

Can I put your s-o-b on that, with e.g.

io_uring: IORING_OP_F[GS]ETXATTR is fine with REQ_F_FIXED_FILE

Rejection of IOSQE_FIXED_FILE combined with IORING_OP_[GS]ETXATTR
is fine - these do not take a file descriptor, so such combination
makes no sense.  The checks are misplaced, though - as it is, they
triggers on IORING_OP_F[GS]ETXATTR as well, and those do take 
a file reference, no matter the origin. 

as commit message?

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 5/9] replace do_setxattr() with saner helpers.
  2024-10-07 21:20                     ` Al Viro
@ 2024-10-07 22:29                       ` Jens Axboe
  2024-10-07 23:58                         ` Al Viro
  0 siblings, 1 reply; 50+ messages in thread
From: Jens Axboe @ 2024-10-07 22:29 UTC (permalink / raw)
  To: Al Viro; +Cc: linux-fsdevel, brauner, io-uring, cgzones

On 10/7/24 3:20 PM, Al Viro wrote:
> On Mon, Oct 07, 2024 at 12:20:20PM -0600, Jens Axboe wrote:
>> On 10/7/24 12:09 PM, Jens Axboe wrote:
>>>>>> Questions on the io_uring side:
>>>>>> 	* you usually reject REQ_F_FIXED_FILE for ...at() at ->prep() time.
>>>>>> Fine, but... what's the point of doing that in IORING_OP_FGETXATTR case?
>>>>>> Or IORING_OP_GETXATTR, for that matter, since you pass AT_FDCWD anyway...
>>>>>> Am I missing something subtle here?
>>>>>
>>>>> Right, it could be allowed for fgetxattr on the io_uring side. Anything
>>>>> that passes in a struct file would be fair game to enable it on.
>>>>> Anything that passes in a path (eg a non-fd value), it obviously
>>>>> wouldn't make sense anyway.
>>>>
>>>> OK, done and force-pushed into #work.xattr.
>>>
>>> I just checked, and while I think this is fine to do for the 'fd' taking
>>> {s,g}etxattr, I don't think the path taking ones should allow
>>> IOSQE_FIXED_FILE being set. It's nonsensical, as they don't take a file
>>> descriptor. So I'd prefer if we kept it to just the f* variants. I can
>>> just make this tweak in my io_uring 6.12 branch and get it upstream this
>>> week, that'll take it out of your hands.
>>>
>>> What do you think?
>>
>> Like the below. You can update yours if you want, or I can shove this
>> into 6.12, whatever is the easiest for you.
> 
> Can I put your s-o-b on that, with e.g.
> 
> io_uring: IORING_OP_F[GS]ETXATTR is fine with REQ_F_FIXED_FILE
> 
> Rejection of IOSQE_FIXED_FILE combined with IORING_OP_[GS]ETXATTR
> is fine - these do not take a file descriptor, so such combination
> makes no sense.  The checks are misplaced, though - as it is, they
> triggers on IORING_OP_F[GS]ETXATTR as well, and those do take 
> a file reference, no matter the origin. 

Yep that's perfect, officially:

Signed-off-by: Jens Axboe <[email protected]>

Thanks Al!

-- 
Jens Axboe

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 5/9] replace do_setxattr() with saner helpers.
  2024-10-07 22:29                       ` Jens Axboe
@ 2024-10-07 23:58                         ` Al Viro
  2024-10-08  1:58                           ` Jens Axboe
  0 siblings, 1 reply; 50+ messages in thread
From: Al Viro @ 2024-10-07 23:58 UTC (permalink / raw)
  To: Jens Axboe; +Cc: linux-fsdevel, brauner, io-uring, cgzones

On Mon, Oct 07, 2024 at 04:29:29PM -0600, Jens Axboe wrote:
> > Can I put your s-o-b on that, with e.g.
> > 
> > io_uring: IORING_OP_F[GS]ETXATTR is fine with REQ_F_FIXED_FILE
> > 
> > Rejection of IOSQE_FIXED_FILE combined with IORING_OP_[GS]ETXATTR
> > is fine - these do not take a file descriptor, so such combination
> > makes no sense.  The checks are misplaced, though - as it is, they
> > triggers on IORING_OP_F[GS]ETXATTR as well, and those do take 
> > a file reference, no matter the origin. 
> 
> Yep that's perfect, officially:
> 
> Signed-off-by: Jens Axboe <[email protected]>
> 
> Thanks Al!

OK, updated and force-pushed (with slight reordering).  I can almost
promise no-rebase mode for that thing from now on, as long as nobody
on fsdevel objects to fs/xattr.c part of things after I repost the
series in the current form.

One possible exception: I'm not sure that fs/internal.h is a good
place for those primitives.  OTOH, any bikeshedding in that direction
can be delayed until the next cycle...

To expand the circle of potential bikeshedders: s/do_mkdirat/filename_mkdirat/
is a reasonable idea for this series, innit?  How about turning e.g.

int __init init_mkdir(const char *pathname, umode_t mode)
{
        struct dentry *dentry;
        struct path path;
        int error;

        dentry = kern_path_create(AT_FDCWD, pathname, &path, LOOKUP_DIRECTORY);
        if (IS_ERR(dentry))
                return PTR_ERR(dentry);
        mode = mode_strip_umask(d_inode(path.dentry), mode);
        error = security_path_mkdir(&path, dentry, mode);
        if (!error)
                error = vfs_mkdir(mnt_idmap(path.mnt), path.dentry->d_inode,
                                  dentry, mode);
        done_path_create(&path, dentry);
        return error;
}

into

int __init init_mkdir(const char *pathname, umode_t mode)
{
	return filename_mkdirat(AT_FDCWD, getname_kernel(pathname), mode);
}

reducing the duplication?  It really should not be accessible to random
places in the kernel, but syscalls in core VFS + io_uring interface for
the same + possibly init/*.c...

OTOH, I'm afraid to let the "but our driver is sooo special!" crowd play
with the full set of syscalls...  init_syscalls.h is already bad enough.
Hell knows, fs/internal.h just might be a bit of deterrent...

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 5/9] replace do_setxattr() with saner helpers.
  2024-10-07 23:58                         ` Al Viro
@ 2024-10-08  1:58                           ` Jens Axboe
  2024-10-08  4:08                             ` Al Viro
  0 siblings, 1 reply; 50+ messages in thread
From: Jens Axboe @ 2024-10-08  1:58 UTC (permalink / raw)
  To: Al Viro; +Cc: linux-fsdevel, brauner, io-uring, cgzones

On 10/7/24 5:58 PM, Al Viro wrote:
> On Mon, Oct 07, 2024 at 04:29:29PM -0600, Jens Axboe wrote:
>>> Can I put your s-o-b on that, with e.g.
>>>
>>> io_uring: IORING_OP_F[GS]ETXATTR is fine with REQ_F_FIXED_FILE
>>>
>>> Rejection of IOSQE_FIXED_FILE combined with IORING_OP_[GS]ETXATTR
>>> is fine - these do not take a file descriptor, so such combination
>>> makes no sense.  The checks are misplaced, though - as it is, they
>>> triggers on IORING_OP_F[GS]ETXATTR as well, and those do take 
>>> a file reference, no matter the origin. 
>>
>> Yep that's perfect, officially:
>>
>> Signed-off-by: Jens Axboe <[email protected]>
>>
>> Thanks Al!
> 
> OK, updated and force-pushed (with slight reordering).  I can almost
> promise no-rebase mode for that thing from now on, as long as nobody
> on fsdevel objects to fs/xattr.c part of things after I repost the
> series in the current form.

No worries on my end in terms of rebasing, I have no plans to touch
xattr.c for the coming series. Risk of conflict should be very low, so I
don't even need to pull that in.

> One possible exception: I'm not sure that fs/internal.h is a good
> place for those primitives.  OTOH, any bikeshedding in that direction
> can be delayed until the next cycle...

It ended up just being the defacto place to shove declarations for
things like that. But it always felt a bit dirty, particularly needing
to include that from the io_uring side as it moved out of fs/ as well.
Would indeed be nice to get that cleaned up a bit.

> To expand the circle of potential bikeshedders: s/do_mkdirat/filename_mkdirat/
> is a reasonable idea for this series, innit?  How about turning e.g.
> 
> int __init init_mkdir(const char *pathname, umode_t mode)
> {
>         struct dentry *dentry;
>         struct path path;
>         int error;
> 
>         dentry = kern_path_create(AT_FDCWD, pathname, &path, LOOKUP_DIRECTORY);
>         if (IS_ERR(dentry))
>                 return PTR_ERR(dentry);
>         mode = mode_strip_umask(d_inode(path.dentry), mode);
>         error = security_path_mkdir(&path, dentry, mode);
>         if (!error)
>                 error = vfs_mkdir(mnt_idmap(path.mnt), path.dentry->d_inode,
>                                   dentry, mode);
>         done_path_create(&path, dentry);
>         return error;
> }
> 
> into
> 
> int __init init_mkdir(const char *pathname, umode_t mode)
> {
> 	return filename_mkdirat(AT_FDCWD, getname_kernel(pathname), mode);
> }
> 
> reducing the duplication?  It really should not be accessible to random
> places in the kernel, but syscalls in core VFS + io_uring interface for
> the same + possibly init/*.c...
> 
> OTOH, I'm afraid to let the "but our driver is sooo special!" crowd play
> with the full set of syscalls...  init_syscalls.h is already bad enough.
> Hell knows, fs/internal.h just might be a bit of deterrent...

Deduping it is a good thing, suggestion looks good to me. For random
drivers, very much agree. But are there any of these symbols we end up
exporting? That tends to put a damper on the enthusiasm...

-- 
Jens Axboe

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 5/9] replace do_setxattr() with saner helpers.
  2024-10-08  1:58                           ` Jens Axboe
@ 2024-10-08  4:08                             ` Al Viro
  0 siblings, 0 replies; 50+ messages in thread
From: Al Viro @ 2024-10-08  4:08 UTC (permalink / raw)
  To: Jens Axboe; +Cc: linux-fsdevel, brauner, io-uring, cgzones

On Mon, Oct 07, 2024 at 07:58:15PM -0600, Jens Axboe wrote:

> > OTOH, I'm afraid to let the "but our driver is sooo special!" crowd play
> > with the full set of syscalls...  init_syscalls.h is already bad enough.
> > Hell knows, fs/internal.h just might be a bit of deterrent...
> 
> Deduping it is a good thing, suggestion looks good to me. For random
> drivers, very much agree. But are there any of these symbols we end up
> exporting? That tends to put a damper on the enthusiasm...

	You wish...  init_unlink() et.al. are not just not exported, they
are freed once the system is booted.  Doesn't stop that kind of magic being
attempted.

^ permalink raw reply	[flat|nested] 50+ messages in thread

* [RFC][PATCHES v2] xattr stuff and interactions with io_uring
  2024-10-02  1:10 [RFC][PATCHES] xattr stuff and interactions with io_uring Al Viro
  2024-10-02  1:22 ` [PATCH 1/9] xattr: switch to CLASS(fd) Al Viro
@ 2024-11-02  7:28 ` Al Viro
  2024-11-02  7:31   ` [PATCH v2 01/13] teach filename_lookup() to treat NULL filename as "" Al Viro
  2024-11-02 14:43   ` [RFC][PATCHES v2] xattr stuff and interactions with io_uring Jens Axboe
  1 sibling, 2 replies; 50+ messages in thread
From: Al Viro @ 2024-11-02  7:28 UTC (permalink / raw)
  To: linux-fsdevel; +Cc: Christian Brauner, io-uring, Christian Göttsche

	Changes since v1:
* moved on top of (and makes use of) getname_maybe_null() stuff
(first two commits here, form #base.getname-fixed)
* fixed a leak on io_uring side spotted by Jens
* putname(ERR_PTR(-E...)) is a no-op; allows to simplify things on
io_uring side.
* applied reviewed-by
* picked a generic_listxattr() cleanup from Colin Ian King

	Help with review and testing would be welcome; if nobody objects,
to #for-next it goes...

The branch lives in
git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs.git #work.xattr2
Individual patches in followups.
 
Diffstat:
 arch/alpha/kernel/syscalls/syscall.tbl      |   4 +
 arch/arm/tools/syscall.tbl                  |   4 +
 arch/m68k/kernel/syscalls/syscall.tbl       |   4 +
 arch/microblaze/kernel/syscalls/syscall.tbl |   4 +
 arch/mips/kernel/syscalls/syscall_n32.tbl   |   4 +
 arch/mips/kernel/syscalls/syscall_n64.tbl   |   4 +
 arch/mips/kernel/syscalls/syscall_o32.tbl   |   4 +
 arch/parisc/kernel/syscalls/syscall.tbl     |   4 +
 arch/powerpc/kernel/syscalls/syscall.tbl    |   4 +
 arch/s390/kernel/syscalls/syscall.tbl       |   4 +
 arch/sh/kernel/syscalls/syscall.tbl         |   4 +
 arch/sparc/kernel/syscalls/syscall.tbl      |   4 +
 arch/x86/entry/syscalls/syscall_32.tbl      |   4 +
 arch/x86/entry/syscalls/syscall_64.tbl      |   4 +
 arch/xtensa/kernel/syscalls/syscall.tbl     |   4 +
 fs/internal.h                               |  17 +-
 fs/namei.c                                  |  34 ++-
 fs/stat.c                                   |  28 +-
 fs/xattr.c                                  | 446 ++++++++++++++++++----------
 include/asm-generic/audit_change_attr.h     |   6 +
 include/linux/fs.h                          |  10 +
 include/linux/syscalls.h                    |  13 +
 include/linux/xattr.h                       |   4 +
 include/uapi/asm-generic/unistd.h           |  11 +-
 include/uapi/linux/xattr.h                  |   7 +
 io_uring/xattr.c                            |  97 ++----
 26 files changed, 466 insertions(+), 267 deletions(-)

Shortog:

Al Viro (9):
      teach filename_lookup() to treat NULL filename as ""
      getname_maybe_null() - the third variant of pathname copy-in
      io_[gs]etxattr_prep(): just use getname()
      xattr: switch to CLASS(fd)
      new helper: import_xattr_name()
      replace do_setxattr() with saner helpers.
      replace do_getxattr() with saner helpers.
      new helpers: file_listxattr(), filename_listxattr()
      new helpers: file_removexattr(), filename_removexattr()

Christian Göttsche (2):
      fs: rename struct xattr_ctx to kernel_xattr_ctx
      fs/xattr: add *at family syscalls

Colin Ian King (1):
      xattr: remove redundant check on variable err

Jens Axboe (1):
      io_uring: IORING_OP_F[GS]ETXATTR is fine with REQ_F_FIXED_FILE

Patch summaries:

getname_maybe_null() introduction (#getname.base-fixed):
01/13) teach filename_lookup() to treat NULL filename as ""
02/13) getname_maybe_null() - the third variant of pathname copy-in

io_uring-side prep:
03/13) io_uring: IORING_OP_F[GS]ETXATTR is fine with REQ_F_FIXED_FILE
04/13) io_[gs]etxattr_prep(): just use getname()
	getname_flags(_, LOOKUP_FOLLOW) is ridiculous.

05/13)	switch to CLASS(fd) use.  Obvious.

06/13)	rename struct xattr_ctx to kernel_xattr_ctx
prep from the ...xattrat() series, to reduce the PITA for rebase

07/13)	new helper: import_xattr_name()
exact same logics for copying the xattr name in, dealing with
overflows, etc. in a lot of places.  

08/13)	replace do_setxattr() with saner helpers
file_setxattr(file, ctx) and filename_setxattr(dfd, filename, lookup_flags, ctx).
Don't mess with do_setxattr() directly - use those.  In particular, don't
bother with the ESTALE loop in io_uring - it doesn't belong there.
 
09/13)	replace do_getxattr() with saner helpers
Same on getxattr() side.

10/13)	new helpers: file_listxattr(), filename_listxattr()
Same, except that io_uring doesn't use those, so the helpers are left static.

11/13)	new helpers: file_removexattr(), filename_removexattr()
Ditto

12/13)	fs/xattr: add *at family syscalls
Rebased patch introducing those, with a bunch of fixes folded in so we don't
create bisect hazard there.  Potentially interesting bit is the pathname-handling
logics - getname_maybe_null(pathname, flags) returns a struct filename reference
if no AT_EMPTY_PATH had been given or if the pathname was non-NULL and turned out
to be non-empty.  With (NULL,AT_EMPTY_PATH) or (empty string, AT_EMPTY_PATH) it
returns NULL (and it tries to skip the allocation using the same trick with
get_user() that vfs_empty_path() uses).  That helper simplifies a lot of things,
and it should be cheap enough to convert fsetxattr(2) et.al. to the unified
path_setxattrat() and its ilk.  IF we get a slowdown on those, we can always
expand and simplify the calls in fsetxattr(2) and friends.

13/13) xattr: remove redundant check on variable err

^ permalink raw reply	[flat|nested] 50+ messages in thread

* [PATCH v2 01/13] teach filename_lookup() to treat NULL filename as ""
  2024-11-02  7:28 ` [RFC][PATCHES v2] xattr stuff and interactions with io_uring Al Viro
@ 2024-11-02  7:31   ` Al Viro
  2024-11-02  7:31     ` [PATCH v2 02/13] getname_maybe_null() - the third variant of pathname copy-in Al Viro
                       ` (11 more replies)
  2024-11-02 14:43   ` [RFC][PATCHES v2] xattr stuff and interactions with io_uring Jens Axboe
  1 sibling, 12 replies; 50+ messages in thread
From: Al Viro @ 2024-11-02  7:31 UTC (permalink / raw)
  To: linux-fsdevel; +Cc: Christian Brauner, io-uring, Christian Göttsche

Signed-off-by: Al Viro <[email protected]>
---
 fs/namei.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/fs/namei.c b/fs/namei.c
index 4a4a22a08ac2..aaf3cd6c802f 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -588,6 +588,7 @@ struct nameidata {
 		unsigned seq;
 	} *stack, internal[EMBEDDED_LEVELS];
 	struct filename	*name;
+	const char *pathname;
 	struct nameidata *saved;
 	unsigned	root_seq;
 	int		dfd;
@@ -606,6 +607,7 @@ static void __set_nameidata(struct nameidata *p, int dfd, struct filename *name)
 	p->depth = 0;
 	p->dfd = dfd;
 	p->name = name;
+	p->pathname = likely(name) ? name->name : "";
 	p->path.mnt = NULL;
 	p->path.dentry = NULL;
 	p->total_link_count = old ? old->total_link_count : 0;
@@ -2439,7 +2441,7 @@ static int link_path_walk(const char *name, struct nameidata *nd)
 static const char *path_init(struct nameidata *nd, unsigned flags)
 {
 	int error;
-	const char *s = nd->name->name;
+	const char *s = nd->pathname;
 
 	/* LOOKUP_CACHED requires RCU, ask caller to retry */
 	if ((flags & (LOOKUP_RCU | LOOKUP_CACHED)) == LOOKUP_CACHED)
-- 
2.39.5


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH v2 02/13] getname_maybe_null() - the third variant of pathname copy-in
  2024-11-02  7:31   ` [PATCH v2 01/13] teach filename_lookup() to treat NULL filename as "" Al Viro
@ 2024-11-02  7:31     ` Al Viro
  2024-11-02  7:31     ` [PATCH v2 03/13] io_uring: IORING_OP_F[GS]ETXATTR is fine with REQ_F_FIXED_FILE Al Viro
                       ` (10 subsequent siblings)
  11 siblings, 0 replies; 50+ messages in thread
From: Al Viro @ 2024-11-02  7:31 UTC (permalink / raw)
  To: linux-fsdevel; +Cc: Christian Brauner, io-uring, Christian Göttsche

Semantics used by statx(2) (and later *xattrat(2)): without AT_EMPTY_PATH
it's standard getname() (i.e. ERR_PTR(-ENOENT) on empty string,
ERR_PTR(-EFAULT) on NULL), with AT_EMPTY_PATH both empty string and
NULL are accepted.

Calling conventions: getname_maybe_null(user_pointer, flags) returns
	* pointer to struct filename when non-empty string had been
successfully read
	* ERR_PTR(...) on error
	* NULL if an empty string or NULL pointer had been given
with AT_EMPTY_PATH in the flags argument.

It tries to avoid allocation in the last case; it's not always
able to do so, in which case the temporary struct filename instance
is freed and NULL returned anyway.

Fast path is inlined.

Signed-off-by: Al Viro <[email protected]>
---
 fs/namei.c         | 30 +++++++++++++++++++++++-------
 fs/stat.c          | 28 ++++------------------------
 include/linux/fs.h | 10 ++++++++++
 3 files changed, 37 insertions(+), 31 deletions(-)

diff --git a/fs/namei.c b/fs/namei.c
index aaf3cd6c802f..2bfe476c3bd0 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -211,22 +211,38 @@ getname_flags(const char __user *filename, int flags)
 	return result;
 }
 
-struct filename *
-getname_uflags(const char __user *filename, int uflags)
+struct filename *getname_uflags(const char __user *filename, int uflags)
 {
 	int flags = (uflags & AT_EMPTY_PATH) ? LOOKUP_EMPTY : 0;
 
 	return getname_flags(filename, flags);
 }
 
-struct filename *
-getname(const char __user * filename)
+struct filename *getname(const char __user * filename)
 {
 	return getname_flags(filename, 0);
 }
 
-struct filename *
-getname_kernel(const char * filename)
+struct filename *__getname_maybe_null(const char __user *pathname)
+{
+	struct filename *name;
+	char c;
+
+	/* try to save on allocations; loss on um, though */
+	if (get_user(c, pathname))
+		return ERR_PTR(-EFAULT);
+	if (!c)
+		return NULL;
+
+	name = getname_flags(pathname, LOOKUP_EMPTY);
+	if (!IS_ERR(name) && !(name->name[0])) {
+		putname(name);
+		name = NULL;
+	}
+	return name;
+}
+
+struct filename *getname_kernel(const char * filename)
 {
 	struct filename *result;
 	int len = strlen(filename) + 1;
@@ -264,7 +280,7 @@ EXPORT_SYMBOL(getname_kernel);
 
 void putname(struct filename *name)
 {
-	if (IS_ERR(name))
+	if (IS_ERR_OR_NULL(name))
 		return;
 
 	if (WARN_ON_ONCE(!atomic_read(&name->refcnt)))
diff --git a/fs/stat.c b/fs/stat.c
index 41e598376d7e..b74831dc7ae6 100644
--- a/fs/stat.c
+++ b/fs/stat.c
@@ -326,18 +326,11 @@ int vfs_fstatat(int dfd, const char __user *filename,
 {
 	int ret;
 	int statx_flags = flags | AT_NO_AUTOMOUNT;
-	struct filename *name;
+	struct filename *name = getname_maybe_null(filename, flags);
 
-	/*
-	 * Work around glibc turning fstat() into fstatat(AT_EMPTY_PATH)
-	 *
-	 * If AT_EMPTY_PATH is set, we expect the common case to be that
-	 * empty path, and avoid doing all the extra pathname work.
-	 */
-	if (flags == AT_EMPTY_PATH && vfs_empty_path(dfd, filename))
+	if (!name && dfd >= 0)
 		return vfs_fstat(dfd, stat);
 
-	name = getname_flags(filename, getname_statx_lookup_flags(statx_flags));
 	ret = vfs_statx(dfd, name, statx_flags, stat, STATX_BASIC_STATS);
 	putname(name);
 
@@ -774,24 +767,11 @@ SYSCALL_DEFINE5(statx,
 		struct statx __user *, buffer)
 {
 	int ret;
-	unsigned lflags;
-	struct filename *name;
+	struct filename *name = getname_maybe_null(filename, flags);
 
-	/*
-	 * Short-circuit handling of NULL and "" paths.
-	 *
-	 * For a NULL path we require and accept only the AT_EMPTY_PATH flag
-	 * (possibly |'d with AT_STATX flags).
-	 *
-	 * However, glibc on 32-bit architectures implements fstatat as statx
-	 * with the "" pathname and AT_NO_AUTOMOUNT | AT_EMPTY_PATH flags.
-	 * Supporting this results in the uglification below.
-	 */
-	lflags = flags & ~(AT_NO_AUTOMOUNT | AT_STATX_SYNC_TYPE);
-	if (lflags == AT_EMPTY_PATH && vfs_empty_path(dfd, filename))
+	if (!name && dfd >= 0)
 		return do_statx_fd(dfd, flags & ~AT_NO_AUTOMOUNT, mask, buffer);
 
-	name = getname_flags(filename, getname_statx_lookup_flags(flags));
 	ret = do_statx(dfd, name, flags, mask, buffer);
 	putname(name);
 
diff --git a/include/linux/fs.h b/include/linux/fs.h
index e3c603d01337..403258ac2ea2 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -2766,6 +2766,16 @@ extern struct filename *getname_flags(const char __user *, int);
 extern struct filename *getname_uflags(const char __user *, int);
 extern struct filename *getname(const char __user *);
 extern struct filename *getname_kernel(const char *);
+extern struct filename *__getname_maybe_null(const char __user *);
+static inline struct filename *getname_maybe_null(const char __user *name, int flags)
+{
+	if (!(flags & AT_EMPTY_PATH))
+		return getname(name);
+
+	if (!name)
+		return NULL;
+	return __getname_maybe_null(name);
+}
 extern void putname(struct filename *name);
 
 extern int finish_open(struct file *file, struct dentry *dentry,
-- 
2.39.5


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH v2 03/13] io_uring: IORING_OP_F[GS]ETXATTR is fine with REQ_F_FIXED_FILE
  2024-11-02  7:31   ` [PATCH v2 01/13] teach filename_lookup() to treat NULL filename as "" Al Viro
  2024-11-02  7:31     ` [PATCH v2 02/13] getname_maybe_null() - the third variant of pathname copy-in Al Viro
@ 2024-11-02  7:31     ` Al Viro
  2024-11-02  7:31     ` [PATCH v2 04/13] io_[gs]etxattr_prep(): just use getname() Al Viro
                       ` (9 subsequent siblings)
  11 siblings, 0 replies; 50+ messages in thread
From: Al Viro @ 2024-11-02  7:31 UTC (permalink / raw)
  To: linux-fsdevel; +Cc: Christian Brauner, io-uring, Christian Göttsche

From: Jens Axboe <[email protected]>

Rejection of IOSQE_FIXED_FILE combined with IORING_OP_[GS]ETXATTR
is fine - these do not take a file descriptor, so such combination
makes no sense.  The checks are misplaced, though - as it is, they
triggers on IORING_OP_F[GS]ETXATTR as well, and those do take
a file reference, no matter the origin.

Signed-off-by: Jens Axboe <[email protected]>
Signed-off-by: Al Viro <[email protected]>
---
 io_uring/xattr.c | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/io_uring/xattr.c b/io_uring/xattr.c
index 6cf41c3bc369..4b68c282c91a 100644
--- a/io_uring/xattr.c
+++ b/io_uring/xattr.c
@@ -48,9 +48,6 @@ static int __io_getxattr_prep(struct io_kiocb *req,
 	const char __user *name;
 	int ret;
 
-	if (unlikely(req->flags & REQ_F_FIXED_FILE))
-		return -EBADF;
-
 	ix->filename = NULL;
 	ix->ctx.kvalue = NULL;
 	name = u64_to_user_ptr(READ_ONCE(sqe->addr));
@@ -90,6 +87,9 @@ int io_getxattr_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
 	const char __user *path;
 	int ret;
 
+	if (unlikely(req->flags & REQ_F_FIXED_FILE))
+		return -EBADF;
+
 	ret = __io_getxattr_prep(req, sqe);
 	if (ret)
 		return ret;
@@ -152,9 +152,6 @@ static int __io_setxattr_prep(struct io_kiocb *req,
 	const char __user *name;
 	int ret;
 
-	if (unlikely(req->flags & REQ_F_FIXED_FILE))
-		return -EBADF;
-
 	ix->filename = NULL;
 	name = u64_to_user_ptr(READ_ONCE(sqe->addr));
 	ix->ctx.cvalue = u64_to_user_ptr(READ_ONCE(sqe->addr2));
@@ -183,6 +180,9 @@ int io_setxattr_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
 	const char __user *path;
 	int ret;
 
+	if (unlikely(req->flags & REQ_F_FIXED_FILE))
+		return -EBADF;
+
 	ret = __io_setxattr_prep(req, sqe);
 	if (ret)
 		return ret;
-- 
2.39.5


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH v2 04/13] io_[gs]etxattr_prep(): just use getname()
  2024-11-02  7:31   ` [PATCH v2 01/13] teach filename_lookup() to treat NULL filename as "" Al Viro
  2024-11-02  7:31     ` [PATCH v2 02/13] getname_maybe_null() - the third variant of pathname copy-in Al Viro
  2024-11-02  7:31     ` [PATCH v2 03/13] io_uring: IORING_OP_F[GS]ETXATTR is fine with REQ_F_FIXED_FILE Al Viro
@ 2024-11-02  7:31     ` Al Viro
  2024-11-02 14:44       ` Jens Axboe
  2024-11-02  7:31     ` [PATCH v2 05/13] xattr: switch to CLASS(fd) Al Viro
                       ` (8 subsequent siblings)
  11 siblings, 1 reply; 50+ messages in thread
From: Al Viro @ 2024-11-02  7:31 UTC (permalink / raw)
  To: linux-fsdevel; +Cc: Christian Brauner, io-uring, Christian Göttsche

getname_flags(pathname, LOOKUP_FOLLOW) is obviously bogus - following
trailing symlinks has no impact on how to copy the pathname from userland...

Reviewed-by: Christian Brauner <[email protected]>
Signed-off-by: Al Viro <[email protected]>
---
 io_uring/xattr.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/io_uring/xattr.c b/io_uring/xattr.c
index 4b68c282c91a..967c5d8da061 100644
--- a/io_uring/xattr.c
+++ b/io_uring/xattr.c
@@ -96,7 +96,7 @@ int io_getxattr_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
 
 	path = u64_to_user_ptr(READ_ONCE(sqe->addr3));
 
-	ix->filename = getname_flags(path, LOOKUP_FOLLOW);
+	ix->filename = getname(path);
 	if (IS_ERR(ix->filename)) {
 		ret = PTR_ERR(ix->filename);
 		ix->filename = NULL;
@@ -189,7 +189,7 @@ int io_setxattr_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
 
 	path = u64_to_user_ptr(READ_ONCE(sqe->addr3));
 
-	ix->filename = getname_flags(path, LOOKUP_FOLLOW);
+	ix->filename = getname(path);
 	if (IS_ERR(ix->filename)) {
 		ret = PTR_ERR(ix->filename);
 		ix->filename = NULL;
-- 
2.39.5


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH v2 05/13] xattr: switch to CLASS(fd)
  2024-11-02  7:31   ` [PATCH v2 01/13] teach filename_lookup() to treat NULL filename as "" Al Viro
                       ` (2 preceding siblings ...)
  2024-11-02  7:31     ` [PATCH v2 04/13] io_[gs]etxattr_prep(): just use getname() Al Viro
@ 2024-11-02  7:31     ` Al Viro
  2024-11-02  7:31     ` [PATCH v2 06/13] fs: rename struct xattr_ctx to kernel_xattr_ctx Al Viro
                       ` (7 subsequent siblings)
  11 siblings, 0 replies; 50+ messages in thread
From: Al Viro @ 2024-11-02  7:31 UTC (permalink / raw)
  To: linux-fsdevel; +Cc: Christian Brauner, io-uring, Christian Göttsche

Reviewed-by: Christian Brauner <[email protected]>
Signed-off-by: Al Viro <[email protected]>
---
 fs/xattr.c | 35 ++++++++++++++---------------------
 1 file changed, 14 insertions(+), 21 deletions(-)

diff --git a/fs/xattr.c b/fs/xattr.c
index 05ec7e7d9e87..0fc813cb005c 100644
--- a/fs/xattr.c
+++ b/fs/xattr.c
@@ -697,9 +697,9 @@ SYSCALL_DEFINE5(fsetxattr, int, fd, const char __user *, name,
 	int error;
 
 	CLASS(fd, f)(fd);
-	if (!fd_file(f))
-		return -EBADF;
 
+	if (fd_empty(f))
+		return -EBADF;
 	audit_file(fd_file(f));
 	error = setxattr_copy(name, &ctx);
 	if (error)
@@ -809,16 +809,13 @@ SYSCALL_DEFINE4(lgetxattr, const char __user *, pathname,
 SYSCALL_DEFINE4(fgetxattr, int, fd, const char __user *, name,
 		void __user *, value, size_t, size)
 {
-	struct fd f = fdget(fd);
-	ssize_t error = -EBADF;
+	CLASS(fd, f)(fd);
 
-	if (!fd_file(f))
-		return error;
+	if (fd_empty(f))
+		return -EBADF;
 	audit_file(fd_file(f));
-	error = getxattr(file_mnt_idmap(fd_file(f)), fd_file(f)->f_path.dentry,
+	return getxattr(file_mnt_idmap(fd_file(f)), fd_file(f)->f_path.dentry,
 			 name, value, size);
-	fdput(f);
-	return error;
 }
 
 /*
@@ -885,15 +882,12 @@ SYSCALL_DEFINE3(llistxattr, const char __user *, pathname, char __user *, list,
 
 SYSCALL_DEFINE3(flistxattr, int, fd, char __user *, list, size_t, size)
 {
-	struct fd f = fdget(fd);
-	ssize_t error = -EBADF;
+	CLASS(fd, f)(fd);
 
-	if (!fd_file(f))
-		return error;
+	if (fd_empty(f))
+		return -EBADF;
 	audit_file(fd_file(f));
-	error = listxattr(fd_file(f)->f_path.dentry, list, size);
-	fdput(f);
-	return error;
+	return listxattr(fd_file(f)->f_path.dentry, list, size);
 }
 
 /*
@@ -950,12 +944,12 @@ SYSCALL_DEFINE2(lremovexattr, const char __user *, pathname,
 
 SYSCALL_DEFINE2(fremovexattr, int, fd, const char __user *, name)
 {
-	struct fd f = fdget(fd);
+	CLASS(fd, f)(fd);
 	char kname[XATTR_NAME_MAX + 1];
-	int error = -EBADF;
+	int error;
 
-	if (!fd_file(f))
-		return error;
+	if (fd_empty(f))
+		return -EBADF;
 	audit_file(fd_file(f));
 
 	error = strncpy_from_user(kname, name, sizeof(kname));
@@ -970,7 +964,6 @@ SYSCALL_DEFINE2(fremovexattr, int, fd, const char __user *, name)
 				    fd_file(f)->f_path.dentry, kname);
 		mnt_drop_write_file(fd_file(f));
 	}
-	fdput(f);
 	return error;
 }
 
-- 
2.39.5


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH v2 06/13] fs: rename struct xattr_ctx to kernel_xattr_ctx
  2024-11-02  7:31   ` [PATCH v2 01/13] teach filename_lookup() to treat NULL filename as "" Al Viro
                       ` (3 preceding siblings ...)
  2024-11-02  7:31     ` [PATCH v2 05/13] xattr: switch to CLASS(fd) Al Viro
@ 2024-11-02  7:31     ` Al Viro
  2024-11-02  7:31     ` [PATCH v2 07/13] new helper: import_xattr_name() Al Viro
                       ` (6 subsequent siblings)
  11 siblings, 0 replies; 50+ messages in thread
From: Al Viro @ 2024-11-02  7:31 UTC (permalink / raw)
  To: linux-fsdevel; +Cc: Christian Brauner, io-uring, Christian Göttsche

From: Christian Göttsche <[email protected]>

Rename the struct xattr_ctx to increase distinction with the about to be
added user API struct xattr_args.

No functional change.

Suggested-by: Christian Brauner <[email protected]>
Signed-off-by: Christian Göttsche <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
Reviewed-by: Jan Kara <[email protected]>
Signed-off-by: Christian Brauner <[email protected]>
---
 fs/internal.h    |  8 ++++----
 fs/xattr.c       | 12 ++++++------
 io_uring/xattr.c |  2 +-
 3 files changed, 11 insertions(+), 11 deletions(-)

diff --git a/fs/internal.h b/fs/internal.h
index 8c1b7acbbe8f..81c7a085355c 100644
--- a/fs/internal.h
+++ b/fs/internal.h
@@ -267,7 +267,7 @@ struct xattr_name {
 	char name[XATTR_NAME_MAX + 1];
 };
 
-struct xattr_ctx {
+struct kernel_xattr_ctx {
 	/* Value of attribute */
 	union {
 		const void __user *cvalue;
@@ -283,11 +283,11 @@ struct xattr_ctx {
 
 ssize_t do_getxattr(struct mnt_idmap *idmap,
 		    struct dentry *d,
-		    struct xattr_ctx *ctx);
+		    struct kernel_xattr_ctx *ctx);
 
-int setxattr_copy(const char __user *name, struct xattr_ctx *ctx);
+int setxattr_copy(const char __user *name, struct kernel_xattr_ctx *ctx);
 int do_setxattr(struct mnt_idmap *idmap, struct dentry *dentry,
-		struct xattr_ctx *ctx);
+		struct kernel_xattr_ctx *ctx);
 int may_write_xattr(struct mnt_idmap *idmap, struct inode *inode);
 
 #ifdef CONFIG_FS_POSIX_ACL
diff --git a/fs/xattr.c b/fs/xattr.c
index 0fc813cb005c..1214ae7e71db 100644
--- a/fs/xattr.c
+++ b/fs/xattr.c
@@ -590,7 +590,7 @@ EXPORT_SYMBOL_GPL(vfs_removexattr);
  * Extended attribute SET operations
  */
 
-int setxattr_copy(const char __user *name, struct xattr_ctx *ctx)
+int setxattr_copy(const char __user *name, struct kernel_xattr_ctx *ctx)
 {
 	int error;
 
@@ -620,7 +620,7 @@ int setxattr_copy(const char __user *name, struct xattr_ctx *ctx)
 }
 
 int do_setxattr(struct mnt_idmap *idmap, struct dentry *dentry,
-		struct xattr_ctx *ctx)
+		struct kernel_xattr_ctx *ctx)
 {
 	if (is_posix_acl_xattr(ctx->kname->name))
 		return do_set_acl(idmap, dentry, ctx->kname->name,
@@ -635,7 +635,7 @@ static int path_setxattr(const char __user *pathname,
 			 size_t size, int flags, unsigned int lookup_flags)
 {
 	struct xattr_name kname;
-	struct xattr_ctx ctx = {
+	struct kernel_xattr_ctx ctx = {
 		.cvalue   = value,
 		.kvalue   = NULL,
 		.size     = size,
@@ -687,7 +687,7 @@ SYSCALL_DEFINE5(fsetxattr, int, fd, const char __user *, name,
 		const void __user *,value, size_t, size, int, flags)
 {
 	struct xattr_name kname;
-	struct xattr_ctx ctx = {
+	struct kernel_xattr_ctx ctx = {
 		.cvalue   = value,
 		.kvalue   = NULL,
 		.size     = size,
@@ -720,7 +720,7 @@ SYSCALL_DEFINE5(fsetxattr, int, fd, const char __user *, name,
  */
 ssize_t
 do_getxattr(struct mnt_idmap *idmap, struct dentry *d,
-	struct xattr_ctx *ctx)
+	struct kernel_xattr_ctx *ctx)
 {
 	ssize_t error;
 	char *kname = ctx->kname->name;
@@ -755,7 +755,7 @@ getxattr(struct mnt_idmap *idmap, struct dentry *d,
 {
 	ssize_t error;
 	struct xattr_name kname;
-	struct xattr_ctx ctx = {
+	struct kernel_xattr_ctx ctx = {
 		.value    = value,
 		.kvalue   = NULL,
 		.size     = size,
diff --git a/io_uring/xattr.c b/io_uring/xattr.c
index 967c5d8da061..f440121c3984 100644
--- a/io_uring/xattr.c
+++ b/io_uring/xattr.c
@@ -18,7 +18,7 @@
 
 struct io_xattr {
 	struct file			*file;
-	struct xattr_ctx		ctx;
+	struct kernel_xattr_ctx		ctx;
 	struct filename			*filename;
 };
 
-- 
2.39.5


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH v2 07/13] new helper: import_xattr_name()
  2024-11-02  7:31   ` [PATCH v2 01/13] teach filename_lookup() to treat NULL filename as "" Al Viro
                       ` (4 preceding siblings ...)
  2024-11-02  7:31     ` [PATCH v2 06/13] fs: rename struct xattr_ctx to kernel_xattr_ctx Al Viro
@ 2024-11-02  7:31     ` Al Viro
  2024-11-02  7:31     ` [PATCH v2 08/13] replace do_setxattr() with saner helpers Al Viro
                       ` (5 subsequent siblings)
  11 siblings, 0 replies; 50+ messages in thread
From: Al Viro @ 2024-11-02  7:31 UTC (permalink / raw)
  To: linux-fsdevel; +Cc: Christian Brauner, io-uring, Christian Göttsche

common logics for marshalling xattr names.

Reviewed-by: Christian Brauner <[email protected]>
Signed-off-by: Al Viro <[email protected]>
---
 fs/internal.h    |  3 +++
 fs/xattr.c       | 45 +++++++++++++++++++++++----------------------
 io_uring/xattr.c |  7 ++-----
 3 files changed, 28 insertions(+), 27 deletions(-)

diff --git a/fs/internal.h b/fs/internal.h
index 81c7a085355c..b9f5ac4d39fc 100644
--- a/fs/internal.h
+++ b/fs/internal.h
@@ -288,6 +288,9 @@ ssize_t do_getxattr(struct mnt_idmap *idmap,
 int setxattr_copy(const char __user *name, struct kernel_xattr_ctx *ctx);
 int do_setxattr(struct mnt_idmap *idmap, struct dentry *dentry,
 		struct kernel_xattr_ctx *ctx);
+
+int import_xattr_name(struct xattr_name *kname, const char __user *name);
+
 int may_write_xattr(struct mnt_idmap *idmap, struct inode *inode);
 
 #ifdef CONFIG_FS_POSIX_ACL
diff --git a/fs/xattr.c b/fs/xattr.c
index 1214ae7e71db..d8f7c766f28a 100644
--- a/fs/xattr.c
+++ b/fs/xattr.c
@@ -586,6 +586,17 @@ vfs_removexattr(struct mnt_idmap *idmap, struct dentry *dentry,
 }
 EXPORT_SYMBOL_GPL(vfs_removexattr);
 
+int import_xattr_name(struct xattr_name *kname, const char __user *name)
+{
+	int error = strncpy_from_user(kname->name, name,
+					sizeof(kname->name));
+	if (error == 0 || error == sizeof(kname->name))
+		return -ERANGE;
+	if (error < 0)
+		return error;
+	return 0;
+}
+
 /*
  * Extended attribute SET operations
  */
@@ -597,14 +608,10 @@ int setxattr_copy(const char __user *name, struct kernel_xattr_ctx *ctx)
 	if (ctx->flags & ~(XATTR_CREATE|XATTR_REPLACE))
 		return -EINVAL;
 
-	error = strncpy_from_user(ctx->kname->name, name,
-				sizeof(ctx->kname->name));
-	if (error == 0 || error == sizeof(ctx->kname->name))
-		return  -ERANGE;
-	if (error < 0)
+	error = import_xattr_name(ctx->kname, name);
+	if (error)
 		return error;
 
-	error = 0;
 	if (ctx->size) {
 		if (ctx->size > XATTR_SIZE_MAX)
 			return -E2BIG;
@@ -763,10 +770,8 @@ getxattr(struct mnt_idmap *idmap, struct dentry *d,
 		.flags    = 0,
 	};
 
-	error = strncpy_from_user(kname.name, name, sizeof(kname.name));
-	if (error == 0 || error == sizeof(kname.name))
-		error = -ERANGE;
-	if (error < 0)
+	error = import_xattr_name(&kname, name);
+	if (error)
 		return error;
 
 	error =  do_getxattr(idmap, d, &ctx);
@@ -906,12 +911,10 @@ static int path_removexattr(const char __user *pathname,
 {
 	struct path path;
 	int error;
-	char kname[XATTR_NAME_MAX + 1];
+	struct xattr_name kname;
 
-	error = strncpy_from_user(kname, name, sizeof(kname));
-	if (error == 0 || error == sizeof(kname))
-		error = -ERANGE;
-	if (error < 0)
+	error = import_xattr_name(&kname, name);
+	if (error)
 		return error;
 retry:
 	error = user_path_at(AT_FDCWD, pathname, lookup_flags, &path);
@@ -919,7 +922,7 @@ static int path_removexattr(const char __user *pathname,
 		return error;
 	error = mnt_want_write(path.mnt);
 	if (!error) {
-		error = removexattr(mnt_idmap(path.mnt), path.dentry, kname);
+		error = removexattr(mnt_idmap(path.mnt), path.dentry, kname.name);
 		mnt_drop_write(path.mnt);
 	}
 	path_put(&path);
@@ -945,23 +948,21 @@ SYSCALL_DEFINE2(lremovexattr, const char __user *, pathname,
 SYSCALL_DEFINE2(fremovexattr, int, fd, const char __user *, name)
 {
 	CLASS(fd, f)(fd);
-	char kname[XATTR_NAME_MAX + 1];
+	struct xattr_name kname;
 	int error;
 
 	if (fd_empty(f))
 		return -EBADF;
 	audit_file(fd_file(f));
 
-	error = strncpy_from_user(kname, name, sizeof(kname));
-	if (error == 0 || error == sizeof(kname))
-		error = -ERANGE;
-	if (error < 0)
+	error = import_xattr_name(&kname, name);
+	if (error)
 		return error;
 
 	error = mnt_want_write_file(fd_file(f));
 	if (!error) {
 		error = removexattr(file_mnt_idmap(fd_file(f)),
-				    fd_file(f)->f_path.dentry, kname);
+				    fd_file(f)->f_path.dentry, kname.name);
 		mnt_drop_write_file(fd_file(f));
 	}
 	return error;
diff --git a/io_uring/xattr.c b/io_uring/xattr.c
index f440121c3984..0b3b871eaa65 100644
--- a/io_uring/xattr.c
+++ b/io_uring/xattr.c
@@ -62,11 +62,8 @@ static int __io_getxattr_prep(struct io_kiocb *req,
 	if (!ix->ctx.kname)
 		return -ENOMEM;
 
-	ret = strncpy_from_user(ix->ctx.kname->name, name,
-				sizeof(ix->ctx.kname->name));
-	if (!ret || ret == sizeof(ix->ctx.kname->name))
-		ret = -ERANGE;
-	if (ret < 0) {
+	ret = import_xattr_name(ix->ctx.kname, name);
+	if (ret) {
 		kfree(ix->ctx.kname);
 		return ret;
 	}
-- 
2.39.5


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH v2 08/13] replace do_setxattr() with saner helpers.
  2024-11-02  7:31   ` [PATCH v2 01/13] teach filename_lookup() to treat NULL filename as "" Al Viro
                       ` (5 preceding siblings ...)
  2024-11-02  7:31     ` [PATCH v2 07/13] new helper: import_xattr_name() Al Viro
@ 2024-11-02  7:31     ` Al Viro
  2024-11-02  7:31     ` [PATCH v2 09/13] replace do_getxattr() " Al Viro
                       ` (4 subsequent siblings)
  11 siblings, 0 replies; 50+ messages in thread
From: Al Viro @ 2024-11-02  7:31 UTC (permalink / raw)
  To: linux-fsdevel; +Cc: Christian Brauner, io-uring, Christian Göttsche

io_uring setxattr logics duplicates stuff from fs/xattr.c; provide
saner helpers (filename_setxattr() and file_setxattr() resp.) and
use them.

NB: putname(ERR_PTR()) is a no-op

Signed-off-by: Al Viro <[email protected]>
---
 fs/internal.h    |  6 ++---
 fs/xattr.c       | 69 ++++++++++++++++++++++++++++++------------------
 io_uring/xattr.c | 41 +++++-----------------------
 3 files changed, 54 insertions(+), 62 deletions(-)

diff --git a/fs/internal.h b/fs/internal.h
index b9f5ac4d39fc..be7c0da3bcec 100644
--- a/fs/internal.h
+++ b/fs/internal.h
@@ -285,10 +285,10 @@ ssize_t do_getxattr(struct mnt_idmap *idmap,
 		    struct dentry *d,
 		    struct kernel_xattr_ctx *ctx);
 
+int file_setxattr(struct file *file, struct kernel_xattr_ctx *ctx);
+int filename_setxattr(int dfd, struct filename *filename,
+		      unsigned int lookup_flags, struct kernel_xattr_ctx *ctx);
 int setxattr_copy(const char __user *name, struct kernel_xattr_ctx *ctx);
-int do_setxattr(struct mnt_idmap *idmap, struct dentry *dentry,
-		struct kernel_xattr_ctx *ctx);
-
 int import_xattr_name(struct xattr_name *kname, const char __user *name);
 
 int may_write_xattr(struct mnt_idmap *idmap, struct inode *inode);
diff --git a/fs/xattr.c b/fs/xattr.c
index d8f7c766f28a..38bf8cfbd464 100644
--- a/fs/xattr.c
+++ b/fs/xattr.c
@@ -626,7 +626,7 @@ int setxattr_copy(const char __user *name, struct kernel_xattr_ctx *ctx)
 	return error;
 }
 
-int do_setxattr(struct mnt_idmap *idmap, struct dentry *dentry,
+static int do_setxattr(struct mnt_idmap *idmap, struct dentry *dentry,
 		struct kernel_xattr_ctx *ctx)
 {
 	if (is_posix_acl_xattr(ctx->kname->name))
@@ -637,32 +637,32 @@ int do_setxattr(struct mnt_idmap *idmap, struct dentry *dentry,
 			ctx->kvalue, ctx->size, ctx->flags);
 }
 
-static int path_setxattr(const char __user *pathname,
-			 const char __user *name, const void __user *value,
-			 size_t size, int flags, unsigned int lookup_flags)
+int file_setxattr(struct file *f, struct kernel_xattr_ctx *ctx)
+{
+	int error = mnt_want_write_file(f);
+
+	if (!error) {
+		audit_file(f);
+		error = do_setxattr(file_mnt_idmap(f), f->f_path.dentry, ctx);
+		mnt_drop_write_file(f);
+	}
+	return error;
+}
+
+/* unconditionally consumes filename */
+int filename_setxattr(int dfd, struct filename *filename,
+		      unsigned int lookup_flags, struct kernel_xattr_ctx *ctx)
 {
-	struct xattr_name kname;
-	struct kernel_xattr_ctx ctx = {
-		.cvalue   = value,
-		.kvalue   = NULL,
-		.size     = size,
-		.kname    = &kname,
-		.flags    = flags,
-	};
 	struct path path;
 	int error;
 
-	error = setxattr_copy(name, &ctx);
-	if (error)
-		return error;
-
 retry:
-	error = user_path_at(AT_FDCWD, pathname, lookup_flags, &path);
+	error = filename_lookup(dfd, filename, lookup_flags, &path, NULL);
 	if (error)
 		goto out;
 	error = mnt_want_write(path.mnt);
 	if (!error) {
-		error = do_setxattr(mnt_idmap(path.mnt), path.dentry, &ctx);
+		error = do_setxattr(mnt_idmap(path.mnt), path.dentry, ctx);
 		mnt_drop_write(path.mnt);
 	}
 	path_put(&path);
@@ -672,6 +672,30 @@ static int path_setxattr(const char __user *pathname,
 	}
 
 out:
+	putname(filename);
+	return error;
+}
+
+static int path_setxattr(const char __user *pathname,
+			 const char __user *name, const void __user *value,
+			 size_t size, int flags, unsigned int lookup_flags)
+{
+	struct xattr_name kname;
+	struct kernel_xattr_ctx ctx = {
+		.cvalue   = value,
+		.kvalue   = NULL,
+		.size     = size,
+		.kname    = &kname,
+		.flags    = flags,
+	};
+	int error;
+
+	error = setxattr_copy(name, &ctx);
+	if (error)
+		return error;
+
+	error = filename_setxattr(AT_FDCWD, getname(pathname), lookup_flags,
+				  &ctx);
 	kvfree(ctx.kvalue);
 	return error;
 }
@@ -707,17 +731,12 @@ SYSCALL_DEFINE5(fsetxattr, int, fd, const char __user *, name,
 
 	if (fd_empty(f))
 		return -EBADF;
-	audit_file(fd_file(f));
+
 	error = setxattr_copy(name, &ctx);
 	if (error)
 		return error;
 
-	error = mnt_want_write_file(fd_file(f));
-	if (!error) {
-		error = do_setxattr(file_mnt_idmap(fd_file(f)),
-				    fd_file(f)->f_path.dentry, &ctx);
-		mnt_drop_write_file(fd_file(f));
-	}
+	error = file_setxattr(fd_file(f), &ctx);
 	kvfree(ctx.kvalue);
 	return error;
 }
diff --git a/io_uring/xattr.c b/io_uring/xattr.c
index 0b3b871eaa65..2671ad05d63b 100644
--- a/io_uring/xattr.c
+++ b/io_uring/xattr.c
@@ -187,12 +187,10 @@ int io_setxattr_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
 	path = u64_to_user_ptr(READ_ONCE(sqe->addr3));
 
 	ix->filename = getname(path);
-	if (IS_ERR(ix->filename)) {
-		ret = PTR_ERR(ix->filename);
-		ix->filename = NULL;
-	}
+	if (IS_ERR(ix->filename))
+		return PTR_ERR(ix->filename);
 
-	return ret;
+	return 0;
 }
 
 int io_fsetxattr_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
@@ -200,28 +198,14 @@ int io_fsetxattr_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
 	return __io_setxattr_prep(req, sqe);
 }
 
-static int __io_setxattr(struct io_kiocb *req, unsigned int issue_flags,
-			const struct path *path)
-{
-	struct io_xattr *ix = io_kiocb_to_cmd(req, struct io_xattr);
-	int ret;
-
-	ret = mnt_want_write(path->mnt);
-	if (!ret) {
-		ret = do_setxattr(mnt_idmap(path->mnt), path->dentry, &ix->ctx);
-		mnt_drop_write(path->mnt);
-	}
-
-	return ret;
-}
-
 int io_fsetxattr(struct io_kiocb *req, unsigned int issue_flags)
 {
+	struct io_xattr *ix = io_kiocb_to_cmd(req, struct io_xattr);
 	int ret;
 
 	WARN_ON_ONCE(issue_flags & IO_URING_F_NONBLOCK);
 
-	ret = __io_setxattr(req, issue_flags, &req->file->f_path);
+	ret = file_setxattr(req->file, &ix->ctx);
 	io_xattr_finish(req, ret);
 	return IOU_OK;
 }
@@ -229,23 +213,12 @@ int io_fsetxattr(struct io_kiocb *req, unsigned int issue_flags)
 int io_setxattr(struct io_kiocb *req, unsigned int issue_flags)
 {
 	struct io_xattr *ix = io_kiocb_to_cmd(req, struct io_xattr);
-	unsigned int lookup_flags = LOOKUP_FOLLOW;
-	struct path path;
 	int ret;
 
 	WARN_ON_ONCE(issue_flags & IO_URING_F_NONBLOCK);
 
-retry:
-	ret = filename_lookup(AT_FDCWD, ix->filename, lookup_flags, &path, NULL);
-	if (!ret) {
-		ret = __io_setxattr(req, issue_flags, &path);
-		path_put(&path);
-		if (retry_estale(ret, lookup_flags)) {
-			lookup_flags |= LOOKUP_REVAL;
-			goto retry;
-		}
-	}
-
+	ret = filename_setxattr(AT_FDCWD, ix->filename, LOOKUP_FOLLOW, &ix->ctx);
+	ix->filename = NULL;
 	io_xattr_finish(req, ret);
 	return IOU_OK;
 }
-- 
2.39.5


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH v2 09/13] replace do_getxattr() with saner helpers.
  2024-11-02  7:31   ` [PATCH v2 01/13] teach filename_lookup() to treat NULL filename as "" Al Viro
                       ` (6 preceding siblings ...)
  2024-11-02  7:31     ` [PATCH v2 08/13] replace do_setxattr() with saner helpers Al Viro
@ 2024-11-02  7:31     ` Al Viro
  2024-11-02  7:31     ` [PATCH v2 10/13] new helpers: file_listxattr(), filename_listxattr() Al Viro
                       ` (3 subsequent siblings)
  11 siblings, 0 replies; 50+ messages in thread
From: Al Viro @ 2024-11-02  7:31 UTC (permalink / raw)
  To: linux-fsdevel; +Cc: Christian Brauner, io-uring, Christian Göttsche

similar to do_setxattr() in the previous commit...

Reviewed-by: Christian Brauner <[email protected]>
Signed-off-by: Al Viro <[email protected]>
---
 fs/internal.h    |  8 ++---
 fs/xattr.c       | 88 ++++++++++++++++++++++++++++--------------------
 io_uring/xattr.c | 31 ++++-------------
 3 files changed, 61 insertions(+), 66 deletions(-)

diff --git a/fs/internal.h b/fs/internal.h
index be7c0da3bcec..8001efd1f047 100644
--- a/fs/internal.h
+++ b/fs/internal.h
@@ -280,11 +280,9 @@ struct kernel_xattr_ctx {
 	unsigned int flags;
 };
 
-
-ssize_t do_getxattr(struct mnt_idmap *idmap,
-		    struct dentry *d,
-		    struct kernel_xattr_ctx *ctx);
-
+ssize_t file_getxattr(struct file *file, struct kernel_xattr_ctx *ctx);
+ssize_t filename_getxattr(int dfd, struct filename *filename,
+			  unsigned int lookup_flags, struct kernel_xattr_ctx *ctx);
 int file_setxattr(struct file *file, struct kernel_xattr_ctx *ctx);
 int filename_setxattr(int dfd, struct filename *filename,
 		      unsigned int lookup_flags, struct kernel_xattr_ctx *ctx);
diff --git a/fs/xattr.c b/fs/xattr.c
index 38bf8cfbd464..d55f3d1e7589 100644
--- a/fs/xattr.c
+++ b/fs/xattr.c
@@ -744,27 +744,28 @@ SYSCALL_DEFINE5(fsetxattr, int, fd, const char __user *, name,
 /*
  * Extended attribute GET operations
  */
-ssize_t
+static ssize_t
 do_getxattr(struct mnt_idmap *idmap, struct dentry *d,
 	struct kernel_xattr_ctx *ctx)
 {
 	ssize_t error;
 	char *kname = ctx->kname->name;
+	void *kvalue = NULL;
 
 	if (ctx->size) {
 		if (ctx->size > XATTR_SIZE_MAX)
 			ctx->size = XATTR_SIZE_MAX;
-		ctx->kvalue = kvzalloc(ctx->size, GFP_KERNEL);
-		if (!ctx->kvalue)
+		kvalue = kvzalloc(ctx->size, GFP_KERNEL);
+		if (!kvalue)
 			return -ENOMEM;
 	}
 
-	if (is_posix_acl_xattr(ctx->kname->name))
-		error = do_get_acl(idmap, d, kname, ctx->kvalue, ctx->size);
+	if (is_posix_acl_xattr(kname))
+		error = do_get_acl(idmap, d, kname, kvalue, ctx->size);
 	else
-		error = vfs_getxattr(idmap, d, kname, ctx->kvalue, ctx->size);
+		error = vfs_getxattr(idmap, d, kname, kvalue, ctx->size);
 	if (error > 0) {
-		if (ctx->size && copy_to_user(ctx->value, ctx->kvalue, error))
+		if (ctx->size && copy_to_user(ctx->value, kvalue, error))
 			error = -EFAULT;
 	} else if (error == -ERANGE && ctx->size >= XATTR_SIZE_MAX) {
 		/* The file system tried to returned a value bigger
@@ -772,52 +773,56 @@ do_getxattr(struct mnt_idmap *idmap, struct dentry *d,
 		error = -E2BIG;
 	}
 
+	kvfree(kvalue);
 	return error;
 }
 
-static ssize_t
-getxattr(struct mnt_idmap *idmap, struct dentry *d,
-	 const char __user *name, void __user *value, size_t size)
+ssize_t file_getxattr(struct file *f, struct kernel_xattr_ctx *ctx)
 {
-	ssize_t error;
-	struct xattr_name kname;
-	struct kernel_xattr_ctx ctx = {
-		.value    = value,
-		.kvalue   = NULL,
-		.size     = size,
-		.kname    = &kname,
-		.flags    = 0,
-	};
-
-	error = import_xattr_name(&kname, name);
-	if (error)
-		return error;
-
-	error =  do_getxattr(idmap, d, &ctx);
-
-	kvfree(ctx.kvalue);
-	return error;
+	audit_file(f);
+	return do_getxattr(file_mnt_idmap(f), f->f_path.dentry, ctx);
 }
 
-static ssize_t path_getxattr(const char __user *pathname,
-			     const char __user *name, void __user *value,
-			     size_t size, unsigned int lookup_flags)
+/* unconditionally consumes filename */
+ssize_t filename_getxattr(int dfd, struct filename *filename,
+			  unsigned int lookup_flags, struct kernel_xattr_ctx *ctx)
 {
 	struct path path;
 	ssize_t error;
 retry:
-	error = user_path_at(AT_FDCWD, pathname, lookup_flags, &path);
+	error = filename_lookup(dfd, filename, lookup_flags, &path, NULL);
 	if (error)
-		return error;
-	error = getxattr(mnt_idmap(path.mnt), path.dentry, name, value, size);
+		goto out;
+	error = do_getxattr(mnt_idmap(path.mnt), path.dentry, ctx);
 	path_put(&path);
 	if (retry_estale(error, lookup_flags)) {
 		lookup_flags |= LOOKUP_REVAL;
 		goto retry;
 	}
+out:
+	putname(filename);
 	return error;
 }
 
+static ssize_t path_getxattr(const char __user *pathname,
+			     const char __user *name, void __user *value,
+			     size_t size, unsigned int lookup_flags)
+{
+	ssize_t error;
+	struct xattr_name kname;
+	struct kernel_xattr_ctx ctx = {
+		.value    = value,
+		.size     = size,
+		.kname    = &kname,
+		.flags    = 0,
+	};
+
+	error = import_xattr_name(&kname, name);
+	if (error)
+		return error;
+	return filename_getxattr(AT_FDCWD, getname(pathname), lookup_flags, &ctx);
+}
+
 SYSCALL_DEFINE4(getxattr, const char __user *, pathname,
 		const char __user *, name, void __user *, value, size_t, size)
 {
@@ -833,13 +838,22 @@ SYSCALL_DEFINE4(lgetxattr, const char __user *, pathname,
 SYSCALL_DEFINE4(fgetxattr, int, fd, const char __user *, name,
 		void __user *, value, size_t, size)
 {
+	ssize_t error;
+	struct xattr_name kname;
+	struct kernel_xattr_ctx ctx = {
+		.value    = value,
+		.size     = size,
+		.kname    = &kname,
+		.flags    = 0,
+	};
 	CLASS(fd, f)(fd);
 
 	if (fd_empty(f))
 		return -EBADF;
-	audit_file(fd_file(f));
-	return getxattr(file_mnt_idmap(fd_file(f)), fd_file(f)->f_path.dentry,
-			 name, value, size);
+	error = import_xattr_name(&kname, name);
+	if (error)
+		return error;
+	return file_getxattr(fd_file(f), &ctx);
 }
 
 /*
diff --git a/io_uring/xattr.c b/io_uring/xattr.c
index 2671ad05d63b..de5064fcae8a 100644
--- a/io_uring/xattr.c
+++ b/io_uring/xattr.c
@@ -51,7 +51,7 @@ static int __io_getxattr_prep(struct io_kiocb *req,
 	ix->filename = NULL;
 	ix->ctx.kvalue = NULL;
 	name = u64_to_user_ptr(READ_ONCE(sqe->addr));
-	ix->ctx.cvalue = u64_to_user_ptr(READ_ONCE(sqe->addr2));
+	ix->ctx.value = u64_to_user_ptr(READ_ONCE(sqe->addr2));
 	ix->ctx.size = READ_ONCE(sqe->len);
 	ix->ctx.flags = READ_ONCE(sqe->xattr_flags);
 
@@ -94,12 +94,10 @@ int io_getxattr_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
 	path = u64_to_user_ptr(READ_ONCE(sqe->addr3));
 
 	ix->filename = getname(path);
-	if (IS_ERR(ix->filename)) {
-		ret = PTR_ERR(ix->filename);
-		ix->filename = NULL;
-	}
+	if (IS_ERR(ix->filename))
+		return PTR_ERR(ix->filename);
 
-	return ret;
+	return 0;
 }
 
 int io_fgetxattr(struct io_kiocb *req, unsigned int issue_flags)
@@ -109,10 +107,7 @@ int io_fgetxattr(struct io_kiocb *req, unsigned int issue_flags)
 
 	WARN_ON_ONCE(issue_flags & IO_URING_F_NONBLOCK);
 
-	ret = do_getxattr(file_mnt_idmap(req->file),
-			req->file->f_path.dentry,
-			&ix->ctx);
-
+	ret = file_getxattr(req->file, &ix->ctx);
 	io_xattr_finish(req, ret);
 	return IOU_OK;
 }
@@ -120,24 +115,12 @@ int io_fgetxattr(struct io_kiocb *req, unsigned int issue_flags)
 int io_getxattr(struct io_kiocb *req, unsigned int issue_flags)
 {
 	struct io_xattr *ix = io_kiocb_to_cmd(req, struct io_xattr);
-	unsigned int lookup_flags = LOOKUP_FOLLOW;
-	struct path path;
 	int ret;
 
 	WARN_ON_ONCE(issue_flags & IO_URING_F_NONBLOCK);
 
-retry:
-	ret = filename_lookup(AT_FDCWD, ix->filename, lookup_flags, &path, NULL);
-	if (!ret) {
-		ret = do_getxattr(mnt_idmap(path.mnt), path.dentry, &ix->ctx);
-
-		path_put(&path);
-		if (retry_estale(ret, lookup_flags)) {
-			lookup_flags |= LOOKUP_REVAL;
-			goto retry;
-		}
-	}
-
+	ret = filename_getxattr(AT_FDCWD, ix->filename, LOOKUP_FOLLOW, &ix->ctx);
+	ix->filename = NULL;
 	io_xattr_finish(req, ret);
 	return IOU_OK;
 }
-- 
2.39.5


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH v2 10/13] new helpers: file_listxattr(), filename_listxattr()
  2024-11-02  7:31   ` [PATCH v2 01/13] teach filename_lookup() to treat NULL filename as "" Al Viro
                       ` (7 preceding siblings ...)
  2024-11-02  7:31     ` [PATCH v2 09/13] replace do_getxattr() " Al Viro
@ 2024-11-02  7:31     ` Al Viro
  2024-11-02  7:31     ` [PATCH v2 11/13] new helpers: file_removexattr(), filename_removexattr() Al Viro
                       ` (2 subsequent siblings)
  11 siblings, 0 replies; 50+ messages in thread
From: Al Viro @ 2024-11-02  7:31 UTC (permalink / raw)
  To: linux-fsdevel; +Cc: Christian Brauner, io-uring, Christian Göttsche

switch path_listxattr() and flistxattr(2) to those

Reviewed-by: Christian Brauner <[email protected]>
Signed-off-by: Al Viro <[email protected]>
---
 fs/xattr.c | 30 ++++++++++++++++++++++++------
 1 file changed, 24 insertions(+), 6 deletions(-)

diff --git a/fs/xattr.c b/fs/xattr.c
index d55f3d1e7589..988ea737ae7e 100644
--- a/fs/xattr.c
+++ b/fs/xattr.c
@@ -888,24 +888,43 @@ listxattr(struct dentry *d, char __user *list, size_t size)
 	return error;
 }
 
-static ssize_t path_listxattr(const char __user *pathname, char __user *list,
-			      size_t size, unsigned int lookup_flags)
+static
+ssize_t file_listxattr(struct file *f, char __user *list, size_t size)
+{
+	audit_file(f);
+	return listxattr(f->f_path.dentry, list, size);
+}
+
+/* unconditionally consumes filename */
+static
+ssize_t filename_listxattr(int dfd, struct filename *filename,
+			   unsigned int lookup_flags,
+			   char __user *list, size_t size)
 {
 	struct path path;
 	ssize_t error;
 retry:
-	error = user_path_at(AT_FDCWD, pathname, lookup_flags, &path);
+	error = filename_lookup(dfd, filename, lookup_flags, &path, NULL);
 	if (error)
-		return error;
+		goto out;
 	error = listxattr(path.dentry, list, size);
 	path_put(&path);
 	if (retry_estale(error, lookup_flags)) {
 		lookup_flags |= LOOKUP_REVAL;
 		goto retry;
 	}
+out:
+	putname(filename);
 	return error;
 }
 
+static ssize_t path_listxattr(const char __user *pathname, char __user *list,
+			      size_t size, unsigned int lookup_flags)
+{
+	return filename_listxattr(AT_FDCWD, getname(pathname), lookup_flags,
+				  list, size);
+}
+
 SYSCALL_DEFINE3(listxattr, const char __user *, pathname, char __user *, list,
 		size_t, size)
 {
@@ -924,8 +943,7 @@ SYSCALL_DEFINE3(flistxattr, int, fd, char __user *, list, size_t, size)
 
 	if (fd_empty(f))
 		return -EBADF;
-	audit_file(fd_file(f));
-	return listxattr(fd_file(f)->f_path.dentry, list, size);
+	return file_listxattr(fd_file(f), list, size);
 }
 
 /*
-- 
2.39.5


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH v2 11/13] new helpers: file_removexattr(), filename_removexattr()
  2024-11-02  7:31   ` [PATCH v2 01/13] teach filename_lookup() to treat NULL filename as "" Al Viro
                       ` (8 preceding siblings ...)
  2024-11-02  7:31     ` [PATCH v2 10/13] new helpers: file_listxattr(), filename_listxattr() Al Viro
@ 2024-11-02  7:31     ` Al Viro
  2024-11-02  7:31     ` [PATCH v2 12/13] fs/xattr: add *at family syscalls Al Viro
  2024-11-02  7:31     ` [PATCH v2 13/13] xattr: remove redundant check on variable err Al Viro
  11 siblings, 0 replies; 50+ messages in thread
From: Al Viro @ 2024-11-02  7:31 UTC (permalink / raw)
  To: linux-fsdevel; +Cc: Christian Brauner, io-uring, Christian Göttsche

switch path_removexattrat() and fremovexattr(2) to those

Reviewed-by: Christian Brauner <[email protected]>
Signed-off-by: Al Viro <[email protected]>
---
 fs/xattr.c | 53 +++++++++++++++++++++++++++++++++++------------------
 1 file changed, 35 insertions(+), 18 deletions(-)

diff --git a/fs/xattr.c b/fs/xattr.c
index 988ea737ae7e..b76911b23293 100644
--- a/fs/xattr.c
+++ b/fs/xattr.c
@@ -957,23 +957,33 @@ removexattr(struct mnt_idmap *idmap, struct dentry *d, const char *name)
 	return vfs_removexattr(idmap, d, name);
 }
 
-static int path_removexattr(const char __user *pathname,
-			    const char __user *name, unsigned int lookup_flags)
+static int file_removexattr(struct file *f, struct xattr_name *kname)
+{
+	int error = mnt_want_write_file(f);
+
+	if (!error) {
+		audit_file(f);
+		error = removexattr(file_mnt_idmap(f),
+				    f->f_path.dentry, kname->name);
+		mnt_drop_write_file(f);
+	}
+	return error;
+}
+
+/* unconditionally consumes filename */
+static int filename_removexattr(int dfd, struct filename *filename,
+				unsigned int lookup_flags, struct xattr_name *kname)
 {
 	struct path path;
 	int error;
-	struct xattr_name kname;
 
-	error = import_xattr_name(&kname, name);
-	if (error)
-		return error;
 retry:
-	error = user_path_at(AT_FDCWD, pathname, lookup_flags, &path);
+	error = filename_lookup(dfd, filename, lookup_flags, &path, NULL);
 	if (error)
-		return error;
+		goto out;
 	error = mnt_want_write(path.mnt);
 	if (!error) {
-		error = removexattr(mnt_idmap(path.mnt), path.dentry, kname.name);
+		error = removexattr(mnt_idmap(path.mnt), path.dentry, kname->name);
 		mnt_drop_write(path.mnt);
 	}
 	path_put(&path);
@@ -981,9 +991,24 @@ static int path_removexattr(const char __user *pathname,
 		lookup_flags |= LOOKUP_REVAL;
 		goto retry;
 	}
+out:
+	putname(filename);
 	return error;
 }
 
+static int path_removexattr(const char __user *pathname,
+			    const char __user *name, unsigned int lookup_flags)
+{
+	struct xattr_name kname;
+	int error;
+
+	error = import_xattr_name(&kname, name);
+	if (error)
+		return error;
+	return filename_removexattr(AT_FDCWD, getname(pathname), lookup_flags,
+				    &kname);
+}
+
 SYSCALL_DEFINE2(removexattr, const char __user *, pathname,
 		const char __user *, name)
 {
@@ -1004,19 +1029,11 @@ SYSCALL_DEFINE2(fremovexattr, int, fd, const char __user *, name)
 
 	if (fd_empty(f))
 		return -EBADF;
-	audit_file(fd_file(f));
 
 	error = import_xattr_name(&kname, name);
 	if (error)
 		return error;
-
-	error = mnt_want_write_file(fd_file(f));
-	if (!error) {
-		error = removexattr(file_mnt_idmap(fd_file(f)),
-				    fd_file(f)->f_path.dentry, kname.name);
-		mnt_drop_write_file(fd_file(f));
-	}
-	return error;
+	return file_removexattr(fd_file(f), &kname);
 }
 
 int xattr_list_one(char **buffer, ssize_t *remaining_size, const char *name)
-- 
2.39.5


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH v2 12/13] fs/xattr: add *at family syscalls
  2024-11-02  7:31   ` [PATCH v2 01/13] teach filename_lookup() to treat NULL filename as "" Al Viro
                       ` (9 preceding siblings ...)
  2024-11-02  7:31     ` [PATCH v2 11/13] new helpers: file_removexattr(), filename_removexattr() Al Viro
@ 2024-11-02  7:31     ` Al Viro
  2024-11-02  7:31     ` [PATCH v2 13/13] xattr: remove redundant check on variable err Al Viro
  11 siblings, 0 replies; 50+ messages in thread
From: Al Viro @ 2024-11-02  7:31 UTC (permalink / raw)
  To: linux-fsdevel; +Cc: Christian Brauner, io-uring, Christian Göttsche

From: Christian Göttsche <[email protected]>

Add the four syscalls setxattrat(), getxattrat(), listxattrat() and
removexattrat().  Those can be used to operate on extended attributes,
especially security related ones, either relative to a pinned directory
or on a file descriptor without read access, avoiding a
/proc/<pid>/fd/<fd> detour, requiring a mounted procfs.

One use case will be setfiles(8) setting SELinux file contexts
("security.selinux") without race conditions and without a file
descriptor opened with read access requiring SELinux read permission.

Use the do_{name}at() pattern from fs/open.c.

Pass the value of the extended attribute, its length, and for
setxattrat(2) the command (XATTR_CREATE or XATTR_REPLACE) via an added
struct xattr_args to not exceed six syscall arguments and not
merging the AT_* and XATTR_* flags.

[AV: fixes by Christian Brauner folded in, the entire thing rebased on
top of {filename,file}_...xattr() primitives, treatment of empty
pathnames regularized.  As the result, AT_EMPTY_PATH+NULL handling
is cheap, so f...(2) can use it]

Signed-off-by: Christian Göttsche <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
Reviewed-by: Arnd Bergmann <[email protected]>
Reviewed-by: Christian Brauner <[email protected]>
CC: [email protected]
CC: [email protected]
CC: [email protected]
CC: [email protected]
CC: [email protected]
CC: [email protected]
CC: [email protected]
CC: [email protected]
CC: [email protected]
CC: [email protected]
CC: [email protected]
CC: [email protected]
CC: [email protected]
CC: [email protected]
CC: [email protected]
CC: [email protected]
CC: [email protected]
CC: [email protected]
[brauner: slight tweaks]
Signed-off-by: Christian Brauner <[email protected]>
---
 arch/alpha/kernel/syscalls/syscall.tbl      |   4 +
 arch/arm/tools/syscall.tbl                  |   4 +
 arch/m68k/kernel/syscalls/syscall.tbl       |   4 +
 arch/microblaze/kernel/syscalls/syscall.tbl |   4 +
 arch/mips/kernel/syscalls/syscall_n32.tbl   |   4 +
 arch/mips/kernel/syscalls/syscall_n64.tbl   |   4 +
 arch/mips/kernel/syscalls/syscall_o32.tbl   |   4 +
 arch/parisc/kernel/syscalls/syscall.tbl     |   4 +
 arch/powerpc/kernel/syscalls/syscall.tbl    |   4 +
 arch/s390/kernel/syscalls/syscall.tbl       |   4 +
 arch/sh/kernel/syscalls/syscall.tbl         |   4 +
 arch/sparc/kernel/syscalls/syscall.tbl      |   4 +
 arch/x86/entry/syscalls/syscall_32.tbl      |   4 +
 arch/x86/entry/syscalls/syscall_64.tbl      |   4 +
 arch/xtensa/kernel/syscalls/syscall.tbl     |   4 +
 fs/xattr.c                                  | 245 +++++++++++++-------
 include/asm-generic/audit_change_attr.h     |   6 +
 include/linux/syscalls.h                    |  13 ++
 include/linux/xattr.h                       |   4 +
 include/uapi/asm-generic/unistd.h           |  11 +-
 include/uapi/linux/xattr.h                  |   7 +
 21 files changed, 260 insertions(+), 86 deletions(-)

diff --git a/arch/alpha/kernel/syscalls/syscall.tbl b/arch/alpha/kernel/syscalls/syscall.tbl
index 74720667fe09..c59d53d6d3f3 100644
--- a/arch/alpha/kernel/syscalls/syscall.tbl
+++ b/arch/alpha/kernel/syscalls/syscall.tbl
@@ -502,3 +502,7 @@
 570	common	lsm_set_self_attr		sys_lsm_set_self_attr
 571	common	lsm_list_modules		sys_lsm_list_modules
 572	common  mseal				sys_mseal
+573	common	setxattrat			sys_setxattrat
+574	common	getxattrat			sys_getxattrat
+575	common	listxattrat			sys_listxattrat
+576	common	removexattrat			sys_removexattrat
diff --git a/arch/arm/tools/syscall.tbl b/arch/arm/tools/syscall.tbl
index 23c98203c40f..49eeb2ad8dbd 100644
--- a/arch/arm/tools/syscall.tbl
+++ b/arch/arm/tools/syscall.tbl
@@ -477,3 +477,7 @@
 460	common	lsm_set_self_attr		sys_lsm_set_self_attr
 461	common	lsm_list_modules		sys_lsm_list_modules
 462	common	mseal				sys_mseal
+463	common	setxattrat			sys_setxattrat
+464	common	getxattrat			sys_getxattrat
+465	common	listxattrat			sys_listxattrat
+466	common	removexattrat			sys_removexattrat
diff --git a/arch/m68k/kernel/syscalls/syscall.tbl b/arch/m68k/kernel/syscalls/syscall.tbl
index 22a3cbd4c602..f5ed71f1910d 100644
--- a/arch/m68k/kernel/syscalls/syscall.tbl
+++ b/arch/m68k/kernel/syscalls/syscall.tbl
@@ -462,3 +462,7 @@
 460	common	lsm_set_self_attr		sys_lsm_set_self_attr
 461	common	lsm_list_modules		sys_lsm_list_modules
 462	common	mseal				sys_mseal
+463	common	setxattrat			sys_setxattrat
+464	common	getxattrat			sys_getxattrat
+465	common	listxattrat			sys_listxattrat
+466	common	removexattrat			sys_removexattrat
diff --git a/arch/microblaze/kernel/syscalls/syscall.tbl b/arch/microblaze/kernel/syscalls/syscall.tbl
index 2b81a6bd78b2..680f568b77f2 100644
--- a/arch/microblaze/kernel/syscalls/syscall.tbl
+++ b/arch/microblaze/kernel/syscalls/syscall.tbl
@@ -468,3 +468,7 @@
 460	common	lsm_set_self_attr		sys_lsm_set_self_attr
 461	common	lsm_list_modules		sys_lsm_list_modules
 462	common	mseal				sys_mseal
+463	common	setxattrat			sys_setxattrat
+464	common	getxattrat			sys_getxattrat
+465	common	listxattrat			sys_listxattrat
+466	common	removexattrat			sys_removexattrat
diff --git a/arch/mips/kernel/syscalls/syscall_n32.tbl b/arch/mips/kernel/syscalls/syscall_n32.tbl
index 953f5b7dc723..0b9b7e25b69a 100644
--- a/arch/mips/kernel/syscalls/syscall_n32.tbl
+++ b/arch/mips/kernel/syscalls/syscall_n32.tbl
@@ -401,3 +401,7 @@
 460	n32	lsm_set_self_attr		sys_lsm_set_self_attr
 461	n32	lsm_list_modules		sys_lsm_list_modules
 462	n32	mseal				sys_mseal
+463	n32	setxattrat			sys_setxattrat
+464	n32	getxattrat			sys_getxattrat
+465	n32	listxattrat			sys_listxattrat
+466	n32	removexattrat			sys_removexattrat
diff --git a/arch/mips/kernel/syscalls/syscall_n64.tbl b/arch/mips/kernel/syscalls/syscall_n64.tbl
index 1464c6be6eb3..c844cd5cda62 100644
--- a/arch/mips/kernel/syscalls/syscall_n64.tbl
+++ b/arch/mips/kernel/syscalls/syscall_n64.tbl
@@ -377,3 +377,7 @@
 460	n64	lsm_set_self_attr		sys_lsm_set_self_attr
 461	n64	lsm_list_modules		sys_lsm_list_modules
 462	n64	mseal				sys_mseal
+463	n64	setxattrat			sys_setxattrat
+464	n64	getxattrat			sys_getxattrat
+465	n64	listxattrat			sys_listxattrat
+466	n64	removexattrat			sys_removexattrat
diff --git a/arch/mips/kernel/syscalls/syscall_o32.tbl b/arch/mips/kernel/syscalls/syscall_o32.tbl
index 2439a2491cff..349b8aad1159 100644
--- a/arch/mips/kernel/syscalls/syscall_o32.tbl
+++ b/arch/mips/kernel/syscalls/syscall_o32.tbl
@@ -450,3 +450,7 @@
 460	o32	lsm_set_self_attr		sys_lsm_set_self_attr
 461	o32	lsm_list_modules		sys_lsm_list_modules
 462	o32	mseal				sys_mseal
+463	o32	setxattrat			sys_setxattrat
+464	o32	getxattrat			sys_getxattrat
+465	o32	listxattrat			sys_listxattrat
+466	o32	removexattrat			sys_removexattrat
diff --git a/arch/parisc/kernel/syscalls/syscall.tbl b/arch/parisc/kernel/syscalls/syscall.tbl
index 66dc406b12e4..d9fc94c86965 100644
--- a/arch/parisc/kernel/syscalls/syscall.tbl
+++ b/arch/parisc/kernel/syscalls/syscall.tbl
@@ -461,3 +461,7 @@
 460	common	lsm_set_self_attr		sys_lsm_set_self_attr
 461	common	lsm_list_modules		sys_lsm_list_modules
 462	common	mseal				sys_mseal
+463	common	setxattrat			sys_setxattrat
+464	common	getxattrat			sys_getxattrat
+465	common	listxattrat			sys_listxattrat
+466	common	removexattrat			sys_removexattrat
diff --git a/arch/powerpc/kernel/syscalls/syscall.tbl b/arch/powerpc/kernel/syscalls/syscall.tbl
index ebae8415dfbb..d8b4ab78bef0 100644
--- a/arch/powerpc/kernel/syscalls/syscall.tbl
+++ b/arch/powerpc/kernel/syscalls/syscall.tbl
@@ -553,3 +553,7 @@
 460	common	lsm_set_self_attr		sys_lsm_set_self_attr
 461	common	lsm_list_modules		sys_lsm_list_modules
 462	common	mseal				sys_mseal
+463	common	setxattrat			sys_setxattrat
+464	common	getxattrat			sys_getxattrat
+465	common	listxattrat			sys_listxattrat
+466	common	removexattrat			sys_removexattrat
diff --git a/arch/s390/kernel/syscalls/syscall.tbl b/arch/s390/kernel/syscalls/syscall.tbl
index 01071182763e..e9115b4d8b63 100644
--- a/arch/s390/kernel/syscalls/syscall.tbl
+++ b/arch/s390/kernel/syscalls/syscall.tbl
@@ -465,3 +465,7 @@
 460  common	lsm_set_self_attr	sys_lsm_set_self_attr		sys_lsm_set_self_attr
 461  common	lsm_list_modules	sys_lsm_list_modules		sys_lsm_list_modules
 462  common	mseal			sys_mseal			sys_mseal
+463  common	setxattrat		sys_setxattrat			sys_setxattrat
+464  common	getxattrat		sys_getxattrat			sys_getxattrat
+465  common	listxattrat		sys_listxattrat			sys_listxattrat
+466  common	removexattrat		sys_removexattrat		sys_removexattrat
diff --git a/arch/sh/kernel/syscalls/syscall.tbl b/arch/sh/kernel/syscalls/syscall.tbl
index c55fd7696d40..c8cad33bf250 100644
--- a/arch/sh/kernel/syscalls/syscall.tbl
+++ b/arch/sh/kernel/syscalls/syscall.tbl
@@ -466,3 +466,7 @@
 460	common	lsm_set_self_attr		sys_lsm_set_self_attr
 461	common	lsm_list_modules		sys_lsm_list_modules
 462	common	mseal				sys_mseal
+463	common	setxattrat			sys_setxattrat
+464	common	getxattrat			sys_getxattrat
+465	common	listxattrat			sys_listxattrat
+466	common	removexattrat			sys_removexattrat
diff --git a/arch/sparc/kernel/syscalls/syscall.tbl b/arch/sparc/kernel/syscalls/syscall.tbl
index cfdfb3707c16..727f99d333b3 100644
--- a/arch/sparc/kernel/syscalls/syscall.tbl
+++ b/arch/sparc/kernel/syscalls/syscall.tbl
@@ -508,3 +508,7 @@
 460	common	lsm_set_self_attr		sys_lsm_set_self_attr
 461	common	lsm_list_modules		sys_lsm_list_modules
 462	common	mseal 				sys_mseal
+463	common	setxattrat			sys_setxattrat
+464	common	getxattrat			sys_getxattrat
+465	common	listxattrat			sys_listxattrat
+466	common	removexattrat			sys_removexattrat
diff --git a/arch/x86/entry/syscalls/syscall_32.tbl b/arch/x86/entry/syscalls/syscall_32.tbl
index 534c74b14fab..4d0fb2fba7e2 100644
--- a/arch/x86/entry/syscalls/syscall_32.tbl
+++ b/arch/x86/entry/syscalls/syscall_32.tbl
@@ -468,3 +468,7 @@
 460	i386	lsm_set_self_attr	sys_lsm_set_self_attr
 461	i386	lsm_list_modules	sys_lsm_list_modules
 462	i386	mseal 			sys_mseal
+463	i386	setxattrat		sys_setxattrat
+464	i386	getxattrat		sys_getxattrat
+465	i386	listxattrat		sys_listxattrat
+466	i386	removexattrat		sys_removexattrat
diff --git a/arch/x86/entry/syscalls/syscall_64.tbl b/arch/x86/entry/syscalls/syscall_64.tbl
index 7093ee21c0d1..5eb708bff1c7 100644
--- a/arch/x86/entry/syscalls/syscall_64.tbl
+++ b/arch/x86/entry/syscalls/syscall_64.tbl
@@ -386,6 +386,10 @@
 460	common	lsm_set_self_attr	sys_lsm_set_self_attr
 461	common	lsm_list_modules	sys_lsm_list_modules
 462 	common  mseal			sys_mseal
+463	common	setxattrat		sys_setxattrat
+464	common	getxattrat		sys_getxattrat
+465	common	listxattrat		sys_listxattrat
+466	common	removexattrat		sys_removexattrat
 
 #
 # Due to a historical design error, certain syscalls are numbered differently
diff --git a/arch/xtensa/kernel/syscalls/syscall.tbl b/arch/xtensa/kernel/syscalls/syscall.tbl
index 67083fc1b2f5..37effc1b134e 100644
--- a/arch/xtensa/kernel/syscalls/syscall.tbl
+++ b/arch/xtensa/kernel/syscalls/syscall.tbl
@@ -433,3 +433,7 @@
 460	common	lsm_set_self_attr		sys_lsm_set_self_attr
 461	common	lsm_list_modules		sys_lsm_list_modules
 462	common	mseal 				sys_mseal
+463	common	setxattrat			sys_setxattrat
+464	common	getxattrat			sys_getxattrat
+465	common	listxattrat			sys_listxattrat
+466	common	removexattrat			sys_removexattrat
diff --git a/fs/xattr.c b/fs/xattr.c
index b76911b23293..deb336b821c9 100644
--- a/fs/xattr.c
+++ b/fs/xattr.c
@@ -676,69 +676,90 @@ int filename_setxattr(int dfd, struct filename *filename,
 	return error;
 }
 
-static int path_setxattr(const char __user *pathname,
-			 const char __user *name, const void __user *value,
-			 size_t size, int flags, unsigned int lookup_flags)
+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)
 {
 	struct xattr_name kname;
 	struct kernel_xattr_ctx ctx = {
-		.cvalue   = value,
-		.kvalue   = NULL,
-		.size     = size,
-		.kname    = &kname,
-		.flags    = flags,
+		.cvalue	= value,
+		.kvalue	= NULL,
+		.size	= size,
+		.kname	= &kname,
+		.flags	= flags,
 	};
+	struct filename *filename;
+	unsigned int lookup_flags = 0;
 	int error;
 
+	if ((at_flags & ~(AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH)) != 0)
+		return -EINVAL;
+
+	if (!(at_flags & AT_SYMLINK_NOFOLLOW))
+		lookup_flags = LOOKUP_FOLLOW;
+
 	error = setxattr_copy(name, &ctx);
 	if (error)
 		return error;
 
-	error = filename_setxattr(AT_FDCWD, getname(pathname), lookup_flags,
-				  &ctx);
+	filename = getname_maybe_null(pathname, at_flags);
+	if (!filename) {
+		CLASS(fd, f)(dfd);
+		if (fd_empty(f))
+			error = -EBADF;
+		else
+			error = file_setxattr(fd_file(f), &ctx);
+	} else {
+		error = filename_setxattr(dfd, filename, lookup_flags, &ctx);
+	}
 	kvfree(ctx.kvalue);
 	return error;
 }
 
+SYSCALL_DEFINE6(setxattrat, int, dfd, const char __user *, pathname, unsigned int, at_flags,
+		const char __user *, name, const struct xattr_args __user *, uargs,
+		size_t, usize)
+{
+	struct xattr_args args = {};
+	int error;
+
+	BUILD_BUG_ON(sizeof(struct xattr_args) < XATTR_ARGS_SIZE_VER0);
+	BUILD_BUG_ON(sizeof(struct xattr_args) != XATTR_ARGS_SIZE_LATEST);
+
+	if (unlikely(usize < XATTR_ARGS_SIZE_VER0))
+		return -EINVAL;
+	if (usize > PAGE_SIZE)
+		return -E2BIG;
+
+	error = copy_struct_from_user(&args, sizeof(args), uargs, usize);
+	if (error)
+		return error;
+
+	return path_setxattrat(dfd, pathname, at_flags, name,
+			       u64_to_user_ptr(args.value), args.size,
+			       args.flags);
+}
+
 SYSCALL_DEFINE5(setxattr, const char __user *, pathname,
 		const char __user *, name, const void __user *, value,
 		size_t, size, int, flags)
 {
-	return path_setxattr(pathname, name, value, size, flags, LOOKUP_FOLLOW);
+	return path_setxattrat(AT_FDCWD, 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_setxattr(pathname, name, value, size, flags, 0);
+	return path_setxattrat(AT_FDCWD, 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)
 {
-	struct xattr_name kname;
-	struct kernel_xattr_ctx ctx = {
-		.cvalue   = value,
-		.kvalue   = NULL,
-		.size     = size,
-		.kname    = &kname,
-		.flags    = flags,
-	};
-	int error;
-
-	CLASS(fd, f)(fd);
-
-	if (fd_empty(f))
-		return -EBADF;
-
-	error = setxattr_copy(name, &ctx);
-	if (error)
-		return error;
-
-	error = file_setxattr(fd_file(f), &ctx);
-	kvfree(ctx.kvalue);
-	return error;
+	return path_setxattrat(fd, NULL, AT_EMPTY_PATH, name,
+			       value, size, flags);
 }
 
 /*
@@ -804,11 +825,10 @@ ssize_t filename_getxattr(int dfd, struct filename *filename,
 	return error;
 }
 
-static ssize_t path_getxattr(const char __user *pathname,
-			     const char __user *name, void __user *value,
-			     size_t size, unsigned int lookup_flags)
+static ssize_t path_getxattrat(int dfd, const char __user *pathname,
+			       unsigned int at_flags, const char __user *name,
+			       void __user *value, size_t size)
 {
-	ssize_t error;
 	struct xattr_name kname;
 	struct kernel_xattr_ctx ctx = {
 		.value    = value,
@@ -816,44 +836,72 @@ static ssize_t path_getxattr(const char __user *pathname,
 		.kname    = &kname,
 		.flags    = 0,
 	};
+	struct filename *filename;
+	ssize_t error;
+
+	if ((at_flags & ~(AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH)) != 0)
+		return -EINVAL;
 
 	error = import_xattr_name(&kname, name);
 	if (error)
 		return error;
-	return filename_getxattr(AT_FDCWD, getname(pathname), lookup_flags, &ctx);
+
+	filename = getname_maybe_null(pathname, at_flags);
+	if (!filename) {
+		CLASS(fd, f)(dfd);
+		if (fd_empty(f))
+			return -EBADF;
+		return file_getxattr(fd_file(f), &ctx);
+	} else {
+		int lookup_flags = 0;
+		if (!(at_flags & AT_SYMLINK_NOFOLLOW))
+			lookup_flags = LOOKUP_FOLLOW;
+		return filename_getxattr(dfd, filename, lookup_flags, &ctx);
+	}
+}
+
+SYSCALL_DEFINE6(getxattrat, int, dfd, const char __user *, pathname, unsigned int, at_flags,
+		const char __user *, name, struct xattr_args __user *, uargs, size_t, usize)
+{
+	struct xattr_args args = {};
+	int error;
+
+	BUILD_BUG_ON(sizeof(struct xattr_args) < XATTR_ARGS_SIZE_VER0);
+	BUILD_BUG_ON(sizeof(struct xattr_args) != XATTR_ARGS_SIZE_LATEST);
+
+	if (unlikely(usize < XATTR_ARGS_SIZE_VER0))
+		return -EINVAL;
+	if (usize > PAGE_SIZE)
+		return -E2BIG;
+
+	error = copy_struct_from_user(&args, sizeof(args), uargs, usize);
+	if (error)
+		return error;
+
+	if (args.flags != 0)
+		return -EINVAL;
+
+	return path_getxattrat(dfd, 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_getxattr(pathname, name, value, size, LOOKUP_FOLLOW);
+	return path_getxattrat(AT_FDCWD, pathname, 0, name, value, size);
 }
 
 SYSCALL_DEFINE4(lgetxattr, const char __user *, pathname,
 		const char __user *, name, void __user *, value, size_t, size)
 {
-	return path_getxattr(pathname, name, value, size, 0);
+	return path_getxattrat(AT_FDCWD, pathname, AT_SYMLINK_NOFOLLOW, name,
+			       value, size);
 }
 
 SYSCALL_DEFINE4(fgetxattr, int, fd, const char __user *, name,
 		void __user *, value, size_t, size)
 {
-	ssize_t error;
-	struct xattr_name kname;
-	struct kernel_xattr_ctx ctx = {
-		.value    = value,
-		.size     = size,
-		.kname    = &kname,
-		.flags    = 0,
-	};
-	CLASS(fd, f)(fd);
-
-	if (fd_empty(f))
-		return -EBADF;
-	error = import_xattr_name(&kname, name);
-	if (error)
-		return error;
-	return file_getxattr(fd_file(f), &ctx);
+	return path_getxattrat(fd, NULL, AT_EMPTY_PATH, name, value, size);
 }
 
 /*
@@ -918,32 +966,50 @@ ssize_t filename_listxattr(int dfd, struct filename *filename,
 	return error;
 }
 
-static ssize_t path_listxattr(const char __user *pathname, char __user *list,
-			      size_t size, unsigned int lookup_flags)
+static ssize_t path_listxattrat(int dfd, const char __user *pathname,
+				unsigned int at_flags, char __user *list,
+				size_t size)
+{
+	struct filename *filename;
+	int lookup_flags;
+
+	if ((at_flags & ~(AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH)) != 0)
+		return -EINVAL;
+
+	filename = getname_maybe_null(pathname, at_flags);
+	if (!filename) {
+		CLASS(fd, f)(dfd);
+		if (fd_empty(f))
+			return -EBADF;
+		return file_listxattr(fd_file(f), list, size);
+	}
+
+	lookup_flags = (at_flags & AT_SYMLINK_NOFOLLOW) ? 0 : LOOKUP_FOLLOW;
+	return filename_listxattr(dfd, filename, lookup_flags, list, size);
+}
+
+SYSCALL_DEFINE5(listxattrat, int, dfd, const char __user *, pathname,
+		unsigned int, at_flags,
+		char __user *, list, size_t, size)
 {
-	return filename_listxattr(AT_FDCWD, getname(pathname), lookup_flags,
-				  list, size);
+	return path_listxattrat(dfd, pathname, at_flags, list, size);
 }
 
 SYSCALL_DEFINE3(listxattr, const char __user *, pathname, char __user *, list,
 		size_t, size)
 {
-	return path_listxattr(pathname, list, size, LOOKUP_FOLLOW);
+	return path_listxattrat(AT_FDCWD, pathname, 0, list, size);
 }
 
 SYSCALL_DEFINE3(llistxattr, const char __user *, pathname, char __user *, list,
 		size_t, size)
 {
-	return path_listxattr(pathname, list, size, 0);
+	return path_listxattrat(AT_FDCWD, pathname, AT_SYMLINK_NOFOLLOW, list, size);
 }
 
 SYSCALL_DEFINE3(flistxattr, int, fd, char __user *, list, size_t, size)
 {
-	CLASS(fd, f)(fd);
-
-	if (fd_empty(f))
-		return -EBADF;
-	return file_listxattr(fd_file(f), list, size);
+	return path_listxattrat(fd, NULL, AT_EMPTY_PATH, list, size);
 }
 
 /*
@@ -996,44 +1062,53 @@ static int filename_removexattr(int dfd, struct filename *filename,
 	return error;
 }
 
-static int path_removexattr(const char __user *pathname,
-			    const char __user *name, unsigned int lookup_flags)
+static int path_removexattrat(int dfd, const char __user *pathname,
+			      unsigned int at_flags, const char __user *name)
 {
 	struct xattr_name kname;
+	struct filename *filename;
+	unsigned int lookup_flags;
 	int error;
 
+	if ((at_flags & ~(AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH)) != 0)
+		return -EINVAL;
+
 	error = import_xattr_name(&kname, name);
 	if (error)
 		return error;
-	return filename_removexattr(AT_FDCWD, getname(pathname), lookup_flags,
-				    &kname);
+
+	filename = getname_maybe_null(pathname, at_flags);
+	if (!filename) {
+		CLASS(fd, f)(dfd);
+		if (fd_empty(f))
+			return -EBADF;
+		return file_removexattr(fd_file(f), &kname);
+	}
+	lookup_flags = (at_flags & AT_SYMLINK_NOFOLLOW) ? 0 : LOOKUP_FOLLOW;
+	return filename_removexattr(dfd, filename, lookup_flags, &kname);
+}
+
+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);
 }
 
 SYSCALL_DEFINE2(removexattr, const char __user *, pathname,
 		const char __user *, name)
 {
-	return path_removexattr(pathname, name, LOOKUP_FOLLOW);
+	return path_removexattrat(AT_FDCWD, pathname, 0, name);
 }
 
 SYSCALL_DEFINE2(lremovexattr, const char __user *, pathname,
 		const char __user *, name)
 {
-	return path_removexattr(pathname, name, 0);
+	return path_removexattrat(AT_FDCWD, pathname, AT_SYMLINK_NOFOLLOW, name);
 }
 
 SYSCALL_DEFINE2(fremovexattr, int, fd, const char __user *, name)
 {
-	CLASS(fd, f)(fd);
-	struct xattr_name kname;
-	int error;
-
-	if (fd_empty(f))
-		return -EBADF;
-
-	error = import_xattr_name(&kname, name);
-	if (error)
-		return error;
-	return file_removexattr(fd_file(f), &kname);
+	return path_removexattrat(fd, NULL, AT_EMPTY_PATH, name);
 }
 
 int xattr_list_one(char **buffer, ssize_t *remaining_size, const char *name)
diff --git a/include/asm-generic/audit_change_attr.h b/include/asm-generic/audit_change_attr.h
index 331670807cf0..cc840537885f 100644
--- a/include/asm-generic/audit_change_attr.h
+++ b/include/asm-generic/audit_change_attr.h
@@ -11,9 +11,15 @@ __NR_lchown,
 __NR_fchown,
 #endif
 __NR_setxattr,
+#ifdef __NR_setxattrat
+__NR_setxattrat,
+#endif
 __NR_lsetxattr,
 __NR_fsetxattr,
 __NR_removexattr,
+#ifdef __NR_removexattrat
+__NR_removexattrat,
+#endif
 __NR_lremovexattr,
 __NR_fremovexattr,
 #ifdef __NR_fchownat
diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h
index 5758104921e6..c6333204d451 100644
--- a/include/linux/syscalls.h
+++ b/include/linux/syscalls.h
@@ -77,6 +77,7 @@ struct cachestat_range;
 struct cachestat;
 struct statmount;
 struct mnt_id_req;
+struct xattr_args;
 
 #include <linux/types.h>
 #include <linux/aio_abi.h>
@@ -338,23 +339,35 @@ asmlinkage long sys_io_uring_register(unsigned int fd, unsigned int op,
 				void __user *arg, unsigned int nr_args);
 asmlinkage long sys_setxattr(const char __user *path, const char __user *name,
 			     const void __user *value, size_t size, int flags);
+asmlinkage long sys_setxattrat(int dfd, const char __user *path, unsigned int at_flags,
+			       const char __user *name,
+			       const struct xattr_args __user *args, size_t size);
 asmlinkage long sys_lsetxattr(const char __user *path, const char __user *name,
 			      const void __user *value, size_t size, int flags);
 asmlinkage long sys_fsetxattr(int fd, const char __user *name,
 			      const void __user *value, size_t size, int flags);
 asmlinkage long sys_getxattr(const char __user *path, const char __user *name,
 			     void __user *value, size_t size);
+asmlinkage long sys_getxattrat(int dfd, const char __user *path, unsigned int at_flags,
+			       const char __user *name,
+			       struct xattr_args __user *args, size_t size);
 asmlinkage long sys_lgetxattr(const char __user *path, const char __user *name,
 			      void __user *value, size_t size);
 asmlinkage long sys_fgetxattr(int fd, const char __user *name,
 			      void __user *value, size_t size);
 asmlinkage long sys_listxattr(const char __user *path, char __user *list,
 			      size_t size);
+asmlinkage long sys_listxattrat(int dfd, const char __user *path,
+				unsigned int at_flags,
+				char __user *list, size_t size);
 asmlinkage long sys_llistxattr(const char __user *path, char __user *list,
 			       size_t size);
 asmlinkage long sys_flistxattr(int fd, char __user *list, size_t size);
 asmlinkage long sys_removexattr(const char __user *path,
 				const char __user *name);
+asmlinkage long sys_removexattrat(int dfd, const char __user *path,
+				  unsigned int at_flags,
+				  const char __user *name);
 asmlinkage long sys_lremovexattr(const char __user *path,
 				 const char __user *name);
 asmlinkage long sys_fremovexattr(int fd, const char __user *name);
diff --git a/include/linux/xattr.h b/include/linux/xattr.h
index d20051865800..86b0d47984a1 100644
--- a/include/linux/xattr.h
+++ b/include/linux/xattr.h
@@ -19,6 +19,10 @@
 #include <linux/user_namespace.h>
 #include <uapi/linux/xattr.h>
 
+/* List of all open_how "versions". */
+#define XATTR_ARGS_SIZE_VER0	16 /* sizeof first published struct */
+#define XATTR_ARGS_SIZE_LATEST	XATTR_ARGS_SIZE_VER0
+
 struct inode;
 struct dentry;
 
diff --git a/include/uapi/asm-generic/unistd.h b/include/uapi/asm-generic/unistd.h
index 5bf6148cac2b..88dc393c2bca 100644
--- a/include/uapi/asm-generic/unistd.h
+++ b/include/uapi/asm-generic/unistd.h
@@ -841,8 +841,17 @@ __SYSCALL(__NR_lsm_list_modules, sys_lsm_list_modules)
 #define __NR_mseal 462
 __SYSCALL(__NR_mseal, sys_mseal)
 
+#define __NR_setxattrat 463
+__SYSCALL(__NR_setxattrat, sys_setxattrat)
+#define __NR_getxattrat 464
+__SYSCALL(__NR_getxattrat, sys_getxattrat)
+#define __NR_listxattrat 465
+__SYSCALL(__NR_listxattrat, sys_listxattrat)
+#define __NR_removexattrat 466
+__SYSCALL(__NR_removexattrat, sys_removexattrat)
+
 #undef __NR_syscalls
-#define __NR_syscalls 463
+#define __NR_syscalls 467
 
 /*
  * 32 bit systems traditionally used different
diff --git a/include/uapi/linux/xattr.h b/include/uapi/linux/xattr.h
index 9463db2dfa9d..9854f9cff3c6 100644
--- a/include/uapi/linux/xattr.h
+++ b/include/uapi/linux/xattr.h
@@ -11,6 +11,7 @@
 */
 
 #include <linux/libc-compat.h>
+#include <linux/types.h>
 
 #ifndef _UAPI_LINUX_XATTR_H
 #define _UAPI_LINUX_XATTR_H
@@ -20,6 +21,12 @@
 
 #define XATTR_CREATE	0x1	/* set value, fail if attr already exists */
 #define XATTR_REPLACE	0x2	/* set value, fail if attr does not exist */
+
+struct xattr_args {
+	__aligned_u64 __user value;
+	__u32 size;
+	__u32 flags;
+};
 #endif
 
 /* Namespaces */
-- 
2.39.5


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH v2 13/13] xattr: remove redundant check on variable err
  2024-11-02  7:31   ` [PATCH v2 01/13] teach filename_lookup() to treat NULL filename as "" Al Viro
                       ` (10 preceding siblings ...)
  2024-11-02  7:31     ` [PATCH v2 12/13] fs/xattr: add *at family syscalls Al Viro
@ 2024-11-02  7:31     ` Al Viro
  11 siblings, 0 replies; 50+ messages in thread
From: Al Viro @ 2024-11-02  7:31 UTC (permalink / raw)
  To: linux-fsdevel; +Cc: Christian Brauner, io-uring, Christian Göttsche

From: Colin Ian King <[email protected]>

Curretly in function generic_listxattr the for_each_xattr_handler loop
checks err and will return out of the function if err is non-zero.
It's impossible for err to be non-zero at the end of the function where
err is checked again for a non-zero value. The final non-zero check is
therefore redundant and can be removed. Also move the declaration of
err into the loop.

Signed-off-by: Colin Ian King <[email protected]>
Signed-off-by: Al Viro <[email protected]>
---
 fs/xattr.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/fs/xattr.c b/fs/xattr.c
index deb336b821c9..02bee149ad96 100644
--- a/fs/xattr.c
+++ b/fs/xattr.c
@@ -1142,9 +1142,10 @@ generic_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size)
 {
 	const struct xattr_handler *handler, * const *handlers = dentry->d_sb->s_xattr;
 	ssize_t remaining_size = buffer_size;
-	int err = 0;
 
 	for_each_xattr_handler(handlers, handler) {
+		int err;
+
 		if (!handler->name || (handler->list && !handler->list(dentry)))
 			continue;
 		err = xattr_list_one(&buffer, &remaining_size, handler->name);
@@ -1152,7 +1153,7 @@ generic_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size)
 			return err;
 	}
 
-	return err ? err : buffer_size - remaining_size;
+	return buffer_size - remaining_size;
 }
 EXPORT_SYMBOL(generic_listxattr);
 
-- 
2.39.5


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* Re: [RFC][PATCHES v2] xattr stuff and interactions with io_uring
  2024-11-02  7:28 ` [RFC][PATCHES v2] xattr stuff and interactions with io_uring Al Viro
  2024-11-02  7:31   ` [PATCH v2 01/13] teach filename_lookup() to treat NULL filename as "" Al Viro
@ 2024-11-02 14:43   ` Jens Axboe
  2024-11-03  6:51     ` Al Viro
  1 sibling, 1 reply; 50+ messages in thread
From: Jens Axboe @ 2024-11-02 14:43 UTC (permalink / raw)
  To: Al Viro, linux-fsdevel
  Cc: Christian Brauner, io-uring, Christian Göttsche

On 11/2/24 1:28 AM, Al Viro wrote:
> 	Changes since v1:
> * moved on top of (and makes use of) getname_maybe_null() stuff
> (first two commits here, form #base.getname-fixed)
> * fixed a leak on io_uring side spotted by Jens
> * putname(ERR_PTR(-E...)) is a no-op; allows to simplify things on
> io_uring side.
> * applied reviewed-by
> * picked a generic_listxattr() cleanup from Colin Ian King
> 
> 	Help with review and testing would be welcome; if nobody objects,
> to #for-next it goes...

Tested on arm64, fwiw I get these:

<stdin>:1603:2: warning: #warning syscall setxattrat not implemented [-Wcpp]
<stdin>:1606:2: warning: #warning syscall getxattrat not implemented [-Wcpp]
<stdin>:1609:2: warning: #warning syscall listxattrat not implemented [-Wcpp]
<stdin>:1612:2: warning: #warning syscall removexattrat not implemented [-Wcpp]

when compiling. But ran it through the usual testing on the io_uring side,
and it looks fine to me. Didn't test the xattr* stuff separately, assuming
I'd just be re-running what you already did on that side.

-- 
Jens Axboe


^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH v2 04/13] io_[gs]etxattr_prep(): just use getname()
  2024-11-02  7:31     ` [PATCH v2 04/13] io_[gs]etxattr_prep(): just use getname() Al Viro
@ 2024-11-02 14:44       ` Jens Axboe
  0 siblings, 0 replies; 50+ messages in thread
From: Jens Axboe @ 2024-11-02 14:44 UTC (permalink / raw)
  To: Al Viro, linux-fsdevel
  Cc: Christian Brauner, io-uring, Christian Göttsche

On 11/2/24 1:31 AM, Al Viro wrote:
> getname_flags(pathname, LOOKUP_FOLLOW) is obviously bogus - following
> trailing symlinks has no impact on how to copy the pathname from userland...

Reviewed-by: Jens Axboe <[email protected]>

-- 
Jens Axboe


^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [RFC][PATCHES v2] xattr stuff and interactions with io_uring
  2024-11-02 14:43   ` [RFC][PATCHES v2] xattr stuff and interactions with io_uring Jens Axboe
@ 2024-11-03  6:51     ` Al Viro
  2024-11-03 13:57       ` Arnd Bergmann
  0 siblings, 1 reply; 50+ messages in thread
From: Al Viro @ 2024-11-03  6:51 UTC (permalink / raw)
  To: Jens Axboe
  Cc: linux-fsdevel, Christian Brauner, io-uring,
	Christian Göttsche, Arnd Bergmann

On Sat, Nov 02, 2024 at 08:43:31AM -0600, Jens Axboe wrote:
> On 11/2/24 1:28 AM, Al Viro wrote:
> > 	Changes since v1:
> > * moved on top of (and makes use of) getname_maybe_null() stuff
> > (first two commits here, form #base.getname-fixed)
> > * fixed a leak on io_uring side spotted by Jens
> > * putname(ERR_PTR(-E...)) is a no-op; allows to simplify things on
> > io_uring side.
> > * applied reviewed-by
> > * picked a generic_listxattr() cleanup from Colin Ian King
> > 
> > 	Help with review and testing would be welcome; if nobody objects,
> > to #for-next it goes...
> 
> Tested on arm64, fwiw I get these:
> 
> <stdin>:1603:2: warning: #warning syscall setxattrat not implemented [-Wcpp]
> <stdin>:1606:2: warning: #warning syscall getxattrat not implemented [-Wcpp]
> <stdin>:1609:2: warning: #warning syscall listxattrat not implemented [-Wcpp]
> <stdin>:1612:2: warning: #warning syscall removexattrat not implemented [-Wcpp]

arch/arm64/tools/syscall*.tbl bits are missing (as well as
arch/sparc/kernel/syscall_32.tbl ones, but that's less of an
issue).

AFAICS, the following should be the right incremental.  Objections, anyone?


diff --git a/arch/arm64/tools/syscall_32.tbl b/arch/arm64/tools/syscall_32.tbl
index 9a37930d4e26..69a829912a05 100644
--- a/arch/arm64/tools/syscall_32.tbl
+++ b/arch/arm64/tools/syscall_32.tbl
@@ -474,3 +474,7 @@
 460	common	lsm_set_self_attr		sys_lsm_set_self_attr
 461	common	lsm_list_modules		sys_lsm_list_modules
 462	common	mseal				sys_mseal
+463	common	setxattrat			sys_setxattrat
+464	common	getxattrat			sys_getxattrat
+465	common	listxattrat			sys_listxattrat
+466	common	removexattrat			sys_removexattrat
diff --git a/scripts/syscall.tbl b/scripts/syscall.tbl
index 845e24eb372e..ebbdb3c42e9f 100644
--- a/scripts/syscall.tbl
+++ b/scripts/syscall.tbl
@@ -403,3 +403,7 @@
 460	common	lsm_set_self_attr		sys_lsm_set_self_attr
 461	common	lsm_list_modules		sys_lsm_list_modules
 462	common	mseal				sys_mseal
+463	common	setxattrat			sys_setxattrat
+464	common	getxattrat			sys_getxattrat
+465	common	listxattrat			sys_listxattrat
+466	common	removexattrat			sys_removexattrat

^ permalink raw reply related	[flat|nested] 50+ messages in thread

* Re: [RFC][PATCHES v2] xattr stuff and interactions with io_uring
  2024-11-03  6:51     ` Al Viro
@ 2024-11-03 13:57       ` Arnd Bergmann
  2024-11-03 18:32         ` Al Viro
  0 siblings, 1 reply; 50+ messages in thread
From: Arnd Bergmann @ 2024-11-03 13:57 UTC (permalink / raw)
  To: Alexander Viro, Jens Axboe
  Cc: linux-fsdevel, Christian Brauner, io-uring,
	Christian Göttsche

On Sun, Nov 3, 2024, at 07:51, Al Viro wrote:
> On Sat, Nov 02, 2024 at 08:43:31AM -0600, Jens Axboe wrote:
>> Tested on arm64, fwiw I get these:
>> 
>> <stdin>:1603:2: warning: #warning syscall setxattrat not implemented [-Wcpp]
>> <stdin>:1606:2: warning: #warning syscall getxattrat not implemented [-Wcpp]
>> <stdin>:1609:2: warning: #warning syscall listxattrat not implemented [-Wcpp]
>> <stdin>:1612:2: warning: #warning syscall removexattrat not implemented [-Wcpp]
>
> arch/arm64/tools/syscall*.tbl bits are missing (as well as
> arch/sparc/kernel/syscall_32.tbl ones, but that's less of an
> issue).
>
> AFAICS, the following should be the right incremental.  Objections, anyone?

Looks fine to me.

I have a patch to convert s390 to use the exact same format
as the others, and I should push that patch, but it slightly
conflict with this one.

We can also remove the old include/uapi/asm-generic/unistd.h
that is no longer used.

I was planning to have a patch by now to only need to chance a
single .tbl file for new entries, which is a bit behind some
other work I have planned for these files.

      Arnd

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [RFC][PATCHES v2] xattr stuff and interactions with io_uring
  2024-11-03 13:57       ` Arnd Bergmann
@ 2024-11-03 18:32         ` Al Viro
  0 siblings, 0 replies; 50+ messages in thread
From: Al Viro @ 2024-11-03 18:32 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: Jens Axboe, linux-fsdevel, Christian Brauner, io-uring,
	Christian Göttsche

On Sun, Nov 03, 2024 at 02:57:14PM +0100, Arnd Bergmann wrote:
> On Sun, Nov 3, 2024, at 07:51, Al Viro wrote:
> > On Sat, Nov 02, 2024 at 08:43:31AM -0600, Jens Axboe wrote:
> >> Tested on arm64, fwiw I get these:
> >> 
> >> <stdin>:1603:2: warning: #warning syscall setxattrat not implemented [-Wcpp]
> >> <stdin>:1606:2: warning: #warning syscall getxattrat not implemented [-Wcpp]
> >> <stdin>:1609:2: warning: #warning syscall listxattrat not implemented [-Wcpp]
> >> <stdin>:1612:2: warning: #warning syscall removexattrat not implemented [-Wcpp]
> >
> > arch/arm64/tools/syscall*.tbl bits are missing (as well as
> > arch/sparc/kernel/syscall_32.tbl ones, but that's less of an
> > issue).
> >
> > AFAICS, the following should be the right incremental.  Objections, anyone?
> 
> Looks fine to me.
> 
> I have a patch to convert s390 to use the exact same format
> as the others, and I should push that patch, but it slightly
> conflict with this one.
> 
> We can also remove the old include/uapi/asm-generic/unistd.h
> that is no longer used.

I'd suggest starting with Documentation/process/adding-syscalls.rst, then...

^ permalink raw reply	[flat|nested] 50+ messages in thread

end of thread, other threads:[~2024-11-03 18:32 UTC | newest]

Thread overview: 50+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-10-02  1:10 [RFC][PATCHES] xattr stuff and interactions with io_uring Al Viro
2024-10-02  1:22 ` [PATCH 1/9] xattr: switch to CLASS(fd) Al Viro
2024-10-02  1:22   ` [PATCH 2/9] fs: rename struct xattr_ctx to kernel_xattr_ctx Al Viro
2024-10-02  5:56     ` Christian Brauner
2024-10-02  1:22   ` [PATCH 3/9] io_[gs]etxattr_prep(): just use getname() Al Viro
2024-10-02  5:57     ` Christian Brauner
2024-10-02  1:22   ` [PATCH 4/9] new helper: import_xattr_name() Al Viro
2024-10-02  5:57     ` Christian Brauner
2024-10-02  1:22   ` [PATCH 5/9] replace do_setxattr() with saner helpers Al Viro
2024-10-02  1:34     ` Jens Axboe
2024-10-02  2:08       ` Al Viro
2024-10-02 18:00         ` Jens Axboe
2024-10-02 21:19           ` Al Viro
2024-10-02 22:55             ` Jens Axboe
2024-10-06  5:28               ` Al Viro
2024-10-07 18:09                 ` Jens Axboe
2024-10-07 18:20                   ` Jens Axboe
2024-10-07 21:20                     ` Al Viro
2024-10-07 22:29                       ` Jens Axboe
2024-10-07 23:58                         ` Al Viro
2024-10-08  1:58                           ` Jens Axboe
2024-10-08  4:08                             ` Al Viro
2024-10-02  1:22   ` [PATCH 6/9] replace do_getxattr() " Al Viro
2024-10-02  5:59     ` Christian Brauner
2024-10-02  1:22   ` [PATCH 7/9] new helpers: file_listxattr(), filename_listxattr() Al Viro
2024-10-02  6:00     ` Christian Brauner
2024-10-02  1:22   ` [PATCH 8/9] new helpers: file_removexattr(), filename_removexattr() Al Viro
2024-10-02  6:01     ` Christian Brauner
2024-10-02  1:22   ` [PATCH 9/9] fs/xattr: add *at family syscalls Al Viro
2024-10-02  6:03     ` Christian Brauner
2024-10-02  5:56   ` [PATCH 1/9] xattr: switch to CLASS(fd) Christian Brauner
2024-11-02  7:28 ` [RFC][PATCHES v2] xattr stuff and interactions with io_uring Al Viro
2024-11-02  7:31   ` [PATCH v2 01/13] teach filename_lookup() to treat NULL filename as "" Al Viro
2024-11-02  7:31     ` [PATCH v2 02/13] getname_maybe_null() - the third variant of pathname copy-in Al Viro
2024-11-02  7:31     ` [PATCH v2 03/13] io_uring: IORING_OP_F[GS]ETXATTR is fine with REQ_F_FIXED_FILE Al Viro
2024-11-02  7:31     ` [PATCH v2 04/13] io_[gs]etxattr_prep(): just use getname() Al Viro
2024-11-02 14:44       ` Jens Axboe
2024-11-02  7:31     ` [PATCH v2 05/13] xattr: switch to CLASS(fd) Al Viro
2024-11-02  7:31     ` [PATCH v2 06/13] fs: rename struct xattr_ctx to kernel_xattr_ctx Al Viro
2024-11-02  7:31     ` [PATCH v2 07/13] new helper: import_xattr_name() Al Viro
2024-11-02  7:31     ` [PATCH v2 08/13] replace do_setxattr() with saner helpers Al Viro
2024-11-02  7:31     ` [PATCH v2 09/13] replace do_getxattr() " Al Viro
2024-11-02  7:31     ` [PATCH v2 10/13] new helpers: file_listxattr(), filename_listxattr() Al Viro
2024-11-02  7:31     ` [PATCH v2 11/13] new helpers: file_removexattr(), filename_removexattr() Al Viro
2024-11-02  7:31     ` [PATCH v2 12/13] fs/xattr: add *at family syscalls Al Viro
2024-11-02  7:31     ` [PATCH v2 13/13] xattr: remove redundant check on variable err Al Viro
2024-11-02 14:43   ` [RFC][PATCHES v2] xattr stuff and interactions with io_uring Jens Axboe
2024-11-03  6:51     ` Al Viro
2024-11-03 13:57       ` Arnd Bergmann
2024-11-03 18:32         ` Al Viro

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox