123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157 |
- // SPDX-License-Identifier: GPL-2.0
- #include "cgroup-internal.h"
- #include <linux/sched/task.h>
- #include <linux/slab.h>
- #include <linux/nsproxy.h>
- #include <linux/proc_ns.h>
- /* 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;
- int ret;
- new_ns = kzalloc(sizeof(struct cgroup_namespace), GFP_KERNEL);
- if (!new_ns)
- return ERR_PTR(-ENOMEM);
- ret = ns_alloc_inum(&new_ns->ns);
- if (ret) {
- kfree(new_ns);
- return ERR_PTR(ret);
- }
- refcount_set(&new_ns->count, 1);
- new_ns->ns.ops = &cgroupns_operations;
- return new_ns;
- }
- 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);
- }
- EXPORT_SYMBOL(free_cgroup_ns);
- struct cgroup_namespace *copy_cgroup_ns(unsigned long flags,
- struct user_namespace *user_ns,
- struct cgroup_namespace *old_ns)
- {
- struct cgroup_namespace *new_ns;
- struct ucounts *ucounts;
- struct css_set *cset;
- BUG_ON(!old_ns);
- if (!(flags & CLONE_NEWCGROUP)) {
- get_cgroup_ns(old_ns);
- return old_ns;
- }
- /* Allow only sysadmin to create cgroup namespace. */
- if (!ns_capable(user_ns, CAP_SYS_ADMIN))
- return ERR_PTR(-EPERM);
- ucounts = inc_cgroup_namespaces(user_ns);
- if (!ucounts)
- return ERR_PTR(-ENOSPC);
- /* It is not safe to take cgroup_mutex here */
- spin_lock_irq(&css_set_lock);
- cset = task_css_set(current);
- get_css_set(cset);
- spin_unlock_irq(&css_set_lock);
- 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;
- }
- static inline struct cgroup_namespace *to_cg_ns(struct ns_common *ns)
- {
- return container_of(ns, struct cgroup_namespace, ns);
- }
- static int cgroupns_install(struct nsproxy *nsproxy, struct ns_common *ns)
- {
- struct cgroup_namespace *cgroup_ns = to_cg_ns(ns);
- if (!ns_capable(current_user_ns(), CAP_SYS_ADMIN) ||
- !ns_capable(cgroup_ns->user_ns, CAP_SYS_ADMIN))
- return -EPERM;
- /* Don't need to do anything if we are attaching to our own cgroupns. */
- if (cgroup_ns == nsproxy->cgroup_ns)
- return 0;
- get_cgroup_ns(cgroup_ns);
- put_cgroup_ns(nsproxy->cgroup_ns);
- nsproxy->cgroup_ns = cgroup_ns;
- return 0;
- }
- static struct ns_common *cgroupns_get(struct task_struct *task)
- {
- struct cgroup_namespace *ns = NULL;
- struct nsproxy *nsproxy;
- task_lock(task);
- nsproxy = task->nsproxy;
- if (nsproxy) {
- ns = nsproxy->cgroup_ns;
- get_cgroup_ns(ns);
- }
- task_unlock(task);
- return ns ? &ns->ns : NULL;
- }
- static void cgroupns_put(struct ns_common *ns)
- {
- put_cgroup_ns(to_cg_ns(ns));
- }
- static struct user_namespace *cgroupns_owner(struct ns_common *ns)
- {
- return to_cg_ns(ns)->user_ns;
- }
- const struct proc_ns_operations cgroupns_operations = {
- .name = "cgroup",
- .type = CLONE_NEWCGROUP,
- .get = cgroupns_get,
- .put = cgroupns_put,
- .install = cgroupns_install,
- .owner = cgroupns_owner,
- };
- static __init int cgroup_namespaces_init(void)
- {
- return 0;
- }
- subsys_initcall(cgroup_namespaces_init);
|