aboutsummaryrefslogtreecommitdiffstats
path: root/ipc
diff options
context:
space:
mode:
authorVasiliy Kulikov <segoon@openwall.com>2011-07-26 19:08:48 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2011-07-26 19:49:44 -0400
commitb34a6b1da371ed8af1221459a18c67970f7e3d53 (patch)
tree5addc850de13623b172395b9d0d7d670930fa6b3 /ipc
parentd40dcdb0172a1ba853464983a059fb45e0aaf61a (diff)
ipc: introduce shm_rmid_forced sysctl
Add support for the shm_rmid_forced sysctl. If set to 1, all shared memory objects in current ipc namespace will be automatically forced to use IPC_RMID. The POSIX way of handling shmem allows one to create shm objects and call shmdt(), leaving shm object associated with no process, thus consuming memory not counted via rlimits. With shm_rmid_forced=1 the shared memory object is counted at least for one process, so OOM killer may effectively kill the fat process holding the shared memory. It obviously breaks POSIX - some programs relying on the feature would stop working. So set shm_rmid_forced=1 only if you're sure nobody uses "orphaned" memory. Use shm_rmid_forced=0 by default for compatability reasons. The feature was previously impemented in -ow as a configure option. [akpm@linux-foundation.org: fix documentation, per Randy] [akpm@linux-foundation.org: fix warning] [akpm@linux-foundation.org: readability/conventionality tweaks] [akpm@linux-foundation.org: fix shm_rmid_forced/shm_forced_rmid confusion, use standard comment layout] Signed-off-by: Vasiliy Kulikov <segoon@openwall.com> Cc: Randy Dunlap <rdunlap@xenotime.net> Cc: "Eric W. Biederman" <ebiederm@xmission.com> Cc: "Serge E. Hallyn" <serge.hallyn@canonical.com> Cc: Daniel Lezcano <daniel.lezcano@free.fr> Cc: Oleg Nesterov <oleg@redhat.com> Cc: Tejun Heo <tj@kernel.org> Cc: Ingo Molnar <mingo@elte.hu> Cc: Alan Cox <alan@lxorguk.ukuu.org.uk> Cc: Solar Designer <solar@openwall.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'ipc')
-rw-r--r--ipc/ipc_sysctl.c36
-rw-r--r--ipc/shm.c97
2 files changed, 129 insertions, 4 deletions
diff --git a/ipc/ipc_sysctl.c b/ipc/ipc_sysctl.c
index 56410faa4550..00fba2bab87d 100644
--- a/ipc/ipc_sysctl.c
+++ b/ipc/ipc_sysctl.c
@@ -31,12 +31,37 @@ static int proc_ipc_dointvec(ctl_table *table, int write,
31 void __user *buffer, size_t *lenp, loff_t *ppos) 31 void __user *buffer, size_t *lenp, loff_t *ppos)
32{ 32{
33 struct ctl_table ipc_table; 33 struct ctl_table ipc_table;
34
34 memcpy(&ipc_table, table, sizeof(ipc_table)); 35 memcpy(&ipc_table, table, sizeof(ipc_table));
35 ipc_table.data = get_ipc(table); 36 ipc_table.data = get_ipc(table);
36 37
37 return proc_dointvec(&ipc_table, write, buffer, lenp, ppos); 38 return proc_dointvec(&ipc_table, write, buffer, lenp, ppos);
38} 39}
39 40
41static int proc_ipc_dointvec_minmax(ctl_table *table, int write,
42 void __user *buffer, size_t *lenp, loff_t *ppos)
43{
44 struct ctl_table ipc_table;
45
46 memcpy(&ipc_table, table, sizeof(ipc_table));
47 ipc_table.data = get_ipc(table);
48
49 return proc_dointvec_minmax(&ipc_table, write, buffer, lenp, ppos);
50}
51
52static int proc_ipc_dointvec_minmax_orphans(ctl_table *table, int write,
53 void __user *buffer, size_t *lenp, loff_t *ppos)
54{
55 struct ipc_namespace *ns = current->nsproxy->ipc_ns;
56 int err = proc_ipc_dointvec_minmax(table, write, buffer, lenp, ppos);
57
58 if (err < 0)
59 return err;
60 if (ns->shm_rmid_forced)
61 shm_destroy_orphaned(ns);
62 return err;
63}
64
40static int proc_ipc_callback_dointvec(ctl_table *table, int write, 65static int proc_ipc_callback_dointvec(ctl_table *table, int write,
41 void __user *buffer, size_t *lenp, loff_t *ppos) 66 void __user *buffer, size_t *lenp, loff_t *ppos)
42{ 67{
@@ -125,6 +150,8 @@ static int proc_ipcauto_dointvec_minmax(ctl_table *table, int write,
125#else 150#else
126#define proc_ipc_doulongvec_minmax NULL 151#define proc_ipc_doulongvec_minmax NULL
127#define proc_ipc_dointvec NULL 152#define proc_ipc_dointvec NULL
153#define proc_ipc_dointvec_minmax NULL
154#define proc_ipc_dointvec_minmax_orphans NULL
128#define proc_ipc_callback_dointvec NULL 155#define proc_ipc_callback_dointvec NULL
129#define proc_ipcauto_dointvec_minmax NULL 156#define proc_ipcauto_dointvec_minmax NULL
130#endif 157#endif
@@ -155,6 +182,15 @@ static struct ctl_table ipc_kern_table[] = {
155 .proc_handler = proc_ipc_dointvec, 182 .proc_handler = proc_ipc_dointvec,
156 }, 183 },
157 { 184 {
185 .procname = "shm_rmid_forced",
186 .data = &init_ipc_ns.shm_rmid_forced,
187 .maxlen = sizeof(init_ipc_ns.shm_rmid_forced),
188 .mode = 0644,
189 .proc_handler = proc_ipc_dointvec_minmax_orphans,
190 .extra1 = &zero,
191 .extra2 = &one,
192 },
193 {
158 .procname = "msgmax", 194 .procname = "msgmax",
159 .data = &init_ipc_ns.msg_ctlmax, 195 .data = &init_ipc_ns.msg_ctlmax,
160 .maxlen = sizeof (init_ipc_ns.msg_ctlmax), 196 .maxlen = sizeof (init_ipc_ns.msg_ctlmax),
diff --git a/ipc/shm.c b/ipc/shm.c
index 27884adb1a90..3f5b14365f33 100644
--- a/ipc/shm.c
+++ b/ipc/shm.c
@@ -74,6 +74,7 @@ void shm_init_ns(struct ipc_namespace *ns)
74 ns->shm_ctlmax = SHMMAX; 74 ns->shm_ctlmax = SHMMAX;
75 ns->shm_ctlall = SHMALL; 75 ns->shm_ctlall = SHMALL;
76 ns->shm_ctlmni = SHMMNI; 76 ns->shm_ctlmni = SHMMNI;
77 ns->shm_rmid_forced = 0;
77 ns->shm_tot = 0; 78 ns->shm_tot = 0;
78 ipc_init_ids(&shm_ids(ns)); 79 ipc_init_ids(&shm_ids(ns));
79} 80}
@@ -187,6 +188,23 @@ static void shm_destroy(struct ipc_namespace *ns, struct shmid_kernel *shp)
187} 188}
188 189
189/* 190/*
191 * shm_may_destroy - identifies whether shm segment should be destroyed now
192 *
193 * Returns true if and only if there are no active users of the segment and
194 * one of the following is true:
195 *
196 * 1) shmctl(id, IPC_RMID, NULL) was called for this shp
197 *
198 * 2) sysctl kernel.shm_rmid_forced is set to 1.
199 */
200static bool shm_may_destroy(struct ipc_namespace *ns, struct shmid_kernel *shp)
201{
202 return (shp->shm_nattch == 0) &&
203 (ns->shm_rmid_forced ||
204 (shp->shm_perm.mode & SHM_DEST));
205}
206
207/*
190 * remove the attach descriptor vma. 208 * remove the attach descriptor vma.
191 * free memory for segment if it is marked destroyed. 209 * free memory for segment if it is marked destroyed.
192 * The descriptor has already been removed from the current->mm->mmap list 210 * The descriptor has already been removed from the current->mm->mmap list
@@ -206,11 +224,83 @@ static void shm_close(struct vm_area_struct *vma)
206 shp->shm_lprid = task_tgid_vnr(current); 224 shp->shm_lprid = task_tgid_vnr(current);
207 shp->shm_dtim = get_seconds(); 225 shp->shm_dtim = get_seconds();
208 shp->shm_nattch--; 226 shp->shm_nattch--;
209 if(shp->shm_nattch == 0 && 227 if (shm_may_destroy(ns, shp))
210 shp->shm_perm.mode & SHM_DEST) 228 shm_destroy(ns, shp);
229 else
230 shm_unlock(shp);
231 up_write(&shm_ids(ns).rw_mutex);
232}
233
234static int shm_try_destroy_current(int id, void *p, void *data)
235{
236 struct ipc_namespace *ns = data;
237 struct shmid_kernel *shp = shm_lock(ns, id);
238
239 if (IS_ERR(shp))
240 return 0;
241
242 if (shp->shm_cprid != task_tgid_vnr(current)) {
243 shm_unlock(shp);
244 return 0;
245 }
246
247 if (shm_may_destroy(ns, shp))
248 shm_destroy(ns, shp);
249 else
250 shm_unlock(shp);
251 return 0;
252}
253
254static int shm_try_destroy_orphaned(int id, void *p, void *data)
255{
256 struct ipc_namespace *ns = data;
257 struct shmid_kernel *shp = shm_lock(ns, id);
258 struct task_struct *task;
259
260 if (IS_ERR(shp))
261 return 0;
262
263 /*
264 * We want to destroy segments without users and with already
265 * exit'ed originating process.
266 *
267 * XXX: the originating process may exist in another pid namespace.
268 */
269 task = find_task_by_vpid(shp->shm_cprid);
270 if (task != NULL) {
271 shm_unlock(shp);
272 return 0;
273 }
274
275 if (shm_may_destroy(ns, shp))
211 shm_destroy(ns, shp); 276 shm_destroy(ns, shp);
212 else 277 else
213 shm_unlock(shp); 278 shm_unlock(shp);
279 return 0;
280}
281
282void shm_destroy_orphaned(struct ipc_namespace *ns)
283{
284 down_write(&shm_ids(ns).rw_mutex);
285 idr_for_each(&shm_ids(ns).ipcs_idr, &shm_try_destroy_orphaned, ns);
286 up_write(&shm_ids(ns).rw_mutex);
287}
288
289
290void exit_shm(struct task_struct *task)
291{
292 struct nsproxy *nsp = task->nsproxy;
293 struct ipc_namespace *ns;
294
295 if (!nsp)
296 return;
297 ns = nsp->ipc_ns;
298 if (!ns || !ns->shm_rmid_forced)
299 return;
300
301 /* Destroy all already created segments, but not mapped yet */
302 down_write(&shm_ids(ns).rw_mutex);
303 idr_for_each(&shm_ids(ns).ipcs_idr, &shm_try_destroy_current, ns);
214 up_write(&shm_ids(ns).rw_mutex); 304 up_write(&shm_ids(ns).rw_mutex);
215} 305}
216 306
@@ -950,8 +1040,7 @@ out_nattch:
950 shp = shm_lock(ns, shmid); 1040 shp = shm_lock(ns, shmid);
951 BUG_ON(IS_ERR(shp)); 1041 BUG_ON(IS_ERR(shp));
952 shp->shm_nattch--; 1042 shp->shm_nattch--;
953 if(shp->shm_nattch == 0 && 1043 if (shm_may_destroy(ns, shp))
954 shp->shm_perm.mode & SHM_DEST)
955 shm_destroy(ns, shp); 1044 shm_destroy(ns, shp);
956 else 1045 else
957 shm_unlock(shp); 1046 shm_unlock(shp);