kernfs: Add option to enable user xattrs
authorDaniel Xu <dxu@dxuuu.xyz>
Thu, 12 Mar 2020 20:03:16 +0000 (13:03 -0700)
committerTejun Heo <tj@kernel.org>
Mon, 16 Mar 2020 19:53:47 +0000 (15:53 -0400)
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". This is especially important for
desktop linux as delegated subtrees owners are less likely to run as
root.

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 or 128KB -- whichever is hit first -- is placed per cgroup
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@dxuuu.xyz>
Acked-by: Chris Down <chris@chrisdown.name>
Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Tejun Heo <tj@kernel.org>
fs/kernfs/inode.c
fs/kernfs/kernfs-internal.h
include/linux/kernfs.h

index 5f10ae95fbfa727b0effa1c782d5d24cfb039267..fc2469a20fed078b7ddb777cae7cd2dee65aea88 100644 (file)
@@ -53,6 +53,8 @@ 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);
+       atomic_set(&kn->iattr->user_xattr_size, 0);
 out_unlock:
        ret = kn->iattr;
        mutex_unlock(&iattr_mutex);
@@ -327,6 +329,86 @@ 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_add(struct kernfs_node *kn,
+                                    const char *full_name,
+                                    struct simple_xattrs *xattrs,
+                                    const void *value, size_t size, int flags)
+{
+       atomic_t *sz = &kn->iattr->user_xattr_size;
+       atomic_t *nr = &kn->iattr->nr_user_xattrs;
+       ssize_t removed_size;
+       int ret;
+
+       if (atomic_inc_return(nr) > KERNFS_MAX_USER_XATTRS) {
+               ret = -ENOSPC;
+               goto dec_count_out;
+       }
+
+       if (atomic_add_return(size, sz) > KERNFS_USER_XATTR_SIZE_LIMIT) {
+               ret = -ENOSPC;
+               goto dec_size_out;
+       }
+
+       ret = simple_xattr_set(xattrs, full_name, value, size, flags,
+                              &removed_size);
+
+       if (!ret && removed_size >= 0)
+               size = removed_size;
+       else if (!ret)
+               return 0;
+dec_size_out:
+       atomic_sub(size, sz);
+dec_count_out:
+       atomic_dec(nr);
+       return ret;
+}
+
+static int kernfs_vfs_user_xattr_rm(struct kernfs_node *kn,
+                                   const char *full_name,
+                                   struct simple_xattrs *xattrs,
+                                   const void *value, size_t size, int flags)
+{
+       atomic_t *sz = &kn->iattr->user_xattr_size;
+       atomic_t *nr = &kn->iattr->nr_user_xattrs;
+       ssize_t removed_size;
+       int ret;
+
+       ret = simple_xattr_set(xattrs, full_name, value, size, flags,
+                              &removed_size);
+
+       if (removed_size >= 0) {
+               atomic_sub(removed_size, sz);
+               atomic_dec(nr);
+       }
+
+       return ret;
+}
+
+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)
+{
+       const char *full_name = xattr_full_name(handler, suffix);
+       struct kernfs_node *kn = inode->i_private;
+       struct kernfs_iattrs *attrs;
+
+       if (!(kernfs_root(kn)->flags & KERNFS_ROOT_SUPPORT_USER_XATTR))
+               return -EOPNOTSUPP;
+
+       attrs = kernfs_iattrs(kn);
+       if (!attrs)
+               return -ENOMEM;
+
+       if (value)
+               return kernfs_vfs_user_xattr_add(kn, full_name, &attrs->xattrs,
+                                                value, size, flags);
+       else
+               return kernfs_vfs_user_xattr_rm(kn, full_name, &attrs->xattrs,
+                                               value, size, flags);
+
+}
+
 static const struct xattr_handler kernfs_trusted_xattr_handler = {
        .prefix = XATTR_TRUSTED_PREFIX,
        .get = kernfs_vfs_xattr_get,
@@ -339,8 +421,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
 };
index 2f3c51d5526103cf982d1f0ac8a5c9c4fecabe50..7ee97ef59184d190d31cafde86190bc3f593990b 100644 (file)
@@ -26,6 +26,8 @@ struct kernfs_iattrs {
        struct timespec64       ia_ctime;
 
        struct simple_xattrs    xattrs;
+       atomic_t                nr_user_xattrs;
+       atomic_t                user_xattr_size;
 };
 
 /* +1 to avoid triggering overflow warning when negating it */
index dded2e5a9f425902b85dcd0300c9cb3c4977e554..89f6a4214a70c9f03ddc1dafbee825beef109b8f 100644 (file)
@@ -37,8 +37,10 @@ enum kernfs_node_type {
        KERNFS_LINK             = 0x0004,
 };
 
-#define KERNFS_TYPE_MASK       0x000f
-#define KERNFS_FLAG_MASK       ~KERNFS_TYPE_MASK
+#define KERNFS_TYPE_MASK               0x000f
+#define KERNFS_FLAG_MASK               ~KERNFS_TYPE_MASK
+#define KERNFS_MAX_USER_XATTRS         128
+#define KERNFS_USER_XATTR_SIZE_LIMIT   (128 << 10)
 
 enum kernfs_node_flag {
        KERNFS_ACTIVATED        = 0x0010,
@@ -78,6 +80,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 */