Merge branch 'for-4.12/asus' into for-linus
[sfrench/cifs-2.6.git] / ipc / namespace.c
1 /*
2  * linux/ipc/namespace.c
3  * Copyright (C) 2006 Pavel Emelyanov <xemul@openvz.org> OpenVZ, SWsoft Inc.
4  */
5
6 #include <linux/ipc.h>
7 #include <linux/msg.h>
8 #include <linux/ipc_namespace.h>
9 #include <linux/rcupdate.h>
10 #include <linux/nsproxy.h>
11 #include <linux/slab.h>
12 #include <linux/cred.h>
13 #include <linux/fs.h>
14 #include <linux/mount.h>
15 #include <linux/user_namespace.h>
16 #include <linux/proc_ns.h>
17 #include <linux/sched/task.h>
18
19 #include "util.h"
20
21 static struct ucounts *inc_ipc_namespaces(struct user_namespace *ns)
22 {
23         return inc_ucount(ns, current_euid(), UCOUNT_IPC_NAMESPACES);
24 }
25
26 static void dec_ipc_namespaces(struct ucounts *ucounts)
27 {
28         dec_ucount(ucounts, UCOUNT_IPC_NAMESPACES);
29 }
30
31 static struct ipc_namespace *create_ipc_ns(struct user_namespace *user_ns,
32                                            struct ipc_namespace *old_ns)
33 {
34         struct ipc_namespace *ns;
35         struct ucounts *ucounts;
36         int err;
37
38         err = -ENOSPC;
39         ucounts = inc_ipc_namespaces(user_ns);
40         if (!ucounts)
41                 goto fail;
42
43         err = -ENOMEM;
44         ns = kmalloc(sizeof(struct ipc_namespace), GFP_KERNEL);
45         if (ns == NULL)
46                 goto fail_dec;
47
48         err = ns_alloc_inum(&ns->ns);
49         if (err)
50                 goto fail_free;
51         ns->ns.ops = &ipcns_operations;
52
53         atomic_set(&ns->count, 1);
54         ns->user_ns = get_user_ns(user_ns);
55         ns->ucounts = ucounts;
56
57         err = mq_init_ns(ns);
58         if (err)
59                 goto fail_put;
60
61         sem_init_ns(ns);
62         msg_init_ns(ns);
63         shm_init_ns(ns);
64
65         return ns;
66
67 fail_put:
68         put_user_ns(ns->user_ns);
69         ns_free_inum(&ns->ns);
70 fail_free:
71         kfree(ns);
72 fail_dec:
73         dec_ipc_namespaces(ucounts);
74 fail:
75         return ERR_PTR(err);
76 }
77
78 struct ipc_namespace *copy_ipcs(unsigned long flags,
79         struct user_namespace *user_ns, struct ipc_namespace *ns)
80 {
81         if (!(flags & CLONE_NEWIPC))
82                 return get_ipc_ns(ns);
83         return create_ipc_ns(user_ns, ns);
84 }
85
86 /*
87  * free_ipcs - free all ipcs of one type
88  * @ns:   the namespace to remove the ipcs from
89  * @ids:  the table of ipcs to free
90  * @free: the function called to free each individual ipc
91  *
92  * Called for each kind of ipc when an ipc_namespace exits.
93  */
94 void free_ipcs(struct ipc_namespace *ns, struct ipc_ids *ids,
95                void (*free)(struct ipc_namespace *, struct kern_ipc_perm *))
96 {
97         struct kern_ipc_perm *perm;
98         int next_id;
99         int total, in_use;
100
101         down_write(&ids->rwsem);
102
103         in_use = ids->in_use;
104
105         for (total = 0, next_id = 0; total < in_use; next_id++) {
106                 perm = idr_find(&ids->ipcs_idr, next_id);
107                 if (perm == NULL)
108                         continue;
109                 rcu_read_lock();
110                 ipc_lock_object(perm);
111                 free(ns, perm);
112                 total++;
113         }
114         up_write(&ids->rwsem);
115 }
116
117 static void free_ipc_ns(struct ipc_namespace *ns)
118 {
119         sem_exit_ns(ns);
120         msg_exit_ns(ns);
121         shm_exit_ns(ns);
122
123         dec_ipc_namespaces(ns->ucounts);
124         put_user_ns(ns->user_ns);
125         ns_free_inum(&ns->ns);
126         kfree(ns);
127 }
128
129 /*
130  * put_ipc_ns - drop a reference to an ipc namespace.
131  * @ns: the namespace to put
132  *
133  * If this is the last task in the namespace exiting, and
134  * it is dropping the refcount to 0, then it can race with
135  * a task in another ipc namespace but in a mounts namespace
136  * which has this ipcns's mqueuefs mounted, doing some action
137  * with one of the mqueuefs files.  That can raise the refcount.
138  * So dropping the refcount, and raising the refcount when
139  * accessing it through the VFS, are protected with mq_lock.
140  *
141  * (Clearly, a task raising the refcount on its own ipc_ns
142  * needn't take mq_lock since it can't race with the last task
143  * in the ipcns exiting).
144  */
145 void put_ipc_ns(struct ipc_namespace *ns)
146 {
147         if (atomic_dec_and_lock(&ns->count, &mq_lock)) {
148                 mq_clear_sbinfo(ns);
149                 spin_unlock(&mq_lock);
150                 mq_put_mnt(ns);
151                 free_ipc_ns(ns);
152         }
153 }
154
155 static inline struct ipc_namespace *to_ipc_ns(struct ns_common *ns)
156 {
157         return container_of(ns, struct ipc_namespace, ns);
158 }
159
160 static struct ns_common *ipcns_get(struct task_struct *task)
161 {
162         struct ipc_namespace *ns = NULL;
163         struct nsproxy *nsproxy;
164
165         task_lock(task);
166         nsproxy = task->nsproxy;
167         if (nsproxy)
168                 ns = get_ipc_ns(nsproxy->ipc_ns);
169         task_unlock(task);
170
171         return ns ? &ns->ns : NULL;
172 }
173
174 static void ipcns_put(struct ns_common *ns)
175 {
176         return put_ipc_ns(to_ipc_ns(ns));
177 }
178
179 static int ipcns_install(struct nsproxy *nsproxy, struct ns_common *new)
180 {
181         struct ipc_namespace *ns = to_ipc_ns(new);
182         if (!ns_capable(ns->user_ns, CAP_SYS_ADMIN) ||
183             !ns_capable(current_user_ns(), CAP_SYS_ADMIN))
184                 return -EPERM;
185
186         /* Ditch state from the old ipc namespace */
187         exit_sem(current);
188         put_ipc_ns(nsproxy->ipc_ns);
189         nsproxy->ipc_ns = get_ipc_ns(ns);
190         return 0;
191 }
192
193 static struct user_namespace *ipcns_owner(struct ns_common *ns)
194 {
195         return to_ipc_ns(ns)->user_ns;
196 }
197
198 const struct proc_ns_operations ipcns_operations = {
199         .name           = "ipc",
200         .type           = CLONE_NEWIPC,
201         .get            = ipcns_get,
202         .put            = ipcns_put,
203         .install        = ipcns_install,
204         .owner          = ipcns_owner,
205 };