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 | |
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; |
25 | int ret; |
26 | |
27 | new_ns = kzalloc(size: sizeof(struct cgroup_namespace), GFP_KERNEL_ACCOUNT); |
28 | if (!new_ns) |
29 | return ERR_PTR(error: -ENOMEM); |
30 | ret = ns_alloc_inum(ns: &new_ns->ns); |
31 | if (ret) { |
32 | kfree(objp: new_ns); |
33 | return ERR_PTR(error: ret); |
34 | } |
35 | refcount_set(r: &new_ns->ns.count, n: 1); |
36 | new_ns->ns.ops = &cgroupns_operations; |
37 | return new_ns; |
38 | } |
39 | |
40 | void free_cgroup_ns(struct cgroup_namespace *ns) |
41 | { |
42 | put_css_set(cset: ns->root_cset); |
43 | dec_cgroup_namespaces(ucounts: ns->ucounts); |
44 | put_user_ns(ns: ns->user_ns); |
45 | ns_free_inum(&ns->ns); |
46 | kfree(objp: ns); |
47 | } |
48 | EXPORT_SYMBOL(free_cgroup_ns); |
49 | |
50 | struct cgroup_namespace *copy_cgroup_ns(unsigned long flags, |
51 | struct user_namespace *user_ns, |
52 | struct cgroup_namespace *old_ns) |
53 | { |
54 | struct cgroup_namespace *new_ns; |
55 | struct ucounts *ucounts; |
56 | struct css_set *cset; |
57 | |
58 | BUG_ON(!old_ns); |
59 | |
60 | if (!(flags & CLONE_NEWCGROUP)) { |
61 | get_cgroup_ns(ns: old_ns); |
62 | return old_ns; |
63 | } |
64 | |
65 | /* Allow only sysadmin to create cgroup namespace. */ |
66 | if (!ns_capable(ns: user_ns, CAP_SYS_ADMIN)) |
67 | return ERR_PTR(error: -EPERM); |
68 | |
69 | ucounts = inc_cgroup_namespaces(ns: user_ns); |
70 | if (!ucounts) |
71 | return ERR_PTR(error: -ENOSPC); |
72 | |
73 | /* It is not safe to take cgroup_mutex here */ |
74 | spin_lock_irq(lock: &css_set_lock); |
75 | cset = task_css_set(current); |
76 | get_css_set(cset); |
77 | spin_unlock_irq(lock: &css_set_lock); |
78 | |
79 | new_ns = alloc_cgroup_ns(); |
80 | if (IS_ERR(ptr: new_ns)) { |
81 | put_css_set(cset); |
82 | dec_cgroup_namespaces(ucounts); |
83 | return new_ns; |
84 | } |
85 | |
86 | new_ns->user_ns = get_user_ns(ns: user_ns); |
87 | new_ns->ucounts = ucounts; |
88 | new_ns->root_cset = cset; |
89 | |
90 | return new_ns; |
91 | } |
92 | |
93 | static inline struct cgroup_namespace *to_cg_ns(struct ns_common *ns) |
94 | { |
95 | return container_of(ns, struct cgroup_namespace, ns); |
96 | } |
97 | |
98 | static int cgroupns_install(struct nsset *nsset, struct ns_common *ns) |
99 | { |
100 | struct nsproxy *nsproxy = nsset->nsproxy; |
101 | struct cgroup_namespace *cgroup_ns = to_cg_ns(ns); |
102 | |
103 | if (!ns_capable(ns: nsset->cred->user_ns, CAP_SYS_ADMIN) || |
104 | !ns_capable(ns: cgroup_ns->user_ns, CAP_SYS_ADMIN)) |
105 | return -EPERM; |
106 | |
107 | /* Don't need to do anything if we are attaching to our own cgroupns. */ |
108 | if (cgroup_ns == nsproxy->cgroup_ns) |
109 | return 0; |
110 | |
111 | get_cgroup_ns(ns: cgroup_ns); |
112 | put_cgroup_ns(ns: nsproxy->cgroup_ns); |
113 | nsproxy->cgroup_ns = cgroup_ns; |
114 | |
115 | return 0; |
116 | } |
117 | |
118 | static struct ns_common *cgroupns_get(struct task_struct *task) |
119 | { |
120 | struct cgroup_namespace *ns = NULL; |
121 | struct nsproxy *nsproxy; |
122 | |
123 | task_lock(p: task); |
124 | nsproxy = task->nsproxy; |
125 | if (nsproxy) { |
126 | ns = nsproxy->cgroup_ns; |
127 | get_cgroup_ns(ns); |
128 | } |
129 | task_unlock(p: task); |
130 | |
131 | return ns ? &ns->ns : NULL; |
132 | } |
133 | |
134 | static void cgroupns_put(struct ns_common *ns) |
135 | { |
136 | put_cgroup_ns(ns: to_cg_ns(ns)); |
137 | } |
138 | |
139 | static struct user_namespace *cgroupns_owner(struct ns_common *ns) |
140 | { |
141 | return to_cg_ns(ns)->user_ns; |
142 | } |
143 | |
144 | const struct proc_ns_operations cgroupns_operations = { |
145 | .name = "cgroup" , |
146 | .type = CLONE_NEWCGROUP, |
147 | .get = cgroupns_get, |
148 | .put = cgroupns_put, |
149 | .install = cgroupns_install, |
150 | .owner = cgroupns_owner, |
151 | }; |
152 | |