1b2441318SGreg Kroah-Hartman// SPDX-License-Identifier: GPL-2.0
2ae5e1b22SPavel Emelyanov/*
3ae5e1b22SPavel Emelyanov * linux/ipc/namespace.c
4ae5e1b22SPavel Emelyanov * Copyright (C) 2006 Pavel Emelyanov <xemul@openvz.org> OpenVZ, SWsoft Inc.
5ae5e1b22SPavel Emelyanov */
6ae5e1b22SPavel Emelyanov
7ae5e1b22SPavel Emelyanov#include <linux/ipc.h>
8ae5e1b22SPavel Emelyanov#include <linux/msg.h>
9ae5e1b22SPavel Emelyanov#include <linux/ipc_namespace.h>
10ae5e1b22SPavel Emelyanov#include <linux/rcupdate.h>
11ae5e1b22SPavel Emelyanov#include <linux/nsproxy.h>
12ae5e1b22SPavel Emelyanov#include <linux/slab.h>
135b825c3aSIngo Molnar#include <linux/cred.h>
147eafd7c7SSerge E. Hallyn#include <linux/fs.h>
157eafd7c7SSerge E. Hallyn#include <linux/mount.h>
16b515498fSSerge E. Hallyn#include <linux/user_namespace.h>
170bb80f24SDavid Howells#include <linux/proc_ns.h>
18f719ff9bSIngo Molnar#include <linux/sched/task.h>
19ae5e1b22SPavel Emelyanov
20ae5e1b22SPavel Emelyanov#include "util.h"
21ae5e1b22SPavel Emelyanov
22aba35661SEric W. Biedermanstatic struct ucounts *inc_ipc_namespaces(struct user_namespace *ns)
23aba35661SEric W. Biederman{
24aba35661SEric W. Biederman	return inc_ucount(ns, current_euid(), UCOUNT_IPC_NAMESPACES);
25aba35661SEric W. Biederman}
26aba35661SEric W. Biederman
27aba35661SEric W. Biedermanstatic void dec_ipc_namespaces(struct ucounts *ucounts)
28aba35661SEric W. Biederman{
29aba35661SEric W. Biederman	dec_ucount(ucounts, UCOUNT_IPC_NAMESPACES);
30aba35661SEric W. Biederman}
31aba35661SEric W. Biederman
32bcf58e72SEric W. Biedermanstatic struct ipc_namespace *create_ipc_ns(struct user_namespace *user_ns,
33b0e77598SSerge E. Hallyn					   struct ipc_namespace *old_ns)
34ae5e1b22SPavel Emelyanov{
35ae5e1b22SPavel Emelyanov	struct ipc_namespace *ns;
36aba35661SEric W. Biederman	struct ucounts *ucounts;
377eafd7c7SSerge E. Hallyn	int err;
38ae5e1b22SPavel Emelyanov
39df75e774SEric W. Biederman	err = -ENOSPC;
40aba35661SEric W. Biederman	ucounts = inc_ipc_namespaces(user_ns);
41aba35661SEric W. Biederman	if (!ucounts)
42aba35661SEric W. Biederman		goto fail;
43aba35661SEric W. Biederman
44aba35661SEric W. Biederman	err = -ENOMEM;
4530acd0bdSVasily Averin	ns = kzalloc(sizeof(struct ipc_namespace), GFP_KERNEL_ACCOUNT);
46ae5e1b22SPavel Emelyanov	if (ns == NULL)
47aba35661SEric W. Biederman		goto fail_dec;
48ae5e1b22SPavel Emelyanov
496344c433SAl Viro	err = ns_alloc_inum(&ns->ns);
50aba35661SEric W. Biederman	if (err)
51aba35661SEric W. Biederman		goto fail_free;
5233c42940SAl Viro	ns->ns.ops = &ipcns_operations;
5398f842e6SEric W. Biederman
54137ec390SKirill Tkhai	refcount_set(&ns->ns.count, 1);
55b236017aSEric W. Biederman	ns->user_ns = get_user_ns(user_ns);
56aba35661SEric W. Biederman	ns->ucounts = ucounts;
57b236017aSEric W. Biederman
58eae04d25SDavidlohr Bueso	err = mq_init_ns(ns);
59aba35661SEric W. Biederman	if (err)
60aba35661SEric W. Biederman		goto fail_put;
614d89dc6aSNadia Derbey
62eae04d25SDavidlohr Bueso	sem_init_ns(ns);
63eae04d25SDavidlohr Bueso	msg_init_ns(ns);
64eae04d25SDavidlohr Bueso	shm_init_ns(ns);
65ae5e1b22SPavel Emelyanov
66ae5e1b22SPavel Emelyanov	return ns;
67aba35661SEric W. Biederman
68aba35661SEric W. Biedermanfail_put:
69aba35661SEric W. Biederman	put_user_ns(ns->user_ns);
70aba35661SEric W. Biederman	ns_free_inum(&ns->ns);
71aba35661SEric W. Biedermanfail_free:
72aba35661SEric W. Biederman	kfree(ns);
73aba35661SEric W. Biedermanfail_dec:
74aba35661SEric W. Biederman	dec_ipc_namespaces(ucounts);
75aba35661SEric W. Biedermanfail:
76aba35661SEric W. Biederman	return ERR_PTR(err);
77ae5e1b22SPavel Emelyanov}
78ae5e1b22SPavel Emelyanov
79b0e77598SSerge E. Hallynstruct ipc_namespace *copy_ipcs(unsigned long flags,
80bcf58e72SEric W. Biederman	struct user_namespace *user_ns, struct ipc_namespace *ns)
81ae5e1b22SPavel Emelyanov{
82ae5e1b22SPavel Emelyanov	if (!(flags & CLONE_NEWIPC))
8364424289SAlexey Dobriyan		return get_ipc_ns(ns);
84bcf58e72SEric W. Biederman	return create_ipc_ns(user_ns, ns);
85ae5e1b22SPavel Emelyanov}
86ae5e1b22SPavel Emelyanov
8701b8b07aSPierre Peiffer/*
8801b8b07aSPierre Peiffer * free_ipcs - free all ipcs of one type
8901b8b07aSPierre Peiffer * @ns:   the namespace to remove the ipcs from
9001b8b07aSPierre Peiffer * @ids:  the table of ipcs to free
9101b8b07aSPierre Peiffer * @free: the function called to free each individual ipc
9201b8b07aSPierre Peiffer *
9301b8b07aSPierre Peiffer * Called for each kind of ipc when an ipc_namespace exits.
9401b8b07aSPierre Peiffer */
9501b8b07aSPierre Peiffervoid free_ipcs(struct ipc_namespace *ns, struct ipc_ids *ids,
9601b8b07aSPierre Peiffer	       void (*free)(struct ipc_namespace *, struct kern_ipc_perm *))
9701b8b07aSPierre Peiffer{
9801b8b07aSPierre Peiffer	struct kern_ipc_perm *perm;
9901b8b07aSPierre Peiffer	int next_id;
10001b8b07aSPierre Peiffer	int total, in_use;
10101b8b07aSPierre Peiffer
102d9a605e4SDavidlohr Bueso	down_write(&ids->rwsem);
10301b8b07aSPierre Peiffer
10401b8b07aSPierre Peiffer	in_use = ids->in_use;
10501b8b07aSPierre Peiffer
10601b8b07aSPierre Peiffer	for (total = 0, next_id = 0; total < in_use; next_id++) {
10701b8b07aSPierre Peiffer		perm = idr_find(&ids->ipcs_idr, next_id);
10801b8b07aSPierre Peiffer		if (perm == NULL)
10901b8b07aSPierre Peiffer			continue;
11032a27500SDavidlohr Bueso		rcu_read_lock();
11132a27500SDavidlohr Bueso		ipc_lock_object(perm);
11201b8b07aSPierre Peiffer		free(ns, perm);
11301b8b07aSPierre Peiffer		total++;
11401b8b07aSPierre Peiffer	}
115d9a605e4SDavidlohr Bueso	up_write(&ids->rwsem);
11601b8b07aSPierre Peiffer}
11701b8b07aSPierre Peiffer
118b4188defSAlexey Dobriyanstatic void free_ipc_ns(struct ipc_namespace *ns)
119b4188defSAlexey Dobriyan{
120e1eb26faSGiuseppe Scrivano	/* mq_put_mnt() waits for a grace period as kern_unmount()
121e1eb26faSGiuseppe Scrivano	 * uses synchronize_rcu().
122e1eb26faSGiuseppe Scrivano	 */
123e1eb26faSGiuseppe Scrivano	mq_put_mnt(ns);
124b4188defSAlexey Dobriyan	sem_exit_ns(ns);
125b4188defSAlexey Dobriyan	msg_exit_ns(ns);
126b4188defSAlexey Dobriyan	shm_exit_ns(ns);
127b4188defSAlexey Dobriyan
128aba35661SEric W. Biederman	dec_ipc_namespaces(ns->ucounts);
129b515498fSSerge E. Hallyn	put_user_ns(ns->user_ns);
1306344c433SAl Viro	ns_free_inum(&ns->ns);
131be4d250aSXiaotian Feng	kfree(ns);
132b4188defSAlexey Dobriyan}
133b4188defSAlexey Dobriyan
134e1eb26faSGiuseppe Scrivanostatic LLIST_HEAD(free_ipc_list);
135e1eb26faSGiuseppe Scrivanostatic void free_ipc(struct work_struct *unused)
136e1eb26faSGiuseppe Scrivano{
137e1eb26faSGiuseppe Scrivano	struct llist_node *node = llist_del_all(&free_ipc_list);
138e1eb26faSGiuseppe Scrivano	struct ipc_namespace *n, *t;
139e1eb26faSGiuseppe Scrivano
140e1eb26faSGiuseppe Scrivano	llist_for_each_entry_safe(n, t, node, mnt_llist)
141e1eb26faSGiuseppe Scrivano		free_ipc_ns(n);
142e1eb26faSGiuseppe Scrivano}
143e1eb26faSGiuseppe Scrivano
144e1eb26faSGiuseppe Scrivano/*
145e1eb26faSGiuseppe Scrivano * The work queue is used to avoid the cost of synchronize_rcu in kern_unmount.
146e1eb26faSGiuseppe Scrivano */
147e1eb26faSGiuseppe Scrivanostatic DECLARE_WORK(free_ipc_work, free_ipc);
148e1eb26faSGiuseppe Scrivano
1497eafd7c7SSerge E. Hallyn/*
1507eafd7c7SSerge E. Hallyn * put_ipc_ns - drop a reference to an ipc namespace.
1517eafd7c7SSerge E. Hallyn * @ns: the namespace to put
1527eafd7c7SSerge E. Hallyn *
1537eafd7c7SSerge E. Hallyn * If this is the last task in the namespace exiting, and
1547eafd7c7SSerge E. Hallyn * it is dropping the refcount to 0, then it can race with
1557eafd7c7SSerge E. Hallyn * a task in another ipc namespace but in a mounts namespace
1567eafd7c7SSerge E. Hallyn * which has this ipcns's mqueuefs mounted, doing some action
1577eafd7c7SSerge E. Hallyn * with one of the mqueuefs files.  That can raise the refcount.
1587eafd7c7SSerge E. Hallyn * So dropping the refcount, and raising the refcount when
1597eafd7c7SSerge E. Hallyn * accessing it through the VFS, are protected with mq_lock.
1607eafd7c7SSerge E. Hallyn *
1617eafd7c7SSerge E. Hallyn * (Clearly, a task raising the refcount on its own ipc_ns
1627eafd7c7SSerge E. Hallyn * needn't take mq_lock since it can't race with the last task
1637eafd7c7SSerge E. Hallyn * in the ipcns exiting).
1647eafd7c7SSerge E. Hallyn */
1657eafd7c7SSerge E. Hallynvoid put_ipc_ns(struct ipc_namespace *ns)
166ae5e1b22SPavel Emelyanov{
167137ec390SKirill Tkhai	if (refcount_dec_and_lock(&ns->ns.count, &mq_lock)) {
1687eafd7c7SSerge E. Hallyn		mq_clear_sbinfo(ns);
1697eafd7c7SSerge E. Hallyn		spin_unlock(&mq_lock);
170e1eb26faSGiuseppe Scrivano
171e1eb26faSGiuseppe Scrivano		if (llist_add(&ns->mnt_llist, &free_ipc_list))
172e1eb26faSGiuseppe Scrivano			schedule_work(&free_ipc_work);
1737eafd7c7SSerge E. Hallyn	}
1747eafd7c7SSerge E. Hallyn}
175a00eaf11SEric W. Biederman
1763c041184SAl Virostatic inline struct ipc_namespace *to_ipc_ns(struct ns_common *ns)
1773c041184SAl Viro{
1783c041184SAl Viro	return container_of(ns, struct ipc_namespace, ns);
1793c041184SAl Viro}
1803c041184SAl Viro
18164964528SAl Virostatic struct ns_common *ipcns_get(struct task_struct *task)
182a00eaf11SEric W. Biederman{
183a00eaf11SEric W. Biederman	struct ipc_namespace *ns = NULL;
184a00eaf11SEric W. Biederman	struct nsproxy *nsproxy;
185a00eaf11SEric W. Biederman
186728dba3aSEric W. Biederman	task_lock(task);
187728dba3aSEric W. Biederman	nsproxy = task->nsproxy;
188a00eaf11SEric W. Biederman	if (nsproxy)
189a00eaf11SEric W. Biederman		ns = get_ipc_ns(nsproxy->ipc_ns);
190728dba3aSEric W. Biederman	task_unlock(task);
191a00eaf11SEric W. Biederman
1923c041184SAl Viro	return ns ? &ns->ns : NULL;
193a00eaf11SEric W. Biederman}
194a00eaf11SEric W. Biederman
19564964528SAl Virostatic void ipcns_put(struct ns_common *ns)
196a00eaf11SEric W. Biederman{
1973c041184SAl Viro	return put_ipc_ns(to_ipc_ns(ns));
198a00eaf11SEric W. Biederman}
199a00eaf11SEric W. Biederman
200f2a8d52eSChristian Braunerstatic int ipcns_install(struct nsset *nsset, struct ns_common *new)
201a00eaf11SEric W. Biederman{
202f2a8d52eSChristian Brauner	struct nsproxy *nsproxy = nsset->nsproxy;
2033c041184SAl Viro	struct ipc_namespace *ns = to_ipc_ns(new);
2045e4a0847SEric W. Biederman	if (!ns_capable(ns->user_ns, CAP_SYS_ADMIN) ||
205f2a8d52eSChristian Brauner	    !ns_capable(nsset->cred->user_ns, CAP_SYS_ADMIN))
206142e1d1dSEric W. Biederman		return -EPERM;
207142e1d1dSEric W. Biederman
208a00eaf11SEric W. Biederman	put_ipc_ns(nsproxy->ipc_ns);
209a00eaf11SEric W. Biederman	nsproxy->ipc_ns = get_ipc_ns(ns);
210a00eaf11SEric W. Biederman	return 0;
211a00eaf11SEric W. Biederman}
212a00eaf11SEric W. Biederman
213bcac25a5SAndrey Vaginstatic struct user_namespace *ipcns_owner(struct ns_common *ns)
214bcac25a5SAndrey Vagin{
215bcac25a5SAndrey Vagin	return to_ipc_ns(ns)->user_ns;
216bcac25a5SAndrey Vagin}
217bcac25a5SAndrey Vagin
218a00eaf11SEric W. Biedermanconst struct proc_ns_operations ipcns_operations = {
219a00eaf11SEric W. Biederman	.name		= "ipc",
220a00eaf11SEric W. Biederman	.type		= CLONE_NEWIPC,
221a00eaf11SEric W. Biederman	.get		= ipcns_get,
222a00eaf11SEric W. Biederman	.put		= ipcns_put,
223a00eaf11SEric W. Biederman	.install	= ipcns_install,
224bcac25a5SAndrey Vagin	.owner		= ipcns_owner,
225a00eaf11SEric W. Biederman};