aboutsummaryrefslogtreecommitdiffstats
path: root/ipc
diff options
context:
space:
mode:
authorManfred Spraul <manfred@colorfullife.com>2008-07-25 04:48:06 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2008-07-25 13:53:42 -0400
commit380af1b33b3ff92df5cda96329b58f5d1b6b5a53 (patch)
tree9a47d66c18e4aae2093a708a7509c0f188ee0bd1 /ipc
parenta1193f8ec091cd8fd309cc2982abe4499f6f2b4d (diff)
ipc/sem.c: rewrite undo list locking
The attached patch: - reverses the locking order of ulp->lock and sem_lock: Previously, it was first ulp->lock, then inside sem_lock. Now it's the other way around. - converts the undo structure to rcu. Benefits: - With the old locking order, IPC_RMID could not kfree the undo structures. The stale entries remained in the linked lists and were released later. - The patch fixes a a race in semtimedop(): if both IPC_RMID and a semget() that recreates exactly the same id happen between find_alloc_undo() and sem_lock, then semtimedop() would access already kfree'd memory. [akpm@linux-foundation.org: coding-style fixes] Signed-off-by: Manfred Spraul <manfred@colorfullife.com> Reviewed-by: Nadia Derbey <Nadia.Derbey@bull.net> Cc: Pierre Peiffer <peifferp@gmail.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/sem.c147
1 files changed, 93 insertions, 54 deletions
diff --git a/ipc/sem.c b/ipc/sem.c
index 3ca232736b31..bf1bc36cb7ee 100644
--- a/ipc/sem.c
+++ b/ipc/sem.c
@@ -504,27 +504,35 @@ static int count_semzcnt (struct sem_array * sma, ushort semnum)
504 return semzcnt; 504 return semzcnt;
505} 505}
506 506
507void free_un(struct rcu_head *head)
508{
509 struct sem_undo *un = container_of(head, struct sem_undo, rcu);
510 kfree(un);
511}
512
507/* Free a semaphore set. freeary() is called with sem_ids.rw_mutex locked 513/* Free a semaphore set. freeary() is called with sem_ids.rw_mutex locked
508 * as a writer and the spinlock for this semaphore set hold. sem_ids.rw_mutex 514 * as a writer and the spinlock for this semaphore set hold. sem_ids.rw_mutex
509 * remains locked on exit. 515 * remains locked on exit.
510 */ 516 */
511static void freeary(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp) 517static void freeary(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp)
512{ 518{
513 struct sem_undo *un; 519 struct sem_undo *un, *tu;
514 struct sem_queue *q, *t; 520 struct sem_queue *q, *tq;
515 struct sem_array *sma = container_of(ipcp, struct sem_array, sem_perm); 521 struct sem_array *sma = container_of(ipcp, struct sem_array, sem_perm);
516 522
517 /* Invalidate the existing undo structures for this semaphore set. 523 /* Free the existing undo structures for this semaphore set. */
518 * (They will be freed without any further action in exit_sem()
519 * or during the next semop.)
520 */
521 assert_spin_locked(&sma->sem_perm.lock); 524 assert_spin_locked(&sma->sem_perm.lock);
522 list_for_each_entry(un, &sma->list_id, list_id) 525 list_for_each_entry_safe(un, tu, &sma->list_id, list_id) {
526 list_del(&un->list_id);
527 spin_lock(&un->ulp->lock);
523 un->semid = -1; 528 un->semid = -1;
529 list_del_rcu(&un->list_proc);
530 spin_unlock(&un->ulp->lock);
531 call_rcu(&un->rcu, free_un);
532 }
524 533
525 /* Wake up all pending processes and let them fail with EIDRM. */ 534 /* Wake up all pending processes and let them fail with EIDRM. */
526 535 list_for_each_entry_safe(q, tq, &sma->sem_pending, list) {
527 list_for_each_entry_safe(q, t, &sma->sem_pending, list) {
528 list_del(&q->list); 536 list_del(&q->list);
529 537
530 q->status = IN_WAKEUP; 538 q->status = IN_WAKEUP;
@@ -948,16 +956,11 @@ static inline int get_undo_list(struct sem_undo_list **undo_listp)
948 956
949static struct sem_undo *lookup_undo(struct sem_undo_list *ulp, int semid) 957static struct sem_undo *lookup_undo(struct sem_undo_list *ulp, int semid)
950{ 958{
951 struct sem_undo *walk, *tmp; 959 struct sem_undo *walk;
952 960
953 assert_spin_locked(&ulp->lock); 961 list_for_each_entry_rcu(walk, &ulp->list_proc, list_proc) {
954 list_for_each_entry_safe(walk, tmp, &ulp->list_proc, list_proc) {
955 if (walk->semid == semid) 962 if (walk->semid == semid)
956 return walk; 963 return walk;
957 if (walk->semid == -1) {
958 list_del(&walk->list_proc);
959 kfree(walk);
960 }
961 } 964 }
962 return NULL; 965 return NULL;
963} 966}
@@ -970,6 +973,8 @@ static struct sem_undo *lookup_undo(struct sem_undo_list *ulp, int semid)
970 * The function looks up (and if not present creates) the undo structure. 973 * The function looks up (and if not present creates) the undo structure.
971 * The size of the undo structure depends on the size of the semaphore 974 * The size of the undo structure depends on the size of the semaphore
972 * array, thus the alloc path is not that straightforward. 975 * array, thus the alloc path is not that straightforward.
976 * Lifetime-rules: sem_undo is rcu-protected, on success, the function
977 * performs a rcu_read_lock().
973 */ 978 */
974static struct sem_undo *find_alloc_undo(struct ipc_namespace *ns, int semid) 979static struct sem_undo *find_alloc_undo(struct ipc_namespace *ns, int semid)
975{ 980{
@@ -983,11 +988,13 @@ static struct sem_undo *find_alloc_undo(struct ipc_namespace *ns, int semid)
983 if (error) 988 if (error)
984 return ERR_PTR(error); 989 return ERR_PTR(error);
985 990
991 rcu_read_lock();
986 spin_lock(&ulp->lock); 992 spin_lock(&ulp->lock);
987 un = lookup_undo(ulp, semid); 993 un = lookup_undo(ulp, semid);
988 spin_unlock(&ulp->lock); 994 spin_unlock(&ulp->lock);
989 if (likely(un!=NULL)) 995 if (likely(un!=NULL))
990 goto out; 996 goto out;
997 rcu_read_unlock();
991 998
992 /* no undo structure around - allocate one. */ 999 /* no undo structure around - allocate one. */
993 /* step 1: figure out the size of the semaphore array */ 1000 /* step 1: figure out the size of the semaphore array */
@@ -1005,38 +1012,38 @@ static struct sem_undo *find_alloc_undo(struct ipc_namespace *ns, int semid)
1005 return ERR_PTR(-ENOMEM); 1012 return ERR_PTR(-ENOMEM);
1006 } 1013 }
1007 1014
1008 /* step 3: Acquire the lock on the undo list pointer */ 1015 /* step 3: Acquire the lock on semaphore array */
1009 spin_lock(&ulp->lock);
1010
1011 /* step 4: check for races: someone else allocated the undo struct,
1012 * semaphore array was destroyed.
1013 */
1014 un = lookup_undo(ulp, semid);
1015 if (un) {
1016 spin_unlock(&ulp->lock);
1017 kfree(new);
1018 sem_putref(sma);
1019 goto out;
1020 }
1021 sem_lock_and_putref(sma); 1016 sem_lock_and_putref(sma);
1022 if (sma->sem_perm.deleted) { 1017 if (sma->sem_perm.deleted) {
1023 sem_unlock(sma); 1018 sem_unlock(sma);
1024 spin_unlock(&ulp->lock);
1025 kfree(new); 1019 kfree(new);
1026 un = ERR_PTR(-EIDRM); 1020 un = ERR_PTR(-EIDRM);
1027 goto out; 1021 goto out;
1028 } 1022 }
1023 spin_lock(&ulp->lock);
1024
1025 /*
1026 * step 4: check for races: did someone else allocate the undo struct?
1027 */
1028 un = lookup_undo(ulp, semid);
1029 if (un) {
1030 kfree(new);
1031 goto success;
1032 }
1029 /* step 5: initialize & link new undo structure */ 1033 /* step 5: initialize & link new undo structure */
1030 new->semadj = (short *) &new[1]; 1034 new->semadj = (short *) &new[1];
1035 new->ulp = ulp;
1031 new->semid = semid; 1036 new->semid = semid;
1032 assert_spin_locked(&ulp->lock); 1037 assert_spin_locked(&ulp->lock);
1033 list_add(&new->list_proc, &ulp->list_proc); 1038 list_add_rcu(&new->list_proc, &ulp->list_proc);
1034 assert_spin_locked(&sma->sem_perm.lock); 1039 assert_spin_locked(&sma->sem_perm.lock);
1035 list_add(&new->list_id, &sma->list_id); 1040 list_add(&new->list_id, &sma->list_id);
1041 un = new;
1036 1042
1037 sem_unlock(sma); 1043success:
1038 spin_unlock(&ulp->lock); 1044 spin_unlock(&ulp->lock);
1039 un = new; 1045 rcu_read_lock();
1046 sem_unlock(sma);
1040out: 1047out:
1041 return un; 1048 return un;
1042} 1049}
@@ -1103,6 +1110,8 @@ asmlinkage long sys_semtimedop(int semid, struct sembuf __user *tsops,
1103 1110
1104 sma = sem_lock_check(ns, semid); 1111 sma = sem_lock_check(ns, semid);
1105 if (IS_ERR(sma)) { 1112 if (IS_ERR(sma)) {
1113 if (un)
1114 rcu_read_unlock();
1106 error = PTR_ERR(sma); 1115 error = PTR_ERR(sma);
1107 goto out_free; 1116 goto out_free;
1108 } 1117 }
@@ -1111,10 +1120,26 @@ asmlinkage long sys_semtimedop(int semid, struct sembuf __user *tsops,
1111 * semid identifiers are not unique - find_alloc_undo may have 1120 * semid identifiers are not unique - find_alloc_undo may have
1112 * allocated an undo structure, it was invalidated by an RMID 1121 * allocated an undo structure, it was invalidated by an RMID
1113 * and now a new array with received the same id. Check and fail. 1122 * and now a new array with received the same id. Check and fail.
1123 * This case can be detected checking un->semid. The existance of
1124 * "un" itself is guaranteed by rcu.
1114 */ 1125 */
1115 error = -EIDRM; 1126 error = -EIDRM;
1116 if (un && un->semid == -1) 1127 if (un) {
1117 goto out_unlock_free; 1128 if (un->semid == -1) {
1129 rcu_read_unlock();
1130 goto out_unlock_free;
1131 } else {
1132 /*
1133 * rcu lock can be released, "un" cannot disappear:
1134 * - sem_lock is acquir