| 1 | // SPDX-License-Identifier: GPL-2.0 | 
|---|
| 2 | #include "cgroup-internal.h" | 
|---|
| 3 |  | 
|---|
| 4 | #include <linux/sched/task.h> | 
|---|
| 5 | #include <linux/slab.h> | 
|---|
| 6 | #include <linux/nsproxy.h> | 
|---|
| 7 | #include <linux/proc_ns.h> | 
|---|
| 8 | #include <linux/nstree.h> | 
|---|
| 9 |  | 
|---|
| 10 | /* cgroup namespaces */ | 
|---|
| 11 |  | 
|---|
| 12 | static struct ucounts *inc_cgroup_namespaces(struct user_namespace *ns) | 
|---|
| 13 | { | 
|---|
| 14 | return inc_ucount(ns, current_euid(), type: UCOUNT_CGROUP_NAMESPACES); | 
|---|
| 15 | } | 
|---|
| 16 |  | 
|---|
| 17 | static void dec_cgroup_namespaces(struct ucounts *ucounts) | 
|---|
| 18 | { | 
|---|
| 19 | dec_ucount(ucounts, type: UCOUNT_CGROUP_NAMESPACES); | 
|---|
| 20 | } | 
|---|
| 21 |  | 
|---|
| 22 | static struct cgroup_namespace *alloc_cgroup_ns(void) | 
|---|
| 23 | { | 
|---|
| 24 | struct cgroup_namespace *new_ns __free(kfree) = NULL; | 
|---|
| 25 | int ret; | 
|---|
| 26 |  | 
|---|
| 27 | new_ns = kzalloc(sizeof(struct cgroup_namespace), GFP_KERNEL_ACCOUNT); | 
|---|
| 28 | if (!new_ns) | 
|---|
| 29 | return ERR_PTR(error: -ENOMEM); | 
|---|
| 30 | ret = ns_common_init(new_ns); | 
|---|
| 31 | if (ret) | 
|---|
| 32 | return ERR_PTR(error: ret); | 
|---|
| 33 | ns_tree_add(new_ns); | 
|---|
| 34 | return no_free_ptr(new_ns); | 
|---|
| 35 | } | 
|---|
| 36 |  | 
|---|
| 37 | void free_cgroup_ns(struct cgroup_namespace *ns) | 
|---|
| 38 | { | 
|---|
| 39 | ns_tree_remove(ns); | 
|---|
| 40 | put_css_set(cset: ns->root_cset); | 
|---|
| 41 | dec_cgroup_namespaces(ucounts: ns->ucounts); | 
|---|
| 42 | put_user_ns(ns: ns->user_ns); | 
|---|
| 43 | ns_common_free(ns); | 
|---|
| 44 | /* Concurrent nstree traversal depends on a grace period. */ | 
|---|
| 45 | kfree_rcu(ns, ns.ns_rcu); | 
|---|
| 46 | } | 
|---|
| 47 | EXPORT_SYMBOL(free_cgroup_ns); | 
|---|
| 48 |  | 
|---|
| 49 | struct cgroup_namespace *copy_cgroup_ns(u64 flags, | 
|---|
| 50 | struct user_namespace *user_ns, | 
|---|
| 51 | struct cgroup_namespace *old_ns) | 
|---|
| 52 | { | 
|---|
| 53 | struct cgroup_namespace *new_ns; | 
|---|
| 54 | struct ucounts *ucounts; | 
|---|
| 55 | struct css_set *cset; | 
|---|
| 56 |  | 
|---|
| 57 | BUG_ON(!old_ns); | 
|---|
| 58 |  | 
|---|
| 59 | if (!(flags & CLONE_NEWCGROUP)) { | 
|---|
| 60 | get_cgroup_ns(ns: old_ns); | 
|---|
| 61 | return old_ns; | 
|---|
| 62 | } | 
|---|
| 63 |  | 
|---|
| 64 | /* Allow only sysadmin to create cgroup namespace. */ | 
|---|
| 65 | if (!ns_capable(ns: user_ns, CAP_SYS_ADMIN)) | 
|---|
| 66 | return ERR_PTR(error: -EPERM); | 
|---|
| 67 |  | 
|---|
| 68 | ucounts = inc_cgroup_namespaces(ns: user_ns); | 
|---|
| 69 | if (!ucounts) | 
|---|
| 70 | return ERR_PTR(error: -ENOSPC); | 
|---|
| 71 |  | 
|---|
| 72 | /* It is not safe to take cgroup_mutex here */ | 
|---|
| 73 | spin_lock_irq(lock: &css_set_lock); | 
|---|
| 74 | cset = task_css_set(current); | 
|---|
| 75 | get_css_set(cset); | 
|---|
| 76 | spin_unlock_irq(lock: &css_set_lock); | 
|---|
| 77 |  | 
|---|
| 78 | new_ns = alloc_cgroup_ns(); | 
|---|
| 79 | if (IS_ERR(ptr: new_ns)) { | 
|---|
| 80 | put_css_set(cset); | 
|---|
| 81 | dec_cgroup_namespaces(ucounts); | 
|---|
| 82 | return new_ns; | 
|---|
| 83 | } | 
|---|
| 84 |  | 
|---|
| 85 | new_ns->user_ns = get_user_ns(ns: user_ns); | 
|---|
| 86 | new_ns->ucounts = ucounts; | 
|---|
| 87 | new_ns->root_cset = cset; | 
|---|
| 88 |  | 
|---|
| 89 | return new_ns; | 
|---|
| 90 | } | 
|---|
| 91 |  | 
|---|
| 92 | static int cgroupns_install(struct nsset *nsset, struct ns_common *ns) | 
|---|
| 93 | { | 
|---|
| 94 | struct nsproxy *nsproxy = nsset->nsproxy; | 
|---|
| 95 | struct cgroup_namespace *cgroup_ns = to_cg_ns(ns); | 
|---|
| 96 |  | 
|---|
| 97 | if (!ns_capable(ns: nsset->cred->user_ns, CAP_SYS_ADMIN) || | 
|---|
| 98 | !ns_capable(ns: cgroup_ns->user_ns, CAP_SYS_ADMIN)) | 
|---|
| 99 | return -EPERM; | 
|---|
| 100 |  | 
|---|
| 101 | /* Don't need to do anything if we are attaching to our own cgroupns. */ | 
|---|
| 102 | if (cgroup_ns == nsproxy->cgroup_ns) | 
|---|
| 103 | return 0; | 
|---|
| 104 |  | 
|---|
| 105 | get_cgroup_ns(ns: cgroup_ns); | 
|---|
| 106 | put_cgroup_ns(ns: nsproxy->cgroup_ns); | 
|---|
| 107 | nsproxy->cgroup_ns = cgroup_ns; | 
|---|
| 108 |  | 
|---|
| 109 | return 0; | 
|---|
| 110 | } | 
|---|
| 111 |  | 
|---|
| 112 | static struct ns_common *cgroupns_get(struct task_struct *task) | 
|---|
| 113 | { | 
|---|
| 114 | struct cgroup_namespace *ns = NULL; | 
|---|
| 115 | struct nsproxy *nsproxy; | 
|---|
| 116 |  | 
|---|
| 117 | task_lock(p: task); | 
|---|
| 118 | nsproxy = task->nsproxy; | 
|---|
| 119 | if (nsproxy) { | 
|---|
| 120 | ns = nsproxy->cgroup_ns; | 
|---|
| 121 | get_cgroup_ns(ns); | 
|---|
| 122 | } | 
|---|
| 123 | task_unlock(p: task); | 
|---|
| 124 |  | 
|---|
| 125 | return ns ? &ns->ns : NULL; | 
|---|
| 126 | } | 
|---|
| 127 |  | 
|---|
| 128 | static void cgroupns_put(struct ns_common *ns) | 
|---|
| 129 | { | 
|---|
| 130 | put_cgroup_ns(ns: to_cg_ns(ns)); | 
|---|
| 131 | } | 
|---|
| 132 |  | 
|---|
| 133 | static struct user_namespace *cgroupns_owner(struct ns_common *ns) | 
|---|
| 134 | { | 
|---|
| 135 | return to_cg_ns(ns)->user_ns; | 
|---|
| 136 | } | 
|---|
| 137 |  | 
|---|
| 138 | const struct proc_ns_operations cgroupns_operations = { | 
|---|
| 139 | .name		= "cgroup", | 
|---|
| 140 | .get		= cgroupns_get, | 
|---|
| 141 | .put		= cgroupns_put, | 
|---|
| 142 | .install	= cgroupns_install, | 
|---|
| 143 | .owner		= cgroupns_owner, | 
|---|
| 144 | }; | 
|---|
| 145 |  | 
|---|