diff options
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/cgroup.c | 30 |
1 files changed, 27 insertions, 3 deletions
diff --git a/kernel/cgroup.c b/kernel/cgroup.c index 4504d64f91e1..9ef9fc8a774b 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -2392,7 +2392,9 @@ static int cgroup_attach_task(struct cgroup *dst_cgrp, return ret; } -static int cgroup_procs_write_permission(struct task_struct *task) +static int cgroup_procs_write_permission(struct task_struct *task, + struct cgroup *dst_cgrp, + struct kernfs_open_file *of) { const struct cred *cred = current_cred(); const struct cred *tcred = get_task_cred(task); @@ -2407,6 +2409,26 @@ static int cgroup_procs_write_permission(struct task_struct *task) !uid_eq(cred->euid, tcred->suid)) ret = -EACCES; + if (!ret && cgroup_on_dfl(dst_cgrp)) { + struct super_block *sb = of->file->f_path.dentry->d_sb; + struct cgroup *cgrp; + struct inode *inode; + + down_read(&css_set_rwsem); + cgrp = task_cgroup_from_root(task, &cgrp_dfl_root); + up_read(&css_set_rwsem); + + while (!cgroup_is_descendant(dst_cgrp, cgrp)) + cgrp = cgroup_parent(cgrp); + + ret = -ENOMEM; + inode = kernfs_get_inode(sb, cgrp->procs_kn); + if (inode) { + ret = inode_permission(inode, MAY_WRITE); + iput(inode); + } + } + put_cred(tcred); return ret; } @@ -2459,7 +2481,7 @@ static ssize_t __cgroup_procs_write(struct kernfs_open_file *of, char *buf, get_task_struct(tsk); rcu_read_unlock(); - ret = cgroup_procs_write_permission(tsk); + ret = cgroup_procs_write_permission(tsk, cgrp, of); if (!ret) ret = cgroup_attach_task(cgrp, tsk, threadgroup); @@ -3087,7 +3109,9 @@ static int cgroup_add_file(struct cgroup *cgrp, struct cftype *cft) return ret; } - if (cft->seq_show == cgroup_populated_show) + if (cft->write == cgroup_procs_write) + cgrp->procs_kn = kn; + else if (cft->seq_show == cgroup_populated_show) cgrp->populated_kn = kn; return 0; } |