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