mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-09-15 15:15:47 +00:00
cgroup: Use open-time cgroup namespace for process migration perm checks
commite574576416
upstream. cgroup process migration permission checks are performed at write time as whether a given operation is allowed or not is dependent on the content of the write - the PID. This currently uses current's cgroup namespace which is a potential security weakness as it may allow scenarios where a less privileged process tricks a more privileged one into writing into a fd that it created. This patch makes cgroup remember the cgroup namespace at the time of open and uses it for migration permission checks instad of current's. Note that this only applies to cgroup2 as cgroup1 doesn't have namespace support. This also fixes a use-after-free bug on cgroupns reported in https://lore.kernel.org/r/00000000000048c15c05d0083397@google.com Note that backporting this fix also requires the preceding patch. Reported-by: "Eric W. Biederman" <ebiederm@xmission.com> Suggested-by: Linus Torvalds <torvalds@linuxfoundation.org> Cc: Michal Koutný <mkoutny@suse.com> Cc: Oleg Nesterov <oleg@redhat.com> Reviewed-by: Michal Koutný <mkoutny@suse.com> Reported-by: syzbot+50f5cf33a284ce738b62@syzkaller.appspotmail.com Link: https://lore.kernel.org/r/00000000000048c15c05d0083397@google.com Fixes:5136f6365c
("cgroup: implement "nsdelegate" mount option") Signed-off-by: Tejun Heo <tj@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
50273128d6
commit
43fa0b3639
2 changed files with 21 additions and 9 deletions
|
@ -68,6 +68,8 @@ static inline struct cgroup_fs_context *cgroup_fc2context(struct fs_context *fc)
|
||||||
struct cgroup_pidlist;
|
struct cgroup_pidlist;
|
||||||
|
|
||||||
struct cgroup_file_ctx {
|
struct cgroup_file_ctx {
|
||||||
|
struct cgroup_namespace *ns;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
void *trigger;
|
void *trigger;
|
||||||
} psi;
|
} psi;
|
||||||
|
|
|
@ -3822,14 +3822,19 @@ static int cgroup_file_open(struct kernfs_open_file *of)
|
||||||
ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
|
ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
|
||||||
if (!ctx)
|
if (!ctx)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
ctx->ns = current->nsproxy->cgroup_ns;
|
||||||
|
get_cgroup_ns(ctx->ns);
|
||||||
of->priv = ctx;
|
of->priv = ctx;
|
||||||
|
|
||||||
if (!cft->open)
|
if (!cft->open)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
ret = cft->open(of);
|
ret = cft->open(of);
|
||||||
if (ret)
|
if (ret) {
|
||||||
|
put_cgroup_ns(ctx->ns);
|
||||||
kfree(ctx);
|
kfree(ctx);
|
||||||
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3840,13 +3845,14 @@ static void cgroup_file_release(struct kernfs_open_file *of)
|
||||||
|
|
||||||
if (cft->release)
|
if (cft->release)
|
||||||
cft->release(of);
|
cft->release(of);
|
||||||
|
put_cgroup_ns(ctx->ns);
|
||||||
kfree(ctx);
|
kfree(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t cgroup_file_write(struct kernfs_open_file *of, char *buf,
|
static ssize_t cgroup_file_write(struct kernfs_open_file *of, char *buf,
|
||||||
size_t nbytes, loff_t off)
|
size_t nbytes, loff_t off)
|
||||||
{
|
{
|
||||||
struct cgroup_namespace *ns = current->nsproxy->cgroup_ns;
|
struct cgroup_file_ctx *ctx = of->priv;
|
||||||
struct cgroup *cgrp = of->kn->parent->priv;
|
struct cgroup *cgrp = of->kn->parent->priv;
|
||||||
struct cftype *cft = of_cft(of);
|
struct cftype *cft = of_cft(of);
|
||||||
struct cgroup_subsys_state *css;
|
struct cgroup_subsys_state *css;
|
||||||
|
@ -3863,7 +3869,7 @@ static ssize_t cgroup_file_write(struct kernfs_open_file *of, char *buf,
|
||||||
*/
|
*/
|
||||||
if ((cgrp->root->flags & CGRP_ROOT_NS_DELEGATE) &&
|
if ((cgrp->root->flags & CGRP_ROOT_NS_DELEGATE) &&
|
||||||
!(cft->flags & CFTYPE_NS_DELEGATABLE) &&
|
!(cft->flags & CFTYPE_NS_DELEGATABLE) &&
|
||||||
ns != &init_cgroup_ns && ns->root_cset->dfl_cgrp == cgrp)
|
ctx->ns != &init_cgroup_ns && ctx->ns->root_cset->dfl_cgrp == cgrp)
|
||||||
return -EPERM;
|
return -EPERM;
|
||||||
|
|
||||||
if (cft->write)
|
if (cft->write)
|
||||||
|
@ -4853,9 +4859,9 @@ static int cgroup_may_write(const struct cgroup *cgrp, struct super_block *sb)
|
||||||
|
|
||||||
static int cgroup_procs_write_permission(struct cgroup *src_cgrp,
|
static int cgroup_procs_write_permission(struct cgroup *src_cgrp,
|
||||||
struct cgroup *dst_cgrp,
|
struct cgroup *dst_cgrp,
|
||||||
struct super_block *sb)
|
struct super_block *sb,
|
||||||
|
struct cgroup_namespace *ns)
|
||||||
{
|
{
|
||||||
struct cgroup_namespace *ns = current->nsproxy->cgroup_ns;
|
|
||||||
struct cgroup *com_cgrp = src_cgrp;
|
struct cgroup *com_cgrp = src_cgrp;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
@ -4884,11 +4890,12 @@ static int cgroup_procs_write_permission(struct cgroup *src_cgrp,
|
||||||
|
|
||||||
static int cgroup_attach_permissions(struct cgroup *src_cgrp,
|
static int cgroup_attach_permissions(struct cgroup *src_cgrp,
|
||||||
struct cgroup *dst_cgrp,
|
struct cgroup *dst_cgrp,
|
||||||
struct super_block *sb, bool threadgroup)
|
struct super_block *sb, bool threadgroup,
|
||||||
|
struct cgroup_namespace *ns)
|
||||||
{
|
{
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
ret = cgroup_procs_write_permission(src_cgrp, dst_cgrp, sb);
|
ret = cgroup_procs_write_permission(src_cgrp, dst_cgrp, sb, ns);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
@ -4905,6 +4912,7 @@ static int cgroup_attach_permissions(struct cgroup *src_cgrp,
|
||||||
static ssize_t __cgroup_procs_write(struct kernfs_open_file *of, char *buf,
|
static ssize_t __cgroup_procs_write(struct kernfs_open_file *of, char *buf,
|
||||||
bool threadgroup)
|
bool threadgroup)
|
||||||
{
|
{
|
||||||
|
struct cgroup_file_ctx *ctx = of->priv;
|
||||||
struct cgroup *src_cgrp, *dst_cgrp;
|
struct cgroup *src_cgrp, *dst_cgrp;
|
||||||
struct task_struct *task;
|
struct task_struct *task;
|
||||||
const struct cred *saved_cred;
|
const struct cred *saved_cred;
|
||||||
|
@ -4932,7 +4940,8 @@ static ssize_t __cgroup_procs_write(struct kernfs_open_file *of, char *buf,
|
||||||
*/
|
*/
|
||||||
saved_cred = override_creds(of->file->f_cred);
|
saved_cred = override_creds(of->file->f_cred);
|
||||||
ret = cgroup_attach_permissions(src_cgrp, dst_cgrp,
|
ret = cgroup_attach_permissions(src_cgrp, dst_cgrp,
|
||||||
of->file->f_path.dentry->d_sb, threadgroup);
|
of->file->f_path.dentry->d_sb,
|
||||||
|
threadgroup, ctx->ns);
|
||||||
revert_creds(saved_cred);
|
revert_creds(saved_cred);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto out_finish;
|
goto out_finish;
|
||||||
|
@ -6149,7 +6158,8 @@ static int cgroup_css_set_fork(struct kernel_clone_args *kargs)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
ret = cgroup_attach_permissions(cset->dfl_cgrp, dst_cgrp, sb,
|
ret = cgroup_attach_permissions(cset->dfl_cgrp, dst_cgrp, sb,
|
||||||
!(kargs->flags & CLONE_THREAD));
|
!(kargs->flags & CLONE_THREAD),
|
||||||
|
current->nsproxy->cgroup_ns);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue