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