162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci#include "cgroup-internal.h"
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci#include <linux/sched/task.h>
562306a36Sopenharmony_ci#include <linux/slab.h>
662306a36Sopenharmony_ci#include <linux/nsproxy.h>
762306a36Sopenharmony_ci#include <linux/proc_ns.h>
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci/* cgroup namespaces */
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_cistatic struct ucounts *inc_cgroup_namespaces(struct user_namespace *ns)
1362306a36Sopenharmony_ci{
1462306a36Sopenharmony_ci	return inc_ucount(ns, current_euid(), UCOUNT_CGROUP_NAMESPACES);
1562306a36Sopenharmony_ci}
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_cistatic void dec_cgroup_namespaces(struct ucounts *ucounts)
1862306a36Sopenharmony_ci{
1962306a36Sopenharmony_ci	dec_ucount(ucounts, UCOUNT_CGROUP_NAMESPACES);
2062306a36Sopenharmony_ci}
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_cistatic struct cgroup_namespace *alloc_cgroup_ns(void)
2362306a36Sopenharmony_ci{
2462306a36Sopenharmony_ci	struct cgroup_namespace *new_ns;
2562306a36Sopenharmony_ci	int ret;
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci	new_ns = kzalloc(sizeof(struct cgroup_namespace), GFP_KERNEL_ACCOUNT);
2862306a36Sopenharmony_ci	if (!new_ns)
2962306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
3062306a36Sopenharmony_ci	ret = ns_alloc_inum(&new_ns->ns);
3162306a36Sopenharmony_ci	if (ret) {
3262306a36Sopenharmony_ci		kfree(new_ns);
3362306a36Sopenharmony_ci		return ERR_PTR(ret);
3462306a36Sopenharmony_ci	}
3562306a36Sopenharmony_ci	refcount_set(&new_ns->ns.count, 1);
3662306a36Sopenharmony_ci	new_ns->ns.ops = &cgroupns_operations;
3762306a36Sopenharmony_ci	return new_ns;
3862306a36Sopenharmony_ci}
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_civoid free_cgroup_ns(struct cgroup_namespace *ns)
4162306a36Sopenharmony_ci{
4262306a36Sopenharmony_ci	put_css_set(ns->root_cset);
4362306a36Sopenharmony_ci	dec_cgroup_namespaces(ns->ucounts);
4462306a36Sopenharmony_ci	put_user_ns(ns->user_ns);
4562306a36Sopenharmony_ci	ns_free_inum(&ns->ns);
4662306a36Sopenharmony_ci	kfree(ns);
4762306a36Sopenharmony_ci}
4862306a36Sopenharmony_ciEXPORT_SYMBOL(free_cgroup_ns);
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_cistruct cgroup_namespace *copy_cgroup_ns(unsigned long flags,
5162306a36Sopenharmony_ci					struct user_namespace *user_ns,
5262306a36Sopenharmony_ci					struct cgroup_namespace *old_ns)
5362306a36Sopenharmony_ci{
5462306a36Sopenharmony_ci	struct cgroup_namespace *new_ns;
5562306a36Sopenharmony_ci	struct ucounts *ucounts;
5662306a36Sopenharmony_ci	struct css_set *cset;
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	BUG_ON(!old_ns);
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	if (!(flags & CLONE_NEWCGROUP)) {
6162306a36Sopenharmony_ci		get_cgroup_ns(old_ns);
6262306a36Sopenharmony_ci		return old_ns;
6362306a36Sopenharmony_ci	}
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	/* Allow only sysadmin to create cgroup namespace. */
6662306a36Sopenharmony_ci	if (!ns_capable(user_ns, CAP_SYS_ADMIN))
6762306a36Sopenharmony_ci		return ERR_PTR(-EPERM);
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	ucounts = inc_cgroup_namespaces(user_ns);
7062306a36Sopenharmony_ci	if (!ucounts)
7162306a36Sopenharmony_ci		return ERR_PTR(-ENOSPC);
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	/* It is not safe to take cgroup_mutex here */
7462306a36Sopenharmony_ci	spin_lock_irq(&css_set_lock);
7562306a36Sopenharmony_ci	cset = task_css_set(current);
7662306a36Sopenharmony_ci	get_css_set(cset);
7762306a36Sopenharmony_ci	spin_unlock_irq(&css_set_lock);
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	new_ns = alloc_cgroup_ns();
8062306a36Sopenharmony_ci	if (IS_ERR(new_ns)) {
8162306a36Sopenharmony_ci		put_css_set(cset);
8262306a36Sopenharmony_ci		dec_cgroup_namespaces(ucounts);
8362306a36Sopenharmony_ci		return new_ns;
8462306a36Sopenharmony_ci	}
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	new_ns->user_ns = get_user_ns(user_ns);
8762306a36Sopenharmony_ci	new_ns->ucounts = ucounts;
8862306a36Sopenharmony_ci	new_ns->root_cset = cset;
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	return new_ns;
9162306a36Sopenharmony_ci}
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_cistatic inline struct cgroup_namespace *to_cg_ns(struct ns_common *ns)
9462306a36Sopenharmony_ci{
9562306a36Sopenharmony_ci	return container_of(ns, struct cgroup_namespace, ns);
9662306a36Sopenharmony_ci}
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_cistatic int cgroupns_install(struct nsset *nsset, struct ns_common *ns)
9962306a36Sopenharmony_ci{
10062306a36Sopenharmony_ci	struct nsproxy *nsproxy = nsset->nsproxy;
10162306a36Sopenharmony_ci	struct cgroup_namespace *cgroup_ns = to_cg_ns(ns);
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	if (!ns_capable(nsset->cred->user_ns, CAP_SYS_ADMIN) ||
10462306a36Sopenharmony_ci	    !ns_capable(cgroup_ns->user_ns, CAP_SYS_ADMIN))
10562306a36Sopenharmony_ci		return -EPERM;
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	/* Don't need to do anything if we are attaching to our own cgroupns. */
10862306a36Sopenharmony_ci	if (cgroup_ns == nsproxy->cgroup_ns)
10962306a36Sopenharmony_ci		return 0;
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	get_cgroup_ns(cgroup_ns);
11262306a36Sopenharmony_ci	put_cgroup_ns(nsproxy->cgroup_ns);
11362306a36Sopenharmony_ci	nsproxy->cgroup_ns = cgroup_ns;
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	return 0;
11662306a36Sopenharmony_ci}
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_cistatic struct ns_common *cgroupns_get(struct task_struct *task)
11962306a36Sopenharmony_ci{
12062306a36Sopenharmony_ci	struct cgroup_namespace *ns = NULL;
12162306a36Sopenharmony_ci	struct nsproxy *nsproxy;
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	task_lock(task);
12462306a36Sopenharmony_ci	nsproxy = task->nsproxy;
12562306a36Sopenharmony_ci	if (nsproxy) {
12662306a36Sopenharmony_ci		ns = nsproxy->cgroup_ns;
12762306a36Sopenharmony_ci		get_cgroup_ns(ns);
12862306a36Sopenharmony_ci	}
12962306a36Sopenharmony_ci	task_unlock(task);
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	return ns ? &ns->ns : NULL;
13262306a36Sopenharmony_ci}
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_cistatic void cgroupns_put(struct ns_common *ns)
13562306a36Sopenharmony_ci{
13662306a36Sopenharmony_ci	put_cgroup_ns(to_cg_ns(ns));
13762306a36Sopenharmony_ci}
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_cistatic struct user_namespace *cgroupns_owner(struct ns_common *ns)
14062306a36Sopenharmony_ci{
14162306a36Sopenharmony_ci	return to_cg_ns(ns)->user_ns;
14262306a36Sopenharmony_ci}
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ciconst struct proc_ns_operations cgroupns_operations = {
14562306a36Sopenharmony_ci	.name		= "cgroup",
14662306a36Sopenharmony_ci	.type		= CLONE_NEWCGROUP,
14762306a36Sopenharmony_ci	.get		= cgroupns_get,
14862306a36Sopenharmony_ci	.put		= cgroupns_put,
14962306a36Sopenharmony_ci	.install	= cgroupns_install,
15062306a36Sopenharmony_ci	.owner		= cgroupns_owner,
15162306a36Sopenharmony_ci};
152