diff options
Diffstat (limited to 'ipc/sem.c')
-rw-r--r-- | ipc/sem.c | 111 |
1 files changed, 65 insertions, 46 deletions
@@ -90,7 +90,6 @@ | |||
90 | 90 | ||
91 | #define sem_lock(ns, id) ((struct sem_array*)ipc_lock(&sem_ids(ns), id)) | 91 | #define sem_lock(ns, id) ((struct sem_array*)ipc_lock(&sem_ids(ns), id)) |
92 | #define sem_unlock(sma) ipc_unlock(&(sma)->sem_perm) | 92 | #define sem_unlock(sma) ipc_unlock(&(sma)->sem_perm) |
93 | #define sem_rmid(ns, id) ((struct sem_array*)ipc_rmid(&sem_ids(ns), id)) | ||
94 | #define sem_checkid(ns, sma, semid) \ | 93 | #define sem_checkid(ns, sma, semid) \ |
95 | ipc_checkid(&sem_ids(ns),&sma->sem_perm,semid) | 94 | ipc_checkid(&sem_ids(ns),&sma->sem_perm,semid) |
96 | #define sem_buildid(ns, id, seq) \ | 95 | #define sem_buildid(ns, id, seq) \ |
@@ -99,7 +98,7 @@ | |||
99 | static struct ipc_ids init_sem_ids; | 98 | static struct ipc_ids init_sem_ids; |
100 | 99 | ||
101 | static int newary(struct ipc_namespace *, key_t, int, int); | 100 | static int newary(struct ipc_namespace *, key_t, int, int); |
102 | static void freeary(struct ipc_namespace *ns, struct sem_array *sma, int id); | 101 | static void freeary(struct ipc_namespace *, struct sem_array *); |
103 | #ifdef CONFIG_PROC_FS | 102 | #ifdef CONFIG_PROC_FS |
104 | static int sysvipc_sem_proc_show(struct seq_file *s, void *it); | 103 | static int sysvipc_sem_proc_show(struct seq_file *s, void *it); |
105 | #endif | 104 | #endif |
@@ -129,7 +128,7 @@ static void __sem_init_ns(struct ipc_namespace *ns, struct ipc_ids *ids) | |||
129 | ns->sc_semopm = SEMOPM; | 128 | ns->sc_semopm = SEMOPM; |
130 | ns->sc_semmni = SEMMNI; | 129 | ns->sc_semmni = SEMMNI; |
131 | ns->used_sems = 0; | 130 | ns->used_sems = 0; |
132 | ipc_init_ids(ids, ns->sc_semmni); | 131 | ipc_init_ids(ids); |
133 | } | 132 | } |
134 | 133 | ||
135 | int sem_init_ns(struct ipc_namespace *ns) | 134 | int sem_init_ns(struct ipc_namespace *ns) |
@@ -146,20 +145,24 @@ int sem_init_ns(struct ipc_namespace *ns) | |||
146 | 145 | ||
147 | void sem_exit_ns(struct ipc_namespace *ns) | 146 | void sem_exit_ns(struct ipc_namespace *ns) |
148 | { | 147 | { |
149 | int i; | ||
150 | struct sem_array *sma; | 148 | struct sem_array *sma; |
149 | int next_id; | ||
150 | int total, in_use; | ||
151 | 151 | ||
152 | mutex_lock(&sem_ids(ns).mutex); | 152 | mutex_lock(&sem_ids(ns).mutex); |
153 | for (i = 0; i <= sem_ids(ns).max_id; i++) { | 153 | |
154 | sma = sem_lock(ns, i); | 154 | in_use = sem_ids(ns).in_use; |
155 | |||
156 | for (total = 0, next_id = 0; total < in_use; next_id++) { | ||
157 | sma = idr_find(&sem_ids(ns).ipcs_idr, next_id); | ||
155 | if (sma == NULL) | 158 | if (sma == NULL) |
156 | continue; | 159 | continue; |
157 | 160 | ipc_lock_by_ptr(&sma->sem_perm); | |
158 | freeary(ns, sma, i); | 161 | freeary(ns, sma); |
162 | total++; | ||
159 | } | 163 | } |
160 | mutex_unlock(&sem_ids(ns).mutex); | 164 | mutex_unlock(&sem_ids(ns).mutex); |
161 | 165 | ||
162 | ipc_fini_ids(ns->ids[IPC_SEM_IDS]); | ||
163 | kfree(ns->ids[IPC_SEM_IDS]); | 166 | kfree(ns->ids[IPC_SEM_IDS]); |
164 | ns->ids[IPC_SEM_IDS] = NULL; | 167 | ns->ids[IPC_SEM_IDS] = NULL; |
165 | } | 168 | } |
@@ -172,6 +175,11 @@ void __init sem_init (void) | |||
172 | IPC_SEM_IDS, sysvipc_sem_proc_show); | 175 | IPC_SEM_IDS, sysvipc_sem_proc_show); |
173 | } | 176 | } |
174 | 177 | ||
178 | static inline void sem_rmid(struct ipc_namespace *ns, struct sem_array *s) | ||
179 | { | ||
180 | ipc_rmid(&sem_ids(ns), &s->sem_perm); | ||
181 | } | ||
182 | |||
175 | /* | 183 | /* |
176 | * Lockless wakeup algorithm: | 184 | * Lockless wakeup algorithm: |
177 | * Without the check/retry algorithm a lockless wakeup is possible: | 185 | * Without the check/retry algorithm a lockless wakeup is possible: |
@@ -243,7 +251,7 @@ static int newary (struct ipc_namespace *ns, key_t key, int nsems, int semflg) | |||
243 | } | 251 | } |
244 | ns->used_sems += nsems; | 252 | ns->used_sems += nsems; |
245 | 253 | ||
246 | sma->sem_id = sem_buildid(ns, id, sma->sem_perm.seq); | 254 | sma->sem_perm.id = sem_buildid(ns, id, sma->sem_perm.seq); |
247 | sma->sem_base = (struct sem *) &sma[1]; | 255 | sma->sem_base = (struct sem *) &sma[1]; |
248 | /* sma->sem_pending = NULL; */ | 256 | /* sma->sem_pending = NULL; */ |
249 | sma->sem_pending_last = &sma->sem_pending; | 257 | sma->sem_pending_last = &sma->sem_pending; |
@@ -252,12 +260,12 @@ static int newary (struct ipc_namespace *ns, key_t key, int nsems, int semflg) | |||
252 | sma->sem_ctime = get_seconds(); | 260 | sma->sem_ctime = get_seconds(); |
253 | sem_unlock(sma); | 261 | sem_unlock(sma); |
254 | 262 | ||
255 | return sma->sem_id; | 263 | return sma->sem_perm.id; |
256 | } | 264 | } |
257 | 265 | ||
258 | asmlinkage long sys_semget (key_t key, int nsems, int semflg) | 266 | asmlinkage long sys_semget (key_t key, int nsems, int semflg) |
259 | { | 267 | { |
260 | int id, err = -EINVAL; | 268 | int err; |
261 | struct sem_array *sma; | 269 | struct sem_array *sma; |
262 | struct ipc_namespace *ns; | 270 | struct ipc_namespace *ns; |
263 | 271 | ||
@@ -265,34 +273,50 @@ asmlinkage long sys_semget (key_t key, int nsems, int semflg) | |||
265 | 273 | ||
266 | if (nsems < 0 || nsems > ns->sc_semmsl) | 274 | if (nsems < 0 || nsems > ns->sc_semmsl) |
267 | return -EINVAL; | 275 | return -EINVAL; |
268 | mutex_lock(&sem_ids(ns).mutex); | 276 | |
269 | 277 | err = idr_pre_get(&sem_ids(ns).ipcs_idr, GFP_KERNEL); | |
278 | |||
270 | if (key == IPC_PRIVATE) { | 279 | if (key == IPC_PRIVATE) { |
271 | err = newary(ns, key, nsems, semflg); | 280 | if (!err) |
272 | } else if ((id = ipc_findkey(&sem_ids(ns), key)) == -1) { /* key not used */ | 281 | err = -ENOMEM; |
273 | if (!(semflg & IPC_CREAT)) | 282 | else { |
274 | err = -ENOENT; | 283 | mutex_lock(&sem_ids(ns).mutex); |
275 | else | ||
276 | err = newary(ns, key, nsems, semflg); | 284 | err = newary(ns, key, nsems, semflg); |
277 | } else if (semflg & IPC_CREAT && semflg & IPC_EXCL) { | 285 | mutex_unlock(&sem_ids(ns).mutex); |
278 | err = -EEXIST; | 286 | } |
279 | } else { | 287 | } else { |
280 | sma = sem_lock(ns, id); | 288 | mutex_lock(&sem_ids(ns).mutex); |
281 | BUG_ON(sma==NULL); | 289 | sma = (struct sem_array *) ipc_findkey(&sem_ids(ns), key); |
282 | if (nsems > sma->sem_nsems) | 290 | if (sma == NULL) { |
283 | err = -EINVAL; | 291 | /* key not used */ |
284 | else if (ipcperms(&sma->sem_perm, semflg)) | 292 | if (!(semflg & IPC_CREAT)) |
285 | err = -EACCES; | 293 | err = -ENOENT; |
286 | else { | 294 | else if (!err) |
287 | int semid = sem_buildid(ns, id, sma->sem_perm.seq); | 295 | err = -ENOMEM; |
288 | err = security_sem_associate(sma, semflg); | 296 | else |
289 | if (!err) | 297 | err = newary(ns, key, nsems, semflg); |
290 | err = semid; | 298 | } else { |
299 | /* sma has been locked by ipc_findkey() */ | ||
300 | |||
301 | if (semflg & IPC_CREAT && semflg & IPC_EXCL) | ||
302 | err = -EEXIST; | ||
303 | else { | ||
304 | if (nsems > sma->sem_nsems) | ||
305 | err = -EINVAL; | ||
306 | else if (ipcperms(&sma->sem_perm, semflg)) | ||
307 | err = -EACCES; | ||
308 | else { | ||
309 | err = security_sem_associate(sma, | ||
310 | semflg); | ||
311 | if (!err) | ||
312 | err = sma->sem_perm.id; | ||
313 | } | ||
314 | } | ||
315 | sem_unlock(sma); | ||
291 | } | 316 | } |
292 | sem_unlock(sma); | 317 | mutex_unlock(&sem_ids(ns).mutex); |
293 | } | 318 | } |
294 | 319 | ||
295 | mutex_unlock(&sem_ids(ns).mutex); | ||
296 | return err; | 320 | return err; |
297 | } | 321 | } |
298 | 322 | ||
@@ -491,11 +515,10 @@ static int count_semzcnt (struct sem_array * sma, ushort semnum) | |||
491 | * the spinlock for this semaphore set hold. sem_ids.mutex remains locked | 515 | * the spinlock for this semaphore set hold. sem_ids.mutex remains locked |
492 | * on exit. | 516 | * on exit. |
493 | */ | 517 | */ |
494 | static void freeary (struct ipc_namespace *ns, struct sem_array *sma, int id) | 518 | static void freeary(struct ipc_namespace *ns, struct sem_array *sma) |
495 | { | 519 | { |
496 | struct sem_undo *un; | 520 | struct sem_undo *un; |
497 | struct sem_queue *q; | 521 | struct sem_queue *q; |
498 | int size; | ||
499 | 522 | ||
500 | /* Invalidate the existing undo structures for this semaphore set. | 523 | /* Invalidate the existing undo structures for this semaphore set. |
501 | * (They will be freed without any further action in exit_sem() | 524 | * (They will be freed without any further action in exit_sem() |
@@ -518,12 +541,11 @@ static void freeary (struct ipc_namespace *ns, struct sem_array *sma, int id) | |||
518 | q = n; | 541 | q = n; |
519 | } | 542 | } |
520 | 543 | ||
521 | /* Remove the semaphore set from the ID array*/ | 544 | /* Remove the semaphore set from the IDR */ |
522 | sma = sem_rmid(ns, id); | 545 | sem_rmid(ns, sma); |
523 | sem_unlock(sma); | 546 | sem_unlock(sma); |
524 | 547 | ||
525 | ns->used_sems -= sma->sem_nsems; | 548 | ns->used_sems -= sma->sem_nsems; |
526 | size = sizeof (*sma) + sma->sem_nsems * sizeof (struct sem); | ||
527 | security_sem_free(sma); | 549 | security_sem_free(sma); |
528 | ipc_rcu_putref(sma); | 550 | ipc_rcu_putref(sma); |
529 | } | 551 | } |
@@ -584,7 +606,7 @@ static int semctl_nolock(struct ipc_namespace *ns, int semid, int semnum, | |||
584 | seminfo.semusz = SEMUSZ; | 606 | seminfo.semusz = SEMUSZ; |
585 | seminfo.semaem = SEMAEM; | 607 | seminfo.semaem = SEMAEM; |
586 | } | 608 | } |
587 | max_id = sem_ids(ns).max_id; | 609 | max_id = ipc_get_maxid(&sem_ids(ns)); |
588 | mutex_unlock(&sem_ids(ns).mutex); | 610 | mutex_unlock(&sem_ids(ns).mutex); |
589 | if (copy_to_user (arg.__buf, &seminfo, sizeof(struct seminfo))) | 611 | if (copy_to_user (arg.__buf, &seminfo, sizeof(struct seminfo))) |
590 | return -EFAULT; | 612 | return -EFAULT; |
@@ -595,9 +617,6 @@ static int semctl_nolock(struct ipc_namespace *ns, int semid, int semnum, | |||
595 | struct semid64_ds tbuf; | 617 | struct semid64_ds tbuf; |
596 | int id; | 618 | int id; |
597 | 619 | ||
598 | if(semid >= sem_ids(ns).entries->size) | ||
599 | return -EINVAL; | ||
600 | |||
601 | memset(&tbuf,0,sizeof(tbuf)); | 620 | memset(&tbuf,0,sizeof(tbuf)); |
602 | 621 | ||
603 | sma = sem_lock(ns, semid); | 622 | sma = sem_lock(ns, semid); |
@@ -612,7 +631,7 @@ static int semctl_nolock(struct ipc_namespace *ns, int semid, int semnum, | |||
612 | if (err) | 631 | if (err) |
613 | goto out_unlock; | 632 | goto out_unlock; |
614 | 633 | ||
615 | id = sem_buildid(ns, semid, sma->sem_perm.seq); | 634 | id = sma->sem_perm.id; |
616 | 635 | ||
617 | kernel_to_ipc64_perm(&sma->sem_perm, &tbuf.sem_perm); | 636 | kernel_to_ipc64_perm(&sma->sem_perm, &tbuf.sem_perm); |
618 | tbuf.sem_otime = sma->sem_otime; | 637 | tbuf.sem_otime = sma->sem_otime; |
@@ -894,7 +913,7 @@ static int semctl_down(struct ipc_namespace *ns, int semid, int semnum, | |||
894 | 913 | ||
895 | switch(cmd){ | 914 | switch(cmd){ |
896 | case IPC_RMID: | 915 | case IPC_RMID: |
897 | freeary(ns, sma, semid); | 916 | freeary(ns, sma); |
898 | err = 0; | 917 | err = 0; |
899 | break; | 918 | break; |
900 | case IPC_SET: | 919 | case IPC_SET: |
@@ -1402,7 +1421,7 @@ static int sysvipc_sem_proc_show(struct seq_file *s, void *it) | |||
1402 | return seq_printf(s, | 1421 | return seq_printf(s, |
1403 | "%10d %10d %4o %10lu %5u %5u %5u %5u %10lu %10lu\n", | 1422 | "%10d %10d %4o %10lu %5u %5u %5u %5u %10lu %10lu\n", |
1404 | sma->sem_perm.key, | 1423 | sma->sem_perm.key, |
1405 | sma->sem_id, | 1424 | sma->sem_perm.id, |
1406 | sma->sem_perm.mode, | 1425 | sma->sem_perm.mode, |
1407 | sma->sem_nsems, | 1426 | sma->sem_nsems, |
1408 | sma->sem_perm.uid, | 1427 | sma->sem_perm.uid, |