]> git.proxmox.com Git - mirror_ubuntu-focal-kernel.git/blobdiff - kernel/cgroup/cgroup-v1.c
cgroup: Allocate cgroup_file_ctx for kernfs_open_file->priv
[mirror_ubuntu-focal-kernel.git] / kernel / cgroup / cgroup-v1.c
index 7f83f4121d8d040624d6d044fe974f287123fd82..117d70098cd49aa3d5d0ea9fafc3681b61c97ace 100644 (file)
@@ -398,6 +398,7 @@ static void *cgroup_pidlist_start(struct seq_file *s, loff_t *pos)
         * next pid to display, if any
         */
        struct kernfs_open_file *of = s->private;
+       struct cgroup_file_ctx *ctx = of->priv;
        struct cgroup *cgrp = seq_css(s)->cgroup;
        struct cgroup_pidlist *l;
        enum cgroup_filetype type = seq_cft(s)->private;
@@ -407,25 +408,24 @@ static void *cgroup_pidlist_start(struct seq_file *s, loff_t *pos)
        mutex_lock(&cgrp->pidlist_mutex);
 
        /*
-        * !NULL @of->priv indicates that this isn't the first start()
-        * after open.  If the matching pidlist is around, we can use that.
-        * Look for it.  Note that @of->priv can't be used directly.  It
-        * could already have been destroyed.
+        * !NULL @ctx->procs1.pidlist indicates that this isn't the first
+        * start() after open. If the matching pidlist is around, we can use
+        * that. Look for it. Note that @ctx->procs1.pidlist can't be used
+        * directly. It could already have been destroyed.
         */
-       if (of->priv)
-               of->priv = cgroup_pidlist_find(cgrp, type);
+       if (ctx->procs1.pidlist)
+               ctx->procs1.pidlist = cgroup_pidlist_find(cgrp, type);
 
        /*
         * Either this is the first start() after open or the matching
         * pidlist has been destroyed inbetween.  Create a new one.
         */
-       if (!of->priv) {
-               ret = pidlist_array_load(cgrp, type,
-                                        (struct cgroup_pidlist **)&of->priv);
+       if (!ctx->procs1.pidlist) {
+               ret = pidlist_array_load(cgrp, type, &ctx->procs1.pidlist);
                if (ret)
                        return ERR_PTR(ret);
        }
-       l = of->priv;
+       l = ctx->procs1.pidlist;
 
        if (pid) {
                int end = l->length;
@@ -453,7 +453,8 @@ static void *cgroup_pidlist_start(struct seq_file *s, loff_t *pos)
 static void cgroup_pidlist_stop(struct seq_file *s, void *v)
 {
        struct kernfs_open_file *of = s->private;
-       struct cgroup_pidlist *l = of->priv;
+       struct cgroup_file_ctx *ctx = of->priv;
+       struct cgroup_pidlist *l = ctx->procs1.pidlist;
 
        if (l)
                mod_delayed_work(cgroup_pidlist_destroy_wq, &l->destroy_dwork,
@@ -464,7 +465,8 @@ static void cgroup_pidlist_stop(struct seq_file *s, void *v)
 static void *cgroup_pidlist_next(struct seq_file *s, void *v, loff_t *pos)
 {
        struct kernfs_open_file *of = s->private;
-       struct cgroup_pidlist *l = of->priv;
+       struct cgroup_file_ctx *ctx = of->priv;
+       struct cgroup_pidlist *l = ctx->procs1.pidlist;
        pid_t *p = v;
        pid_t *end = l->list + l->length;
        /*
@@ -473,6 +475,7 @@ static void *cgroup_pidlist_next(struct seq_file *s, void *v, loff_t *pos)
         */
        p++;
        if (p >= end) {
+               (*pos)++;
                return NULL;
        } else {
                *pos = *p;
@@ -506,10 +509,11 @@ static ssize_t __cgroup1_procs_write(struct kernfs_open_file *of,
                goto out_unlock;
 
        /*
-        * Even if we're attaching all tasks in the thread group, we only
-        * need to check permissions on one of them.
+        * Even if we're attaching all tasks in the thread group, we only need
+        * to check permissions on one of them. Check permissions using the
+        * credentials from file open to protect against inherited fd attacks.
         */
-       cred = current_cred();
+       cred = of->file->f_cred;
        tcred = get_task_cred(task);
        if (!uid_eq(cred->euid, GLOBAL_ROOT_UID) &&
            !uid_eq(cred->euid, tcred->uid) &&
@@ -548,6 +552,14 @@ static ssize_t cgroup_release_agent_write(struct kernfs_open_file *of,
 
        BUILD_BUG_ON(sizeof(cgrp->root->release_agent_path) < PATH_MAX);
 
+       /*
+        * Release agent gets called with all capabilities,
+        * require capabilities to set release agent.
+        */
+       if ((of->file->f_cred->user_ns != &init_user_ns) ||
+           !capable(CAP_SYS_ADMIN))
+               return -EPERM;
+
        cgrp = cgroup_kn_lock_live(of->kn, false);
        if (!cgrp)
                return -ENODEV;
@@ -783,7 +795,7 @@ void cgroup1_release_agent(struct work_struct *work)
 
        pathbuf = kmalloc(PATH_MAX, GFP_KERNEL);
        agentbuf = kstrdup(cgrp->root->release_agent_path, GFP_KERNEL);
-       if (!pathbuf || !agentbuf)
+       if (!pathbuf || !agentbuf || !strlen(agentbuf))
                goto out;
 
        spin_lock_irq(&css_set_lock);
@@ -820,6 +832,10 @@ static int cgroup1_rename(struct kernfs_node *kn, struct kernfs_node *new_parent
        struct cgroup *cgrp = kn->priv;
        int ret;
 
+       /* do not accept '\n' to prevent making /proc/<pid>/cgroup unparsable */
+       if (strchr(new_name_str, '\n'))
+               return -EINVAL;
+
        if (kernfs_type(kn) != KERNFS_DIR)
                return -ENOTDIR;
        if (kn->parent != new_parent)
@@ -913,6 +929,10 @@ int cgroup1_parse_param(struct fs_context *fc, struct fs_parameter *param)
        opt = fs_parse(fc, &cgroup1_fs_parameters, param, &result);
        if (opt == -ENOPARAM) {
                if (strcmp(param->key, "source") == 0) {
+                       if (param->type != fs_value_is_string)
+                               return invalf(fc, "Non-string source");
+                       if (fc->source)
+                               return invalf(fc, "Multiple sources not supported");
                        fc->source = param->string;
                        param->string = NULL;
                        return 0;
@@ -952,6 +972,12 @@ int cgroup1_parse_param(struct fs_context *fc, struct fs_parameter *param)
                /* Specifying two release agents is forbidden */
                if (ctx->release_agent)
                        return cg_invalf(fc, "cgroup1: release_agent respecified");
+               /*
+                * Release agent gets called with all capabilities,
+                * require capabilities to set release agent.
+                */
+               if ((fc->user_ns != &init_user_ns) || !capable(CAP_SYS_ADMIN))
+                       return cg_invalf(fc, "cgroup1: Setting release_agent not allowed");
                ctx->release_agent = param->string;
                param->string = NULL;
                break;
@@ -1219,9 +1245,7 @@ int cgroup1_get_tree(struct fs_context *fc)
                ret = cgroup_do_get_tree(fc);
 
        if (!ret && percpu_ref_is_dying(&ctx->root->cgrp.self.refcnt)) {
-               struct super_block *sb = fc->root->d_sb;
-               dput(fc->root);
-               deactivate_locked_super(sb);
+               fc_drop_locked(fc);
                ret = 1;
        }