sanitize xattr and io_uring interactions with it,

add *xattrat() syscalls, sanitize struct filename handling in there.
 
 Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
 -----BEGIN PGP SIGNATURE-----
 
 iHUEABYIAB0WIQQqUNBr3gm4hGXdBJlZ7Krx/gZQ6wUCZzdj4gAKCRBZ7Krx/gZQ
 6/02AQC8ndn9i1wLGRb5DdZYGNWUDhXCdPrZCF2nyvU2swCIPwEAm1H5F/bxBXeT
 6qCLHThVw4KTJOT2aDY03ELrxbi8Vg4=
 =35Oj
 -----END PGP SIGNATURE-----

Merge tag 'pull-xattr' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs

Pull xattr updates from Al Viro:
 "Sanitize xattr and io_uring interactions with it, add *xattrat()
  syscalls, sanitize struct filename handling in there"

* tag 'pull-xattr' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs:
  xattr: remove redundant check on variable err
  fs/xattr: add *at family syscalls
  new helpers: file_removexattr(), filename_removexattr()
  new helpers: file_listxattr(), filename_listxattr()
  replace do_getxattr() with saner helpers.
  replace do_setxattr() with saner helpers.
  new helper: import_xattr_name()
  fs: rename struct xattr_ctx to kernel_xattr_ctx
  xattr: switch to CLASS(fd)
  io_[gs]etxattr_prep(): just use getname()
  io_uring: IORING_OP_F[GS]ETXATTR is fine with REQ_F_FIXED_FILE
  getname_maybe_null() - the third variant of pathname copy-in
  teach filename_lookup() to treat NULL filename as ""
This commit is contained in:
Linus Torvalds 2024-11-18 12:44:25 -08:00
commit 82339c4911
28 changed files with 476 additions and 269 deletions

View File

@ -502,3 +502,7 @@
570 common lsm_set_self_attr sys_lsm_set_self_attr 570 common lsm_set_self_attr sys_lsm_set_self_attr
571 common lsm_list_modules sys_lsm_list_modules 571 common lsm_list_modules sys_lsm_list_modules
572 common mseal sys_mseal 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

View File

@ -477,3 +477,7 @@
460 common lsm_set_self_attr sys_lsm_set_self_attr 460 common lsm_set_self_attr sys_lsm_set_self_attr
461 common lsm_list_modules sys_lsm_list_modules 461 common lsm_list_modules sys_lsm_list_modules
462 common mseal sys_mseal 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

View File

@ -474,3 +474,7 @@
460 common lsm_set_self_attr sys_lsm_set_self_attr 460 common lsm_set_self_attr sys_lsm_set_self_attr
461 common lsm_list_modules sys_lsm_list_modules 461 common lsm_list_modules sys_lsm_list_modules
462 common mseal sys_mseal 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

View File

@ -462,3 +462,7 @@
460 common lsm_set_self_attr sys_lsm_set_self_attr 460 common lsm_set_self_attr sys_lsm_set_self_attr
461 common lsm_list_modules sys_lsm_list_modules 461 common lsm_list_modules sys_lsm_list_modules
462 common mseal sys_mseal 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

View File

@ -468,3 +468,7 @@
460 common lsm_set_self_attr sys_lsm_set_self_attr 460 common lsm_set_self_attr sys_lsm_set_self_attr
461 common lsm_list_modules sys_lsm_list_modules 461 common lsm_list_modules sys_lsm_list_modules
462 common mseal sys_mseal 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

View File

@ -401,3 +401,7 @@
460 n32 lsm_set_self_attr sys_lsm_set_self_attr 460 n32 lsm_set_self_attr sys_lsm_set_self_attr
461 n32 lsm_list_modules sys_lsm_list_modules 461 n32 lsm_list_modules sys_lsm_list_modules
462 n32 mseal sys_mseal 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

View File

@ -377,3 +377,7 @@
460 n64 lsm_set_self_attr sys_lsm_set_self_attr 460 n64 lsm_set_self_attr sys_lsm_set_self_attr
461 n64 lsm_list_modules sys_lsm_list_modules 461 n64 lsm_list_modules sys_lsm_list_modules
462 n64 mseal sys_mseal 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

View File

@ -450,3 +450,7 @@
460 o32 lsm_set_self_attr sys_lsm_set_self_attr 460 o32 lsm_set_self_attr sys_lsm_set_self_attr
461 o32 lsm_list_modules sys_lsm_list_modules 461 o32 lsm_list_modules sys_lsm_list_modules
462 o32 mseal sys_mseal 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

View File

@ -461,3 +461,7 @@
460 common lsm_set_self_attr sys_lsm_set_self_attr 460 common lsm_set_self_attr sys_lsm_set_self_attr
461 common lsm_list_modules sys_lsm_list_modules 461 common lsm_list_modules sys_lsm_list_modules
462 common mseal sys_mseal 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

View File

@ -553,3 +553,7 @@
460 common lsm_set_self_attr sys_lsm_set_self_attr 460 common lsm_set_self_attr sys_lsm_set_self_attr
461 common lsm_list_modules sys_lsm_list_modules 461 common lsm_list_modules sys_lsm_list_modules
462 common mseal sys_mseal 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

View File

@ -465,3 +465,7 @@
460 common lsm_set_self_attr sys_lsm_set_self_attr sys_lsm_set_self_attr 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 461 common lsm_list_modules sys_lsm_list_modules sys_lsm_list_modules
462 common mseal sys_mseal sys_mseal 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

View File

@ -466,3 +466,7 @@
460 common lsm_set_self_attr sys_lsm_set_self_attr 460 common lsm_set_self_attr sys_lsm_set_self_attr
461 common lsm_list_modules sys_lsm_list_modules 461 common lsm_list_modules sys_lsm_list_modules
462 common mseal sys_mseal 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

View File

@ -508,3 +508,7 @@
460 common lsm_set_self_attr sys_lsm_set_self_attr 460 common lsm_set_self_attr sys_lsm_set_self_attr
461 common lsm_list_modules sys_lsm_list_modules 461 common lsm_list_modules sys_lsm_list_modules
462 common mseal sys_mseal 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

View File

@ -468,3 +468,7 @@
460 i386 lsm_set_self_attr sys_lsm_set_self_attr 460 i386 lsm_set_self_attr sys_lsm_set_self_attr
461 i386 lsm_list_modules sys_lsm_list_modules 461 i386 lsm_list_modules sys_lsm_list_modules
462 i386 mseal sys_mseal 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

View File

@ -386,6 +386,10 @@
460 common lsm_set_self_attr sys_lsm_set_self_attr 460 common lsm_set_self_attr sys_lsm_set_self_attr
461 common lsm_list_modules sys_lsm_list_modules 461 common lsm_list_modules sys_lsm_list_modules
462 common mseal sys_mseal 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 # Due to a historical design error, certain syscalls are numbered differently

View File

@ -433,3 +433,7 @@
460 common lsm_set_self_attr sys_lsm_set_self_attr 460 common lsm_set_self_attr sys_lsm_set_self_attr
461 common lsm_list_modules sys_lsm_list_modules 461 common lsm_list_modules sys_lsm_list_modules
462 common mseal sys_mseal 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

View File

@ -267,7 +267,7 @@ struct xattr_name {
char name[XATTR_NAME_MAX + 1]; char name[XATTR_NAME_MAX + 1];
}; };
struct xattr_ctx { struct kernel_xattr_ctx {
/* Value of attribute */ /* Value of attribute */
union { union {
const void __user *cvalue; const void __user *cvalue;
@ -280,14 +280,15 @@ struct xattr_ctx {
unsigned int flags; unsigned int flags;
}; };
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);
int setxattr_copy(const char __user *name, struct kernel_xattr_ctx *ctx);
int import_xattr_name(struct xattr_name *kname, const char __user *name);
ssize_t do_getxattr(struct mnt_idmap *idmap,
struct dentry *d,
struct xattr_ctx *ctx);
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);
int may_write_xattr(struct mnt_idmap *idmap, struct inode *inode); int may_write_xattr(struct mnt_idmap *idmap, struct inode *inode);
#ifdef CONFIG_FS_POSIX_ACL #ifdef CONFIG_FS_POSIX_ACL

View File

@ -211,22 +211,38 @@ getname_flags(const char __user *filename, int flags)
return result; return result;
} }
struct filename * struct filename *getname_uflags(const char __user *filename, int uflags)
getname_uflags(const char __user *filename, int uflags)
{ {
int flags = (uflags & AT_EMPTY_PATH) ? LOOKUP_EMPTY : 0; int flags = (uflags & AT_EMPTY_PATH) ? LOOKUP_EMPTY : 0;
return getname_flags(filename, flags); return getname_flags(filename, flags);
} }
struct filename * struct filename *getname(const char __user * filename)
getname(const char __user * filename)
{ {
return getname_flags(filename, 0); return getname_flags(filename, 0);
} }
struct filename * struct filename *__getname_maybe_null(const char __user *pathname)
getname_kernel(const char * filename) {
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; struct filename *result;
int len = strlen(filename) + 1; int len = strlen(filename) + 1;
@ -264,7 +280,7 @@ EXPORT_SYMBOL(getname_kernel);
void putname(struct filename *name) void putname(struct filename *name)
{ {
if (IS_ERR(name)) if (IS_ERR_OR_NULL(name))
return; return;
if (WARN_ON_ONCE(!atomic_read(&name->refcnt))) if (WARN_ON_ONCE(!atomic_read(&name->refcnt)))
@ -629,6 +645,7 @@ struct nameidata {
unsigned seq; unsigned seq;
} *stack, internal[EMBEDDED_LEVELS]; } *stack, internal[EMBEDDED_LEVELS];
struct filename *name; struct filename *name;
const char *pathname;
struct nameidata *saved; struct nameidata *saved;
unsigned root_seq; unsigned root_seq;
int dfd; int dfd;
@ -647,6 +664,7 @@ static void __set_nameidata(struct nameidata *p, int dfd, struct filename *name)
p->depth = 0; p->depth = 0;
p->dfd = dfd; p->dfd = dfd;
p->name = name; p->name = name;
p->pathname = likely(name) ? name->name : "";
p->path.mnt = NULL; p->path.mnt = NULL;
p->path.dentry = NULL; p->path.dentry = NULL;
p->total_link_count = old ? old->total_link_count : 0; p->total_link_count = old ? old->total_link_count : 0;
@ -2480,7 +2498,7 @@ OK:
static const char *path_init(struct nameidata *nd, unsigned flags) static const char *path_init(struct nameidata *nd, unsigned flags)
{ {
int error; int error;
const char *s = nd->name->name; const char *s = nd->pathname;
/* LOOKUP_CACHED requires RCU, ask caller to retry */ /* LOOKUP_CACHED requires RCU, ask caller to retry */
if ((flags & (LOOKUP_RCU | LOOKUP_CACHED)) == LOOKUP_CACHED) if ((flags & (LOOKUP_RCU | LOOKUP_CACHED)) == LOOKUP_CACHED)

View File

@ -368,18 +368,11 @@ int vfs_fstatat(int dfd, const char __user *filename,
{ {
int ret; int ret;
int statx_flags = flags | AT_NO_AUTOMOUNT; int statx_flags = flags | AT_NO_AUTOMOUNT;
struct filename *name; struct filename *name = getname_maybe_null(filename, flags);
/* if (!name && dfd >= 0)
* 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))
return vfs_fstat(dfd, stat); 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); ret = vfs_statx(dfd, name, statx_flags, stat, STATX_BASIC_STATS);
putname(name); putname(name);
@ -816,24 +809,11 @@ SYSCALL_DEFINE5(statx,
struct statx __user *, buffer) struct statx __user *, buffer)
{ {
int ret; int ret;
unsigned lflags; struct filename *name = getname_maybe_null(filename, flags);
struct filename *name;
/* if (!name && dfd >= 0)
* 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))
return do_statx_fd(dfd, flags & ~AT_NO_AUTOMOUNT, mask, buffer); 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); ret = do_statx(dfd, name, flags, mask, buffer);
putname(name); putname(name);

View File

@ -586,25 +586,32 @@ retry_deleg:
} }
EXPORT_SYMBOL_GPL(vfs_removexattr); 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 * 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; int error;
if (ctx->flags & ~(XATTR_CREATE|XATTR_REPLACE)) if (ctx->flags & ~(XATTR_CREATE|XATTR_REPLACE))
return -EINVAL; return -EINVAL;
error = strncpy_from_user(ctx->kname->name, name, error = import_xattr_name(ctx->kname, name);
sizeof(ctx->kname->name)); if (error)
if (error == 0 || error == sizeof(ctx->kname->name))
return -ERANGE;
if (error < 0)
return error; return error;
error = 0;
if (ctx->size) { if (ctx->size) {
if (ctx->size > XATTR_SIZE_MAX) if (ctx->size > XATTR_SIZE_MAX)
return -E2BIG; return -E2BIG;
@ -619,8 +626,8 @@ int setxattr_copy(const char __user *name, struct xattr_ctx *ctx)
return error; return error;
} }
int do_setxattr(struct mnt_idmap *idmap, struct dentry *dentry, static 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)) if (is_posix_acl_xattr(ctx->kname->name))
return do_set_acl(idmap, dentry, ctx->kname->name, return do_set_acl(idmap, dentry, ctx->kname->name,
@ -630,32 +637,32 @@ int do_setxattr(struct mnt_idmap *idmap, struct dentry *dentry,
ctx->kvalue, ctx->size, ctx->flags); ctx->kvalue, ctx->size, ctx->flags);
} }
static int path_setxattr(const char __user *pathname, int file_setxattr(struct file *f, struct kernel_xattr_ctx *ctx)
const char __user *name, const void __user *value, {
size_t size, int flags, unsigned int lookup_flags) 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 xattr_ctx ctx = {
.cvalue = value,
.kvalue = NULL,
.size = size,
.kname = &kname,
.flags = flags,
};
struct path path; struct path path;
int error; int error;
error = setxattr_copy(name, &ctx);
if (error)
return error;
retry: retry:
error = user_path_at(AT_FDCWD, pathname, lookup_flags, &path); error = filename_lookup(dfd, filename, lookup_flags, &path, NULL);
if (error) if (error)
goto out; goto out;
error = mnt_want_write(path.mnt); error = mnt_want_write(path.mnt);
if (!error) { 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); mnt_drop_write(path.mnt);
} }
path_put(&path); path_put(&path);
@ -665,80 +672,121 @@ retry:
} }
out: out:
putname(filename);
return error;
}
static int path_setxattrat(int dfd, const char __user *pathname,
unsigned int at_flags, const char __user *name,
const void __user *value, size_t size, int flags)
{
struct xattr_name kname;
struct kernel_xattr_ctx ctx = {
.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;
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); kvfree(ctx.kvalue);
return error; 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, SYSCALL_DEFINE5(setxattr, const char __user *, pathname,
const char __user *, name, const void __user *, value, const char __user *, name, const void __user *, value,
size_t, size, int, flags) 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, SYSCALL_DEFINE5(lsetxattr, const char __user *, pathname,
const char __user *, name, const void __user *, value, const char __user *, name, const void __user *, value,
size_t, size, int, flags) 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, SYSCALL_DEFINE5(fsetxattr, int, fd, const char __user *, name,
const void __user *,value, size_t, size, int, flags) const void __user *,value, size_t, size, int, flags)
{ {
struct xattr_name kname; return path_setxattrat(fd, NULL, AT_EMPTY_PATH, name,
struct xattr_ctx ctx = { value, size, flags);
.cvalue = value,
.kvalue = NULL,
.size = size,
.kname = &kname,
.flags = flags,
};
int error;
CLASS(fd, f)(fd);
if (!fd_file(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));
}
kvfree(ctx.kvalue);
return error;
} }
/* /*
* Extended attribute GET operations * Extended attribute GET operations
*/ */
ssize_t static ssize_t
do_getxattr(struct mnt_idmap *idmap, struct dentry *d, do_getxattr(struct mnt_idmap *idmap, struct dentry *d,
struct xattr_ctx *ctx) struct kernel_xattr_ctx *ctx)
{ {
ssize_t error; ssize_t error;
char *kname = ctx->kname->name; char *kname = ctx->kname->name;
void *kvalue = NULL;
if (ctx->size) { if (ctx->size) {
if (ctx->size > XATTR_SIZE_MAX) if (ctx->size > XATTR_SIZE_MAX)
ctx->size = XATTR_SIZE_MAX; ctx->size = XATTR_SIZE_MAX;
ctx->kvalue = kvzalloc(ctx->size, GFP_KERNEL); kvalue = kvzalloc(ctx->size, GFP_KERNEL);
if (!ctx->kvalue) if (!kvalue)
return -ENOMEM; return -ENOMEM;
} }
if (is_posix_acl_xattr(ctx->kname->name)) if (is_posix_acl_xattr(kname))
error = do_get_acl(idmap, d, kname, ctx->kvalue, ctx->size); error = do_get_acl(idmap, d, kname, kvalue, ctx->size);
else else
error = vfs_getxattr(idmap, d, kname, ctx->kvalue, ctx->size); error = vfs_getxattr(idmap, d, kname, kvalue, ctx->size);
if (error > 0) { 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; error = -EFAULT;
} else if (error == -ERANGE && ctx->size >= XATTR_SIZE_MAX) { } else if (error == -ERANGE && ctx->size >= XATTR_SIZE_MAX) {
/* The file system tried to returned a value bigger /* The file system tried to returned a value bigger
@ -746,79 +794,114 @@ do_getxattr(struct mnt_idmap *idmap, struct dentry *d,
error = -E2BIG; error = -E2BIG;
} }
kvfree(kvalue);
return error; return error;
} }
static ssize_t ssize_t file_getxattr(struct file *f, struct kernel_xattr_ctx *ctx)
getxattr(struct mnt_idmap *idmap, struct dentry *d,
const char __user *name, void __user *value, size_t size)
{ {
ssize_t error; audit_file(f);
struct xattr_name kname; return do_getxattr(file_mnt_idmap(f), f->f_path.dentry, ctx);
struct xattr_ctx ctx = {
.value = value,
.kvalue = NULL,
.size = size,
.kname = &kname,
.flags = 0,
};
error = strncpy_from_user(kname.name, name, sizeof(kname.name));
if (error == 0 || error == sizeof(kname.name))
error = -ERANGE;
if (error < 0)
return error;
error = do_getxattr(idmap, d, &ctx);
kvfree(ctx.kvalue);
return error;
} }
static ssize_t path_getxattr(const char __user *pathname, /* unconditionally consumes filename */
const char __user *name, void __user *value, ssize_t filename_getxattr(int dfd, struct filename *filename,
size_t size, unsigned int lookup_flags) unsigned int lookup_flags, struct kernel_xattr_ctx *ctx)
{ {
struct path path; struct path path;
ssize_t error; ssize_t error;
retry: retry:
error = user_path_at(AT_FDCWD, pathname, lookup_flags, &path); error = filename_lookup(dfd, filename, lookup_flags, &path, NULL);
if (error) if (error)
return error; goto out;
error = getxattr(mnt_idmap(path.mnt), path.dentry, name, value, size); error = do_getxattr(mnt_idmap(path.mnt), path.dentry, ctx);
path_put(&path); path_put(&path);
if (retry_estale(error, lookup_flags)) { if (retry_estale(error, lookup_flags)) {
lookup_flags |= LOOKUP_REVAL; lookup_flags |= LOOKUP_REVAL;
goto retry; goto retry;
} }
out:
putname(filename);
return error; return error;
} }
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)
{
struct xattr_name kname;
struct kernel_xattr_ctx ctx = {
.value = value,
.size = size,
.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;
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, SYSCALL_DEFINE4(getxattr, const char __user *, pathname,
const char __user *, name, void __user *, value, size_t, size) 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, SYSCALL_DEFINE4(lgetxattr, const char __user *, pathname,
const char __user *, name, void __user *, value, size_t, size) 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, SYSCALL_DEFINE4(fgetxattr, int, fd, const char __user *, name,
void __user *, value, size_t, size) void __user *, value, size_t, size)
{ {
struct fd f = fdget(fd); return path_getxattrat(fd, NULL, AT_EMPTY_PATH, name, value, size);
ssize_t error = -EBADF;
if (!fd_file(f))
return error;
audit_file(fd_file(f));
error = getxattr(file_mnt_idmap(fd_file(f)), fd_file(f)->f_path.dentry,
name, value, size);
fdput(f);
return error;
} }
/* /*
@ -853,47 +936,80 @@ listxattr(struct dentry *d, char __user *list, size_t size)
return error; return error;
} }
static ssize_t path_listxattr(const char __user *pathname, char __user *list, static
size_t size, unsigned int lookup_flags) 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; struct path path;
ssize_t error; ssize_t error;
retry: retry:
error = user_path_at(AT_FDCWD, pathname, lookup_flags, &path); error = filename_lookup(dfd, filename, lookup_flags, &path, NULL);
if (error) if (error)
return error; goto out;
error = listxattr(path.dentry, list, size); error = listxattr(path.dentry, list, size);
path_put(&path); path_put(&path);
if (retry_estale(error, lookup_flags)) { if (retry_estale(error, lookup_flags)) {
lookup_flags |= LOOKUP_REVAL; lookup_flags |= LOOKUP_REVAL;
goto retry; goto retry;
} }
out:
putname(filename);
return error; return error;
} }
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 path_listxattrat(dfd, pathname, at_flags, list, size);
}
SYSCALL_DEFINE3(listxattr, const char __user *, pathname, char __user *, list, SYSCALL_DEFINE3(listxattr, const char __user *, pathname, char __user *, list,
size_t, size) 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, SYSCALL_DEFINE3(llistxattr, const char __user *, pathname, char __user *, list,
size_t, size) 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) SYSCALL_DEFINE3(flistxattr, int, fd, char __user *, list, size_t, size)
{ {
struct fd f = fdget(fd); return path_listxattrat(fd, NULL, AT_EMPTY_PATH, list, size);
ssize_t error = -EBADF;
if (!fd_file(f))
return error;
audit_file(fd_file(f));
error = listxattr(fd_file(f)->f_path.dentry, list, size);
fdput(f);
return error;
} }
/* /*
@ -907,25 +1023,33 @@ removexattr(struct mnt_idmap *idmap, struct dentry *d, const char *name)
return vfs_removexattr(idmap, d, name); return vfs_removexattr(idmap, d, name);
} }
static int path_removexattr(const char __user *pathname, static int file_removexattr(struct file *f, struct xattr_name *kname)
const char __user *name, unsigned int lookup_flags) {
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; struct path path;
int error; int error;
char kname[XATTR_NAME_MAX + 1];
error = strncpy_from_user(kname, name, sizeof(kname));
if (error == 0 || error == sizeof(kname))
error = -ERANGE;
if (error < 0)
return error;
retry: retry:
error = user_path_at(AT_FDCWD, pathname, lookup_flags, &path); error = filename_lookup(dfd, filename, lookup_flags, &path, NULL);
if (error) if (error)
return error; goto out;
error = mnt_want_write(path.mnt); error = mnt_want_write(path.mnt);
if (!error) { 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); mnt_drop_write(path.mnt);
} }
path_put(&path); path_put(&path);
@ -933,45 +1057,58 @@ retry:
lookup_flags |= LOOKUP_REVAL; lookup_flags |= LOOKUP_REVAL;
goto retry; goto retry;
} }
out:
putname(filename);
return error; return error;
} }
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;
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, SYSCALL_DEFINE2(removexattr, const char __user *, pathname,
const char __user *, name) 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, SYSCALL_DEFINE2(lremovexattr, const char __user *, pathname,
const char __user *, name) 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) SYSCALL_DEFINE2(fremovexattr, int, fd, const char __user *, name)
{ {
struct fd f = fdget(fd); return path_removexattrat(fd, NULL, AT_EMPTY_PATH, name);
char kname[XATTR_NAME_MAX + 1];
int error = -EBADF;
if (!fd_file(f))
return error;
audit_file(fd_file(f));
error = strncpy_from_user(kname, name, sizeof(kname));
if (error == 0 || error == sizeof(kname))
error = -ERANGE;
if (error < 0)
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);
mnt_drop_write_file(fd_file(f));
}
fdput(f);
return error;
} }
int xattr_list_one(char **buffer, ssize_t *remaining_size, const char *name) int xattr_list_one(char **buffer, ssize_t *remaining_size, const char *name)
@ -1005,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; const struct xattr_handler *handler, * const *handlers = dentry->d_sb->s_xattr;
ssize_t remaining_size = buffer_size; ssize_t remaining_size = buffer_size;
int err = 0;
for_each_xattr_handler(handlers, handler) { for_each_xattr_handler(handlers, handler) {
int err;
if (!handler->name || (handler->list && !handler->list(dentry))) if (!handler->name || (handler->list && !handler->list(dentry)))
continue; continue;
err = xattr_list_one(&buffer, &remaining_size, handler->name); err = xattr_list_one(&buffer, &remaining_size, handler->name);
@ -1015,7 +1153,7 @@ generic_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size)
return err; return err;
} }
return err ? err : buffer_size - remaining_size; return buffer_size - remaining_size;
} }
EXPORT_SYMBOL(generic_listxattr); EXPORT_SYMBOL(generic_listxattr);

View File

@ -11,9 +11,15 @@ __NR_lchown,
__NR_fchown, __NR_fchown,
#endif #endif
__NR_setxattr, __NR_setxattr,
#ifdef __NR_setxattrat
__NR_setxattrat,
#endif
__NR_lsetxattr, __NR_lsetxattr,
__NR_fsetxattr, __NR_fsetxattr,
__NR_removexattr, __NR_removexattr,
#ifdef __NR_removexattrat
__NR_removexattrat,
#endif
__NR_lremovexattr, __NR_lremovexattr,
__NR_fremovexattr, __NR_fremovexattr,
#ifdef __NR_fchownat #ifdef __NR_fchownat

View File

@ -2789,6 +2789,16 @@ extern struct filename *getname_flags(const char __user *, int);
extern struct filename *getname_uflags(const char __user *, int); extern struct filename *getname_uflags(const char __user *, int);
extern struct filename *getname(const char __user *); extern struct filename *getname(const char __user *);
extern struct filename *getname_kernel(const char *); 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 void putname(struct filename *name);
extern int finish_open(struct file *file, struct dentry *dentry, extern int finish_open(struct file *file, struct dentry *dentry,

View File

@ -77,6 +77,7 @@ struct cachestat_range;
struct cachestat; struct cachestat;
struct statmount; struct statmount;
struct mnt_id_req; struct mnt_id_req;
struct xattr_args;
#include <linux/types.h> #include <linux/types.h>
#include <linux/aio_abi.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); void __user *arg, unsigned int nr_args);
asmlinkage long sys_setxattr(const char __user *path, const char __user *name, asmlinkage long sys_setxattr(const char __user *path, const char __user *name,
const void __user *value, size_t size, int flags); 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, asmlinkage long sys_lsetxattr(const char __user *path, const char __user *name,
const void __user *value, size_t size, int flags); const void __user *value, size_t size, int flags);
asmlinkage long sys_fsetxattr(int fd, const char __user *name, asmlinkage long sys_fsetxattr(int fd, const char __user *name,
const void __user *value, size_t size, int flags); const void __user *value, size_t size, int flags);
asmlinkage long sys_getxattr(const char __user *path, const char __user *name, asmlinkage long sys_getxattr(const char __user *path, const char __user *name,
void __user *value, size_t size); 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, asmlinkage long sys_lgetxattr(const char __user *path, const char __user *name,
void __user *value, size_t size); void __user *value, size_t size);
asmlinkage long sys_fgetxattr(int fd, const char __user *name, asmlinkage long sys_fgetxattr(int fd, const char __user *name,
void __user *value, size_t size); void __user *value, size_t size);
asmlinkage long sys_listxattr(const char __user *path, char __user *list, asmlinkage long sys_listxattr(const char __user *path, char __user *list,
size_t size); 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, asmlinkage long sys_llistxattr(const char __user *path, char __user *list,
size_t size); size_t size);
asmlinkage long sys_flistxattr(int fd, 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, asmlinkage long sys_removexattr(const char __user *path,
const char __user *name); 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, asmlinkage long sys_lremovexattr(const char __user *path,
const char __user *name); const char __user *name);
asmlinkage long sys_fremovexattr(int fd, const char __user *name); asmlinkage long sys_fremovexattr(int fd, const char __user *name);

View File

@ -19,6 +19,10 @@
#include <linux/user_namespace.h> #include <linux/user_namespace.h>
#include <uapi/linux/xattr.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 inode;
struct dentry; struct dentry;

View File

@ -841,8 +841,17 @@ __SYSCALL(__NR_lsm_list_modules, sys_lsm_list_modules)
#define __NR_mseal 462 #define __NR_mseal 462
__SYSCALL(__NR_mseal, sys_mseal) __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 #undef __NR_syscalls
#define __NR_syscalls 463 #define __NR_syscalls 467
/* /*
* 32 bit systems traditionally used different * 32 bit systems traditionally used different

View File

@ -11,6 +11,7 @@
*/ */
#include <linux/libc-compat.h> #include <linux/libc-compat.h>
#include <linux/types.h>
#ifndef _UAPI_LINUX_XATTR_H #ifndef _UAPI_LINUX_XATTR_H
#define _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_CREATE 0x1 /* set value, fail if attr already exists */
#define XATTR_REPLACE 0x2 /* set value, fail if attr does not exist */ #define XATTR_REPLACE 0x2 /* set value, fail if attr does not exist */
struct xattr_args {
__aligned_u64 __user value;
__u32 size;
__u32 flags;
};
#endif #endif
/* Namespaces */ /* Namespaces */

View File

@ -18,7 +18,7 @@
struct io_xattr { struct io_xattr {
struct file *file; struct file *file;
struct xattr_ctx ctx; struct kernel_xattr_ctx ctx;
struct filename *filename; struct filename *filename;
}; };
@ -48,13 +48,10 @@ static int __io_getxattr_prep(struct io_kiocb *req,
const char __user *name; const char __user *name;
int ret; int ret;
if (unlikely(req->flags & REQ_F_FIXED_FILE))
return -EBADF;
ix->filename = NULL; ix->filename = NULL;
ix->ctx.kvalue = NULL; ix->ctx.kvalue = NULL;
name = u64_to_user_ptr(READ_ONCE(sqe->addr)); 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.size = READ_ONCE(sqe->len);
ix->ctx.flags = READ_ONCE(sqe->xattr_flags); ix->ctx.flags = READ_ONCE(sqe->xattr_flags);
@ -65,11 +62,8 @@ static int __io_getxattr_prep(struct io_kiocb *req,
if (!ix->ctx.kname) if (!ix->ctx.kname)
return -ENOMEM; return -ENOMEM;
ret = strncpy_from_user(ix->ctx.kname->name, name, ret = import_xattr_name(ix->ctx.kname, name);
sizeof(ix->ctx.kname->name)); if (ret) {
if (!ret || ret == sizeof(ix->ctx.kname->name))
ret = -ERANGE;
if (ret < 0) {
kfree(ix->ctx.kname); kfree(ix->ctx.kname);
return ret; return ret;
} }
@ -90,19 +84,20 @@ int io_getxattr_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
const char __user *path; const char __user *path;
int ret; int ret;
if (unlikely(req->flags & REQ_F_FIXED_FILE))
return -EBADF;
ret = __io_getxattr_prep(req, sqe); ret = __io_getxattr_prep(req, sqe);
if (ret) if (ret)
return ret; return ret;
path = u64_to_user_ptr(READ_ONCE(sqe->addr3)); 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)) { if (IS_ERR(ix->filename))
ret = PTR_ERR(ix->filename); return PTR_ERR(ix->filename);
ix->filename = NULL;
}
return ret; return 0;
} }
int io_fgetxattr(struct io_kiocb *req, unsigned int issue_flags) int io_fgetxattr(struct io_kiocb *req, unsigned int issue_flags)
@ -112,10 +107,7 @@ int io_fgetxattr(struct io_kiocb *req, unsigned int issue_flags)
WARN_ON_ONCE(issue_flags & IO_URING_F_NONBLOCK); WARN_ON_ONCE(issue_flags & IO_URING_F_NONBLOCK);
ret = do_getxattr(file_mnt_idmap(req->file), ret = file_getxattr(req->file, &ix->ctx);
req->file->f_path.dentry,
&ix->ctx);
io_xattr_finish(req, ret); io_xattr_finish(req, ret);
return IOU_OK; return IOU_OK;
} }
@ -123,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) int io_getxattr(struct io_kiocb *req, unsigned int issue_flags)
{ {
struct io_xattr *ix = io_kiocb_to_cmd(req, struct io_xattr); struct io_xattr *ix = io_kiocb_to_cmd(req, struct io_xattr);
unsigned int lookup_flags = LOOKUP_FOLLOW;
struct path path;
int ret; int ret;
WARN_ON_ONCE(issue_flags & IO_URING_F_NONBLOCK); WARN_ON_ONCE(issue_flags & IO_URING_F_NONBLOCK);
retry: ret = filename_getxattr(AT_FDCWD, ix->filename, LOOKUP_FOLLOW, &ix->ctx);
ret = filename_lookup(AT_FDCWD, ix->filename, lookup_flags, &path, NULL); ix->filename = 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;
}
}
io_xattr_finish(req, ret); io_xattr_finish(req, ret);
return IOU_OK; return IOU_OK;
} }
@ -152,9 +132,6 @@ static int __io_setxattr_prep(struct io_kiocb *req,
const char __user *name; const char __user *name;
int ret; int ret;
if (unlikely(req->flags & REQ_F_FIXED_FILE))
return -EBADF;
ix->filename = NULL; ix->filename = NULL;
name = u64_to_user_ptr(READ_ONCE(sqe->addr)); name = u64_to_user_ptr(READ_ONCE(sqe->addr));
ix->ctx.cvalue = u64_to_user_ptr(READ_ONCE(sqe->addr2)); ix->ctx.cvalue = u64_to_user_ptr(READ_ONCE(sqe->addr2));
@ -183,19 +160,20 @@ int io_setxattr_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
const char __user *path; const char __user *path;
int ret; int ret;
if (unlikely(req->flags & REQ_F_FIXED_FILE))
return -EBADF;
ret = __io_setxattr_prep(req, sqe); ret = __io_setxattr_prep(req, sqe);
if (ret) if (ret)
return ret; return ret;
path = u64_to_user_ptr(READ_ONCE(sqe->addr3)); 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)) { if (IS_ERR(ix->filename))
ret = PTR_ERR(ix->filename); return PTR_ERR(ix->filename);
ix->filename = NULL;
}
return ret; return 0;
} }
int io_fsetxattr_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) int io_fsetxattr_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
@ -203,28 +181,14 @@ int io_fsetxattr_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
return __io_setxattr_prep(req, sqe); return __io_setxattr_prep(req, sqe);
} }
static int __io_setxattr(struct io_kiocb *req, unsigned int issue_flags, int io_fsetxattr(struct io_kiocb *req, unsigned int issue_flags)
const struct path *path)
{ {
struct io_xattr *ix = io_kiocb_to_cmd(req, struct io_xattr); struct io_xattr *ix = io_kiocb_to_cmd(req, struct io_xattr);
int ret; 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)
{
int ret;
WARN_ON_ONCE(issue_flags & IO_URING_F_NONBLOCK); 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); io_xattr_finish(req, ret);
return IOU_OK; return IOU_OK;
} }
@ -232,23 +196,12 @@ int io_fsetxattr(struct io_kiocb *req, unsigned int issue_flags)
int io_setxattr(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); struct io_xattr *ix = io_kiocb_to_cmd(req, struct io_xattr);
unsigned int lookup_flags = LOOKUP_FOLLOW;
struct path path;
int ret; int ret;
WARN_ON_ONCE(issue_flags & IO_URING_F_NONBLOCK); WARN_ON_ONCE(issue_flags & IO_URING_F_NONBLOCK);
retry: ret = filename_setxattr(AT_FDCWD, ix->filename, LOOKUP_FOLLOW, &ix->ctx);
ret = filename_lookup(AT_FDCWD, ix->filename, lookup_flags, &path, NULL); ix->filename = 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;
}
}
io_xattr_finish(req, ret); io_xattr_finish(req, ret);
return IOU_OK; return IOU_OK;
} }

View File

@ -403,3 +403,7 @@
460 common lsm_set_self_attr sys_lsm_set_self_attr 460 common lsm_set_self_attr sys_lsm_set_self_attr
461 common lsm_list_modules sys_lsm_list_modules 461 common lsm_list_modules sys_lsm_list_modules
462 common mseal sys_mseal 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