diff options
Diffstat (limited to 'ipc')
-rw-r--r-- | ipc/ipc_sysctl.c | 36 | ||||
-rw-r--r-- | ipc/shm.c | 97 |
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 | ||
41 | static 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 | |||
52 | static 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 | |||
40 | static int proc_ipc_callback_dointvec(ctl_table *table, int write, | 65 | static 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), |
@@ -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 | */ | ||
200 | static 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 | |||
234 | static 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 | |||
254 | static 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 | |||
282 | void 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 | |||
290 | void 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); |