[PATCH 1/2] kernfs: Add option to enable user xattrs

From: Daniel Xu
Date: Mon Mar 02 2020 - 20:42:15 EST


User extended attributes are useful as metadata storage for kernfs
consumers like cgroups. Especially in the case of cgroups, it is useful
to have a central metadata store that multiple processes/services can
use to coordinate actions.

A concrete example is for userspace out of memory killers. We want to
let delegated cgroup subtree owners (running as non-root) to be able to
say "please avoid killing this cgroup". In server environments this is
less important as everyone is running as root. But for desktop linux,
this is more important.

This patch introduces a new flag, KERNFS_ROOT_SUPPORT_USER_XATTR, that
lets kernfs consumers enable user xattr support. An initial limit of 128
entries is placed because xattrs come from kernel memory and we don't
want to let unprivileged users accidentally eat up too much kernel
memory.

Signed-off-by: Daniel Xu <dxu@xxxxxxxxx>
---
fs/kernfs/inode.c | 47 +++++++++++++++++++++++++++++++++++++
fs/kernfs/kernfs-internal.h | 1 +
include/linux/kernfs.h | 6 +++++
3 files changed, 54 insertions(+)

diff --git a/fs/kernfs/inode.c b/fs/kernfs/inode.c
index d0f7a5abd9a9..6d603d1177c4 100644
--- a/fs/kernfs/inode.c
+++ b/fs/kernfs/inode.c
@@ -53,6 +53,7 @@ static struct kernfs_iattrs *__kernfs_iattrs(struct kernfs_node *kn, int alloc)
kn->iattr->ia_ctime = kn->iattr->ia_atime;

simple_xattrs_init(&kn->iattr->xattrs);
+ atomic_set(&kn->iattr->nr_user_xattrs, 0);
out_unlock:
ret = kn->iattr;
mutex_unlock(&iattr_mutex);
@@ -327,6 +328,45 @@ static int kernfs_vfs_xattr_set(const struct xattr_handler *handler,
return kernfs_xattr_set(kn, name, value, size, flags);
}

+static int kernfs_vfs_user_xattr_set(const struct xattr_handler *handler,
+ struct dentry *unused, struct inode *inode,
+ const char *suffix, const void *value,
+ size_t size, int flags)
+{
+ struct kernfs_node *kn = inode->i_private;
+ atomic_t *nr = &kn->iattr->nr_user_xattrs;
+ int ret;
+
+ if (!(kernfs_root(kn)->flags & KERNFS_ROOT_SUPPORT_USER_XATTR)) {
+ ret = -EOPNOTSUPP;
+ goto out;
+ }
+
+ if (value && atomic_inc_return(nr) > KERNFS_MAX_USER_XATTRS) {
+ ret = -ENOSPC;
+ goto dec_out;
+ }
+
+ ret = kernfs_vfs_xattr_set(handler, unused, inode, suffix, value,
+ size, flags);
+
+ /*
+ * Don't decrement counter if we successfully added an xattr
+ */
+ if (value && !ret)
+ goto out;
+
+dec_out:
+ /*
+ * Removing a nonexistent xattr without XATTR_REPLACE returns success
+ * so we have to make sure here we don't go negative.
+ */
+ if (atomic_dec_return(nr) < 0)
+ atomic_inc(nr);
+out:
+ return ret;
+}
+
static const struct xattr_handler kernfs_trusted_xattr_handler = {
.prefix = XATTR_TRUSTED_PREFIX,
.get = kernfs_vfs_xattr_get,
@@ -339,8 +379,15 @@ static const struct xattr_handler kernfs_security_xattr_handler = {
.set = kernfs_vfs_xattr_set,
};

+static const struct xattr_handler kernfs_user_xattr_handler = {
+ .prefix = XATTR_USER_PREFIX,
+ .get = kernfs_vfs_xattr_get,
+ .set = kernfs_vfs_user_xattr_set,
+};
+
const struct xattr_handler *kernfs_xattr_handlers[] = {
&kernfs_trusted_xattr_handler,
&kernfs_security_xattr_handler,
+ &kernfs_user_xattr_handler,
NULL
};
diff --git a/fs/kernfs/kernfs-internal.h b/fs/kernfs/kernfs-internal.h
index 2f3c51d55261..745505ce1f37 100644
--- a/fs/kernfs/kernfs-internal.h
+++ b/fs/kernfs/kernfs-internal.h
@@ -26,6 +26,7 @@ struct kernfs_iattrs {
struct timespec64 ia_ctime;

struct simple_xattrs xattrs;
+ atomic_t nr_user_xattrs;
};

/* +1 to avoid triggering overflow warning when negating it */
diff --git a/include/linux/kernfs.h b/include/linux/kernfs.h
index dded2e5a9f42..0e06d67db05f 100644
--- a/include/linux/kernfs.h
+++ b/include/linux/kernfs.h
@@ -39,6 +39,7 @@ enum kernfs_node_type {

#define KERNFS_TYPE_MASK 0x000f
#define KERNFS_FLAG_MASK ~KERNFS_TYPE_MASK
+#define KERNFS_MAX_USER_XATTRS 128

enum kernfs_node_flag {
KERNFS_ACTIVATED = 0x0010,
@@ -78,6 +79,11 @@ enum kernfs_root_flag {
* fhandle to access nodes of the fs.
*/
KERNFS_ROOT_SUPPORT_EXPORTOP = 0x0004,
+
+ /*
+ * Support user xattrs to be written to nodes rooted at this root.
+ */
+ KERNFS_ROOT_SUPPORT_USER_XATTR = 0x0008,
};

/* type-specific structures for kernfs_node union members */
--
2.21.1