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
12static struct ucounts *inc_cgroup_namespaces(struct user_namespace *ns)
13{
14 return inc_ucount(ns, current_euid(), type: UCOUNT_CGROUP_NAMESPACES);
15}
16
17static void dec_cgroup_namespaces(struct ucounts *ucounts)
18{
19 dec_ucount(ucounts, type: UCOUNT_CGROUP_NAMESPACES);
20}
21
22static 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
37void 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}
47EXPORT_SYMBOL(free_cgroup_ns);
48
49struct 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
92static 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
112static 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
128static void cgroupns_put(struct ns_common *ns)
129{
130 put_cgroup_ns(ns: to_cg_ns(ns));
131}
132
133static struct user_namespace *cgroupns_owner(struct ns_common *ns)
134{
135 return to_cg_ns(ns)->user_ns;
136}
137
138const 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