diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index 984f73b719a9..1ed92812785a 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -621,6 +621,7 @@ struct cgroup_namespace { atomic_t count; struct ns_common ns; struct user_namespace *user_ns; + struct ucounts *ucounts; struct css_set *root_cset; }; diff --git a/include/linux/user_namespace.h b/include/linux/user_namespace.h index e1d672186f00..d067f0d3038e 100644 --- a/include/linux/user_namespace.h +++ b/include/linux/user_namespace.h @@ -29,6 +29,7 @@ enum ucount_type { UCOUNT_PID_NAMESPACES, UCOUNT_UTS_NAMESPACES, UCOUNT_IPC_NAMESPACES, + UCOUNT_CGROUP_NAMESPACES, UCOUNT_COUNTS, }; diff --git a/kernel/cgroup.c b/kernel/cgroup.c index d1c51b7f5221..e9e4427fec46 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -6295,6 +6295,16 @@ void cgroup_sk_free(struct sock_cgroup_data *skcd) /* cgroup namespaces */ +static struct ucounts *inc_cgroup_namespaces(struct user_namespace *ns) +{ + return inc_ucount(ns, current_euid(), UCOUNT_CGROUP_NAMESPACES); +} + +static void dec_cgroup_namespaces(struct ucounts *ucounts) +{ + dec_ucount(ucounts, UCOUNT_CGROUP_NAMESPACES); +} + static struct cgroup_namespace *alloc_cgroup_ns(void) { struct cgroup_namespace *new_ns; @@ -6316,6 +6326,7 @@ static struct cgroup_namespace *alloc_cgroup_ns(void) void free_cgroup_ns(struct cgroup_namespace *ns) { put_css_set(ns->root_cset); + dec_cgroup_namespaces(ns->ucounts); put_user_ns(ns->user_ns); ns_free_inum(&ns->ns); kfree(ns); @@ -6327,6 +6338,7 @@ struct cgroup_namespace *copy_cgroup_ns(unsigned long flags, struct cgroup_namespace *old_ns) { struct cgroup_namespace *new_ns; + struct ucounts *ucounts; struct css_set *cset; BUG_ON(!old_ns); @@ -6340,6 +6352,10 @@ struct cgroup_namespace *copy_cgroup_ns(unsigned long flags, if (!ns_capable(user_ns, CAP_SYS_ADMIN)) return ERR_PTR(-EPERM); + ucounts = inc_cgroup_namespaces(user_ns); + if (!ucounts) + return ERR_PTR(-ENFILE); + /* It is not safe to take cgroup_mutex here */ spin_lock_irq(&css_set_lock); cset = task_css_set(current); @@ -6349,10 +6365,12 @@ struct cgroup_namespace *copy_cgroup_ns(unsigned long flags, new_ns = alloc_cgroup_ns(); if (IS_ERR(new_ns)) { put_css_set(cset); + dec_cgroup_namespaces(ucounts); return new_ns; } new_ns->user_ns = get_user_ns(user_ns); + new_ns->ucounts = ucounts; new_ns->root_cset = cset; return new_ns; diff --git a/kernel/ucount.c b/kernel/ucount.c index fbab75424da6..335cc5d2cdd7 100644 --- a/kernel/ucount.c +++ b/kernel/ucount.c @@ -71,6 +71,7 @@ static struct ctl_table user_table[] = { UCOUNT_ENTRY("max_pid_namespaces"), UCOUNT_ENTRY("max_uts_namespaces"), UCOUNT_ENTRY("max_ipc_namespaces"), + UCOUNT_ENTRY("max_cgroup_namespaces"), { } }; #endif /* CONFIG_SYSCTL */