1ae5e1b22f17983da929a0d0178896269e19da186Pavel Emelyanov/*
2ae5e1b22f17983da929a0d0178896269e19da186Pavel Emelyanov * linux/ipc/namespace.c
3ae5e1b22f17983da929a0d0178896269e19da186Pavel Emelyanov * Copyright (C) 2006 Pavel Emelyanov <xemul@openvz.org> OpenVZ, SWsoft Inc.
4ae5e1b22f17983da929a0d0178896269e19da186Pavel Emelyanov */
5ae5e1b22f17983da929a0d0178896269e19da186Pavel Emelyanov
6ae5e1b22f17983da929a0d0178896269e19da186Pavel Emelyanov#include <linux/ipc.h>
7ae5e1b22f17983da929a0d0178896269e19da186Pavel Emelyanov#include <linux/msg.h>
8ae5e1b22f17983da929a0d0178896269e19da186Pavel Emelyanov#include <linux/ipc_namespace.h>
9ae5e1b22f17983da929a0d0178896269e19da186Pavel Emelyanov#include <linux/rcupdate.h>
10ae5e1b22f17983da929a0d0178896269e19da186Pavel Emelyanov#include <linux/nsproxy.h>
11ae5e1b22f17983da929a0d0178896269e19da186Pavel Emelyanov#include <linux/slab.h>
127eafd7c74c3f2e67c27621b987b28397110d643fSerge E. Hallyn#include <linux/fs.h>
137eafd7c74c3f2e67c27621b987b28397110d643fSerge E. Hallyn#include <linux/mount.h>
14b515498f5bb5f38fc0e390b4ff7d00b6077de127Serge E. Hallyn#include <linux/user_namespace.h>
150bb80f240520c4148b623161e7856858c021696dDavid Howells#include <linux/proc_ns.h>
16ae5e1b22f17983da929a0d0178896269e19da186Pavel Emelyanov
17ae5e1b22f17983da929a0d0178896269e19da186Pavel Emelyanov#include "util.h"
18ae5e1b22f17983da929a0d0178896269e19da186Pavel Emelyanov
19bcf58e725ddc45d31addbc6627d4f0edccc824c1Eric W. Biedermanstatic struct ipc_namespace *create_ipc_ns(struct user_namespace *user_ns,
20b0e77598f87107001a00b8a4ece9c95e4254ccc4Serge E. Hallyn					   struct ipc_namespace *old_ns)
21ae5e1b22f17983da929a0d0178896269e19da186Pavel Emelyanov{
22ae5e1b22f17983da929a0d0178896269e19da186Pavel Emelyanov	struct ipc_namespace *ns;
237eafd7c74c3f2e67c27621b987b28397110d643fSerge E. Hallyn	int err;
24ae5e1b22f17983da929a0d0178896269e19da186Pavel Emelyanov
25ae5e1b22f17983da929a0d0178896269e19da186Pavel Emelyanov	ns = kmalloc(sizeof(struct ipc_namespace), GFP_KERNEL);
26ae5e1b22f17983da929a0d0178896269e19da186Pavel Emelyanov	if (ns == NULL)
27ed2ddbf88c0ddeeae4c78bb306a116dfd867c55cPierre Peiffer		return ERR_PTR(-ENOMEM);
28ae5e1b22f17983da929a0d0178896269e19da186Pavel Emelyanov
2998f842e675f96ffac96e6c50315790912b2812beEric W. Biederman	err = proc_alloc_inum(&ns->proc_inum);
3098f842e675f96ffac96e6c50315790912b2812beEric W. Biederman	if (err) {
3198f842e675f96ffac96e6c50315790912b2812beEric W. Biederman		kfree(ns);
3298f842e675f96ffac96e6c50315790912b2812beEric W. Biederman		return ERR_PTR(err);
3398f842e675f96ffac96e6c50315790912b2812beEric W. Biederman	}
3498f842e675f96ffac96e6c50315790912b2812beEric W. Biederman
357eafd7c74c3f2e67c27621b987b28397110d643fSerge E. Hallyn	atomic_set(&ns->count, 1);
367eafd7c74c3f2e67c27621b987b28397110d643fSerge E. Hallyn	err = mq_init_ns(ns);
377eafd7c74c3f2e67c27621b987b28397110d643fSerge E. Hallyn	if (err) {
3898f842e675f96ffac96e6c50315790912b2812beEric W. Biederman		proc_free_inum(ns->proc_inum);
397eafd7c74c3f2e67c27621b987b28397110d643fSerge E. Hallyn		kfree(ns);
407eafd7c74c3f2e67c27621b987b28397110d643fSerge E. Hallyn		return ERR_PTR(err);
417eafd7c74c3f2e67c27621b987b28397110d643fSerge E. Hallyn	}
424d89dc6ab2711258bfd12c72d753f3ad56b244e2Nadia Derbey	atomic_inc(&nr_ipc_ns);
434d89dc6ab2711258bfd12c72d753f3ad56b244e2Nadia Derbey
44ed2ddbf88c0ddeeae4c78bb306a116dfd867c55cPierre Peiffer	sem_init_ns(ns);
45ed2ddbf88c0ddeeae4c78bb306a116dfd867c55cPierre Peiffer	msg_init_ns(ns);
46ed2ddbf88c0ddeeae4c78bb306a116dfd867c55cPierre Peiffer	shm_init_ns(ns);
47ae5e1b22f17983da929a0d0178896269e19da186Pavel Emelyanov
48e2c284d8a87f95df9b47c6a13168a844ca7c03e9Nadia Derbey	/*
49e2c284d8a87f95df9b47c6a13168a844ca7c03e9Nadia Derbey	 * msgmni has already been computed for the new ipc ns.
50e2c284d8a87f95df9b47c6a13168a844ca7c03e9Nadia Derbey	 * Thus, do the ipcns creation notification before registering that
51e2c284d8a87f95df9b47c6a13168a844ca7c03e9Nadia Derbey	 * new ipcns in the chain.
52e2c284d8a87f95df9b47c6a13168a844ca7c03e9Nadia Derbey	 */
53e2c284d8a87f95df9b47c6a13168a844ca7c03e9Nadia Derbey	ipcns_notify(IPCNS_CREATED);
54b6b337ad1c1d6fe11b09b35d75464b84b3e11f07Nadia Derbey	register_ipcns_notifier(ns);
55b6b337ad1c1d6fe11b09b35d75464b84b3e11f07Nadia Derbey
56bcf58e725ddc45d31addbc6627d4f0edccc824c1Eric W. Biederman	ns->user_ns = get_user_ns(user_ns);
57b515498f5bb5f38fc0e390b4ff7d00b6077de127Serge E. Hallyn
58ae5e1b22f17983da929a0d0178896269e19da186Pavel Emelyanov	return ns;
59ae5e1b22f17983da929a0d0178896269e19da186Pavel Emelyanov}
60ae5e1b22f17983da929a0d0178896269e19da186Pavel Emelyanov
61b0e77598f87107001a00b8a4ece9c95e4254ccc4Serge E. Hallynstruct ipc_namespace *copy_ipcs(unsigned long flags,
62bcf58e725ddc45d31addbc6627d4f0edccc824c1Eric W. Biederman	struct user_namespace *user_ns, struct ipc_namespace *ns)
63ae5e1b22f17983da929a0d0178896269e19da186Pavel Emelyanov{
64ae5e1b22f17983da929a0d0178896269e19da186Pavel Emelyanov	if (!(flags & CLONE_NEWIPC))
6564424289dd2e37b4800df1f7f2ef4fe550f58729Alexey Dobriyan		return get_ipc_ns(ns);
66bcf58e725ddc45d31addbc6627d4f0edccc824c1Eric W. Biederman	return create_ipc_ns(user_ns, ns);
67ae5e1b22f17983da929a0d0178896269e19da186Pavel Emelyanov}
68ae5e1b22f17983da929a0d0178896269e19da186Pavel Emelyanov
6901b8b07a5d77d22e609267dcae74d15e3e9c5f13Pierre Peiffer/*
7001b8b07a5d77d22e609267dcae74d15e3e9c5f13Pierre Peiffer * free_ipcs - free all ipcs of one type
7101b8b07a5d77d22e609267dcae74d15e3e9c5f13Pierre Peiffer * @ns:   the namespace to remove the ipcs from
7201b8b07a5d77d22e609267dcae74d15e3e9c5f13Pierre Peiffer * @ids:  the table of ipcs to free
7301b8b07a5d77d22e609267dcae74d15e3e9c5f13Pierre Peiffer * @free: the function called to free each individual ipc
7401b8b07a5d77d22e609267dcae74d15e3e9c5f13Pierre Peiffer *
7501b8b07a5d77d22e609267dcae74d15e3e9c5f13Pierre Peiffer * Called for each kind of ipc when an ipc_namespace exits.
7601b8b07a5d77d22e609267dcae74d15e3e9c5f13Pierre Peiffer */
7701b8b07a5d77d22e609267dcae74d15e3e9c5f13Pierre Peiffervoid free_ipcs(struct ipc_namespace *ns, struct ipc_ids *ids,
7801b8b07a5d77d22e609267dcae74d15e3e9c5f13Pierre Peiffer	       void (*free)(struct ipc_namespace *, struct kern_ipc_perm *))
7901b8b07a5d77d22e609267dcae74d15e3e9c5f13Pierre Peiffer{
8001b8b07a5d77d22e609267dcae74d15e3e9c5f13Pierre Peiffer	struct kern_ipc_perm *perm;
8101b8b07a5d77d22e609267dcae74d15e3e9c5f13Pierre Peiffer	int next_id;
8201b8b07a5d77d22e609267dcae74d15e3e9c5f13Pierre Peiffer	int total, in_use;
8301b8b07a5d77d22e609267dcae74d15e3e9c5f13Pierre Peiffer
84d9a605e40b1376eb02b067d7690580255a0df68fDavidlohr Bueso	down_write(&ids->rwsem);
8501b8b07a5d77d22e609267dcae74d15e3e9c5f13Pierre Peiffer
8601b8b07a5d77d22e609267dcae74d15e3e9c5f13Pierre Peiffer	in_use = ids->in_use;
8701b8b07a5d77d22e609267dcae74d15e3e9c5f13Pierre Peiffer
8801b8b07a5d77d22e609267dcae74d15e3e9c5f13Pierre Peiffer	for (total = 0, next_id = 0; total < in_use; next_id++) {
8901b8b07a5d77d22e609267dcae74d15e3e9c5f13Pierre Peiffer		perm = idr_find(&ids->ipcs_idr, next_id);
9001b8b07a5d77d22e609267dcae74d15e3e9c5f13Pierre Peiffer		if (perm == NULL)
9101b8b07a5d77d22e609267dcae74d15e3e9c5f13Pierre Peiffer			continue;
9232a2750010981216fb788c5190fb0e646abfab30Davidlohr Bueso		rcu_read_lock();
9332a2750010981216fb788c5190fb0e646abfab30Davidlohr Bueso		ipc_lock_object(perm);
9401b8b07a5d77d22e609267dcae74d15e3e9c5f13Pierre Peiffer		free(ns, perm);
9501b8b07a5d77d22e609267dcae74d15e3e9c5f13Pierre Peiffer		total++;
9601b8b07a5d77d22e609267dcae74d15e3e9c5f13Pierre Peiffer	}
97d9a605e40b1376eb02b067d7690580255a0df68fDavidlohr Bueso	up_write(&ids->rwsem);
9801b8b07a5d77d22e609267dcae74d15e3e9c5f13Pierre Peiffer}
9901b8b07a5d77d22e609267dcae74d15e3e9c5f13Pierre Peiffer
100b4188def441197d38f20e0935372780ed7c0e19dAlexey Dobriyanstatic void free_ipc_ns(struct ipc_namespace *ns)
101b4188def441197d38f20e0935372780ed7c0e19dAlexey Dobriyan{
102b4188def441197d38f20e0935372780ed7c0e19dAlexey Dobriyan	/*
103b4188def441197d38f20e0935372780ed7c0e19dAlexey Dobriyan	 * Unregistering the hotplug notifier at the beginning guarantees
104b4188def441197d38f20e0935372780ed7c0e19dAlexey Dobriyan	 * that the ipc namespace won't be freed while we are inside the
105b4188def441197d38f20e0935372780ed7c0e19dAlexey Dobriyan	 * callback routine. Since the blocking_notifier_chain_XXX routines
106b4188def441197d38f20e0935372780ed7c0e19dAlexey Dobriyan	 * hold a rw lock on the notifier list, unregister_ipcns_notifier()
107b4188def441197d38f20e0935372780ed7c0e19dAlexey Dobriyan	 * won't take the rw lock before blocking_notifier_call_chain() has
108b4188def441197d38f20e0935372780ed7c0e19dAlexey Dobriyan	 * released the rd lock.
109b4188def441197d38f20e0935372780ed7c0e19dAlexey Dobriyan	 */
110b4188def441197d38f20e0935372780ed7c0e19dAlexey Dobriyan	unregister_ipcns_notifier(ns);
111b4188def441197d38f20e0935372780ed7c0e19dAlexey Dobriyan	sem_exit_ns(ns);
112b4188def441197d38f20e0935372780ed7c0e19dAlexey Dobriyan	msg_exit_ns(ns);
113b4188def441197d38f20e0935372780ed7c0e19dAlexey Dobriyan	shm_exit_ns(ns);
114b4188def441197d38f20e0935372780ed7c0e19dAlexey Dobriyan	atomic_dec(&nr_ipc_ns);
115b4188def441197d38f20e0935372780ed7c0e19dAlexey Dobriyan
116b4188def441197d38f20e0935372780ed7c0e19dAlexey Dobriyan	/*
117b4188def441197d38f20e0935372780ed7c0e19dAlexey Dobriyan	 * Do the ipcns removal notification after decrementing nr_ipc_ns in
118b4188def441197d38f20e0935372780ed7c0e19dAlexey Dobriyan	 * order to have a correct value when recomputing msgmni.
119b4188def441197d38f20e0935372780ed7c0e19dAlexey Dobriyan	 */
120b4188def441197d38f20e0935372780ed7c0e19dAlexey Dobriyan	ipcns_notify(IPCNS_REMOVED);
121b515498f5bb5f38fc0e390b4ff7d00b6077de127Serge E. Hallyn	put_user_ns(ns->user_ns);
12298f842e675f96ffac96e6c50315790912b2812beEric W. Biederman	proc_free_inum(ns->proc_inum);
123be4d250ab41e13f8f945be6896695e870b38ba31Xiaotian Feng	kfree(ns);
124b4188def441197d38f20e0935372780ed7c0e19dAlexey Dobriyan}
125b4188def441197d38f20e0935372780ed7c0e19dAlexey Dobriyan
1267eafd7c74c3f2e67c27621b987b28397110d643fSerge E. Hallyn/*
1277eafd7c74c3f2e67c27621b987b28397110d643fSerge E. Hallyn * put_ipc_ns - drop a reference to an ipc namespace.
1287eafd7c74c3f2e67c27621b987b28397110d643fSerge E. Hallyn * @ns: the namespace to put
1297eafd7c74c3f2e67c27621b987b28397110d643fSerge E. Hallyn *
1307eafd7c74c3f2e67c27621b987b28397110d643fSerge E. Hallyn * If this is the last task in the namespace exiting, and
1317eafd7c74c3f2e67c27621b987b28397110d643fSerge E. Hallyn * it is dropping the refcount to 0, then it can race with
1327eafd7c74c3f2e67c27621b987b28397110d643fSerge E. Hallyn * a task in another ipc namespace but in a mounts namespace
1337eafd7c74c3f2e67c27621b987b28397110d643fSerge E. Hallyn * which has this ipcns's mqueuefs mounted, doing some action
1347eafd7c74c3f2e67c27621b987b28397110d643fSerge E. Hallyn * with one of the mqueuefs files.  That can raise the refcount.
1357eafd7c74c3f2e67c27621b987b28397110d643fSerge E. Hallyn * So dropping the refcount, and raising the refcount when
1367eafd7c74c3f2e67c27621b987b28397110d643fSerge E. Hallyn * accessing it through the VFS, are protected with mq_lock.
1377eafd7c74c3f2e67c27621b987b28397110d643fSerge E. Hallyn *
1387eafd7c74c3f2e67c27621b987b28397110d643fSerge E. Hallyn * (Clearly, a task raising the refcount on its own ipc_ns
1397eafd7c74c3f2e67c27621b987b28397110d643fSerge E. Hallyn * needn't take mq_lock since it can't race with the last task
1407eafd7c74c3f2e67c27621b987b28397110d643fSerge E. Hallyn * in the ipcns exiting).
1417eafd7c74c3f2e67c27621b987b28397110d643fSerge E. Hallyn */
1427eafd7c74c3f2e67c27621b987b28397110d643fSerge E. Hallynvoid put_ipc_ns(struct ipc_namespace *ns)
143ae5e1b22f17983da929a0d0178896269e19da186Pavel Emelyanov{
1447eafd7c74c3f2e67c27621b987b28397110d643fSerge E. Hallyn	if (atomic_dec_and_lock(&ns->count, &mq_lock)) {
1457eafd7c74c3f2e67c27621b987b28397110d643fSerge E. Hallyn		mq_clear_sbinfo(ns);
1467eafd7c74c3f2e67c27621b987b28397110d643fSerge E. Hallyn		spin_unlock(&mq_lock);
1477eafd7c74c3f2e67c27621b987b28397110d643fSerge E. Hallyn		mq_put_mnt(ns);
1487eafd7c74c3f2e67c27621b987b28397110d643fSerge E. Hallyn		free_ipc_ns(ns);
1497eafd7c74c3f2e67c27621b987b28397110d643fSerge E. Hallyn	}
1507eafd7c74c3f2e67c27621b987b28397110d643fSerge E. Hallyn}
151a00eaf11a223c63fbb212369d6db69ce4c55a2d1Eric W. Biederman
152a00eaf11a223c63fbb212369d6db69ce4c55a2d1Eric W. Biedermanstatic void *ipcns_get(struct task_struct *task)
153a00eaf11a223c63fbb212369d6db69ce4c55a2d1Eric W. Biederman{
154a00eaf11a223c63fbb212369d6db69ce4c55a2d1Eric W. Biederman	struct ipc_namespace *ns = NULL;
155a00eaf11a223c63fbb212369d6db69ce4c55a2d1Eric W. Biederman	struct nsproxy *nsproxy;
156a00eaf11a223c63fbb212369d6db69ce4c55a2d1Eric W. Biederman
157728dba3a39c66b3d8ac889ddbe38b5b1c264aec3Eric W. Biederman	task_lock(task);
158728dba3a39c66b3d8ac889ddbe38b5b1c264aec3Eric W. Biederman	nsproxy = task->nsproxy;
159a00eaf11a223c63fbb212369d6db69ce4c55a2d1Eric W. Biederman	if (nsproxy)
160a00eaf11a223c63fbb212369d6db69ce4c55a2d1Eric W. Biederman		ns = get_ipc_ns(nsproxy->ipc_ns);
161728dba3a39c66b3d8ac889ddbe38b5b1c264aec3Eric W. Biederman	task_unlock(task);
162a00eaf11a223c63fbb212369d6db69ce4c55a2d1Eric W. Biederman
163a00eaf11a223c63fbb212369d6db69ce4c55a2d1Eric W. Biederman	return ns;
164a00eaf11a223c63fbb212369d6db69ce4c55a2d1Eric W. Biederman}
165a00eaf11a223c63fbb212369d6db69ce4c55a2d1Eric W. Biederman
166a00eaf11a223c63fbb212369d6db69ce4c55a2d1Eric W. Biedermanstatic void ipcns_put(void *ns)
167a00eaf11a223c63fbb212369d6db69ce4c55a2d1Eric W. Biederman{
168a00eaf11a223c63fbb212369d6db69ce4c55a2d1Eric W. Biederman	return put_ipc_ns(ns);
169a00eaf11a223c63fbb212369d6db69ce4c55a2d1Eric W. Biederman}
170a00eaf11a223c63fbb212369d6db69ce4c55a2d1Eric W. Biederman
171142e1d1d5f088e7a38659daca6e84a730967774aEric W. Biedermanstatic int ipcns_install(struct nsproxy *nsproxy, void *new)
172a00eaf11a223c63fbb212369d6db69ce4c55a2d1Eric W. Biederman{
173142e1d1d5f088e7a38659daca6e84a730967774aEric W. Biederman	struct ipc_namespace *ns = new;
1745e4a08476b50fa39210fca82e03325cc46b9c235Eric W. Biederman	if (!ns_capable(ns->user_ns, CAP_SYS_ADMIN) ||
175c7b96acf1456ef127fef461fcfedb54b81fecfbbEric W. Biederman	    !ns_capable(current_user_ns(), CAP_SYS_ADMIN))
176142e1d1d5f088e7a38659daca6e84a730967774aEric W. Biederman		return -EPERM;
177142e1d1d5f088e7a38659daca6e84a730967774aEric W. Biederman
178a00eaf11a223c63fbb212369d6db69ce4c55a2d1Eric W. Biederman	/* Ditch state from the old ipc namespace */
179a00eaf11a223c63fbb212369d6db69ce4c55a2d1Eric W. Biederman	exit_sem(current);
180a00eaf11a223c63fbb212369d6db69ce4c55a2d1Eric W. Biederman	put_ipc_ns(nsproxy->ipc_ns);
181a00eaf11a223c63fbb212369d6db69ce4c55a2d1Eric W. Biederman	nsproxy->ipc_ns = get_ipc_ns(ns);
182a00eaf11a223c63fbb212369d6db69ce4c55a2d1Eric W. Biederman	return 0;
183a00eaf11a223c63fbb212369d6db69ce4c55a2d1Eric W. Biederman}
184a00eaf11a223c63fbb212369d6db69ce4c55a2d1Eric W. Biederman
18598f842e675f96ffac96e6c50315790912b2812beEric W. Biedermanstatic unsigned int ipcns_inum(void *vp)
18698f842e675f96ffac96e6c50315790912b2812beEric W. Biederman{
18798f842e675f96ffac96e6c50315790912b2812beEric W. Biederman	struct ipc_namespace *ns = vp;
18898f842e675f96ffac96e6c50315790912b2812beEric W. Biederman
18998f842e675f96ffac96e6c50315790912b2812beEric W. Biederman	return ns->proc_inum;
19098f842e675f96ffac96e6c50315790912b2812beEric W. Biederman}
19198f842e675f96ffac96e6c50315790912b2812beEric W. Biederman
192a00eaf11a223c63fbb212369d6db69ce4c55a2d1Eric W. Biedermanconst struct proc_ns_operations ipcns_operations = {
193a00eaf11a223c63fbb212369d6db69ce4c55a2d1Eric W. Biederman	.name		= "ipc",
194a00eaf11a223c63fbb212369d6db69ce4c55a2d1Eric W. Biederman	.type		= CLONE_NEWIPC,
195a00eaf11a223c63fbb212369d6db69ce4c55a2d1Eric W. Biederman	.get		= ipcns_get,
196a00eaf11a223c63fbb212369d6db69ce4c55a2d1Eric W. Biederman	.put		= ipcns_put,
197a00eaf11a223c63fbb212369d6db69ce4c55a2d1Eric W. Biederman	.install	= ipcns_install,
19898f842e675f96ffac96e6c50315790912b2812beEric W. Biederman	.inum		= ipcns_inum,
199a00eaf11a223c63fbb212369d6db69ce4c55a2d1Eric W. Biederman};
200