aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/linux/sem.h12
-rw-r--r--ipc/sem.c163
2 files changed, 95 insertions, 80 deletions
diff --git a/include/linux/sem.h b/include/linux/sem.h
index c8eaad9e4b72..6a1af1b49a13 100644
--- a/include/linux/sem.h
+++ b/include/linux/sem.h
@@ -95,7 +95,7 @@ struct sem_array {
95 struct sem *sem_base; /* ptr to first semaphore in array */ 95 struct sem *sem_base; /* ptr to first semaphore in array */
96 struct sem_queue *sem_pending; /* pending operations to be processed */ 96 struct sem_queue *sem_pending; /* pending operations to be processed */
97 struct sem_queue **sem_pending_last; /* last pending operation */ 97 struct sem_queue **sem_pending_last; /* last pending operation */
98 struct sem_undo *undo; /* undo requests on this array */ 98 struct list_head list_id; /* undo requests on this array */
99 unsigned long sem_nsems; /* no. of semaphores in array */ 99 unsigned long sem_nsems; /* no. of semaphores in array */
100}; 100};
101 101
@@ -118,8 +118,8 @@ struct sem_queue {
118 * when the process exits. 118 * when the process exits.
119 */ 119 */
120struct sem_undo { 120struct sem_undo {
121 struct sem_undo * proc_next; /* next entry on this process */ 121 struct list_head list_proc; /* per-process list: all undos from one process */
122 struct sem_undo * id_next; /* next entry on this semaphore set */ 122 struct list_head list_id; /* per semaphore array list: all undos for one array */
123 int semid; /* semaphore set identifier */ 123 int semid; /* semaphore set identifier */
124 short * semadj; /* array of adjustments, one per semaphore */ 124 short * semadj; /* array of adjustments, one per semaphore */
125}; 125};
@@ -128,9 +128,9 @@ struct sem_undo {
128 * that may be shared among all a CLONE_SYSVSEM task group. 128 * that may be shared among all a CLONE_SYSVSEM task group.
129 */ 129 */
130struct sem_undo_list { 130struct sem_undo_list {
131 atomic_t refcnt; 131 atomic_t refcnt;
132 spinlock_t lock; 132 spinlock_t lock;
133 struct sem_undo *proc_list; 133 struct list_head list_proc;
134}; 134};
135 135
136struct sysv_sem { 136struct sysv_sem {
diff --git a/ipc/sem.c b/ipc/sem.c
index e9418df5ff3e..4f26c7157356 100644
--- a/ipc/sem.c
+++ b/ipc/sem.c
@@ -274,7 +274,7 @@ static int newary(struct ipc_namespace *ns, struct ipc_params *params)
274 sma->sem_base = (struct sem *) &sma[1]; 274 sma->sem_base = (struct sem *) &sma[1];
275 /* sma->sem_pending = NULL; */ 275 /* sma->sem_pending = NULL; */
276 sma->sem_pending_last = &sma->sem_pending; 276 sma->sem_pending_last = &sma->sem_pending;
277 /* sma->undo = NULL; */ 277 INIT_LIST_HEAD(&sma->list_id);
278 sma->sem_nsems = nsems; 278 sma->sem_nsems = nsems;
279 sma->sem_ctime = get_seconds(); 279 sma->sem_ctime = get_seconds();
280 sem_unlock(sma); 280 sem_unlock(sma);
@@ -536,7 +536,8 @@ static void freeary(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp)
536 * (They will be freed without any further action in exit_sem() 536 * (They will be freed without any further action in exit_sem()
537 * or during the next semop.) 537 * or during the next semop.)
538 */ 538 */
539 for (un = sma->undo; un; un = un->id_next) 539 assert_spin_locked(&sma->sem_perm.lock);
540 list_for_each_entry(un, &sma->list_id, list_id)
540 un->semid = -1; 541 un->semid = -1;
541 542
542 /* Wake up all pending processes and let them fail with EIDRM. */ 543 /* Wake up all pending processes and let them fail with EIDRM. */
@@ -763,9 +764,12 @@ static int semctl_main(struct ipc_namespace *ns, int semid, int semnum,
763 764
764 for (i = 0; i < nsems; i++) 765 for (i = 0; i < nsems; i++)
765 sma->sem_base[i].semval = sem_io[i]; 766 sma->sem_base[i].semval = sem_io[i];
766 for (un = sma->undo; un; un = un->id_next) 767
768 assert_spin_locked(&sma->sem_perm.lock);
769 list_for_each_entry(un, &sma->list_id, list_id) {
767 for (i = 0; i < nsems; i++) 770 for (i = 0; i < nsems; i++)
768 un->semadj[i] = 0; 771 un->semadj[i] = 0;
772 }
769 sma->sem_ctime = get_seconds(); 773 sma->sem_ctime = get_seconds();
770 /* maybe some queued-up processes were waiting for this */ 774 /* maybe some queued-up processes were waiting for this */
771 update_queue(sma); 775 update_queue(sma);
@@ -797,12 +801,15 @@ static int semctl_main(struct ipc_namespace *ns, int semid, int semnum,
797 { 801 {
798 int val = arg.val; 802 int val = arg.val;
799 struct sem_undo *un; 803 struct sem_undo *un;
804
800 err = -ERANGE; 805 err = -ERANGE;
801 if (val > SEMVMX || val < 0) 806 if (val > SEMVMX || val < 0)
802 goto out_unlock; 807 goto out_unlock;
803 808
804 for (un = sma->undo; un; un = un->id_next) 809 assert_spin_locked(&sma->sem_perm.lock);
810 list_for_each_entry(un, &sma->list_id, list_id)
805 un->semadj[semnum] = 0; 811 un->semadj[semnum] = 0;
812
806 curr->semval = val; 813 curr->semval = val;
807 curr->sempid = task_tgid_vnr(current); 814 curr->sempid = task_tgid_vnr(current);
808 sma->sem_ctime = get_seconds(); 815 sma->sem_ctime = get_seconds();
@@ -952,6 +959,8 @@ static inline int get_undo_list(struct sem_undo_list **undo_listp)
952 return -ENOMEM; 959 return -ENOMEM;
953 spin_lock_init(&undo_list->lock); 960 spin_lock_init(&undo_list->lock);
954 atomic_set(&undo_list->refcnt, 1); 961 atomic_set(&undo_list->refcnt, 1);
962 INIT_LIST_HEAD(&undo_list->list_proc);
963
955 current->sysvsem.undo_list = undo_list; 964 current->sysvsem.undo_list = undo_list;
956 } 965 }
957 *undo_listp = undo_list; 966 *undo_listp = undo_list;
@@ -960,25 +969,30 @@ static inline int get_undo_list(struct sem_undo_list **undo_listp)
960 969
961static struct sem_undo *lookup_undo(struct sem_undo_list *ulp, int semid) 970static struct sem_undo *lookup_undo(struct sem_undo_list *ulp, int semid)
962{ 971{
963 struct sem_undo **last, *un; 972 struct sem_undo *walk, *tmp;
964 973
965 last = &ulp->proc_list; 974 assert_spin_locked(&ulp->lock);
966 un = *last; 975 list_for_each_entry_safe(walk, tmp, &ulp->list_proc, list_proc) {
967 while(un != NULL) { 976 if (walk->semid == semid)
968 if(un->semid==semid) 977 return walk;
969 break; 978 if (walk->semid == -1) {
970 if(un->semid==-1) { 979 list_del(&walk->list_proc);
971 *last=un->proc_next; 980 kfree(walk);
972 kfree(un);
973 } else {
974 last=&un->proc_next;
975 } 981 }
976 un=*last;
977 } 982 }
978 return un; 983 return NULL;
979} 984}
980 985
981static struct sem_undo *find_undo(struct ipc_namespace *ns, int semid) 986/**
987 * find_alloc_undo - Lookup (and if not present create) undo array
988 * @ns: namespace
989 * @semid: semaphore array id
990 *
991 * The function looks up (and if not present creates) the undo structure.
992 * The size of the undo structure depends on the size of the semaphore
993 * array, thus the alloc path is not that straightforward.
994 */
995static struct sem_undo *find_alloc_undo(struct ipc_namespace *ns, int semid)
982{ 996{
983 struct sem_array *sma; 997 struct sem_array *sma;
984 struct sem_undo_list *ulp; 998 struct sem_undo_list *ulp;
@@ -997,6 +1011,7 @@ static struct sem_undo *find_undo(struct ipc_namespace *ns, int semid)
997 goto out; 1011 goto out;
998 1012
999 /* no undo structure around - allocate one. */ 1013 /* no undo structure around - allocate one. */
1014 /* step 1: figure out the size of the semaphore array */
1000 sma = sem_lock_check(ns, semid); 1015 sma = sem_lock_check(ns, semid);
1001 if (IS_ERR(sma)) 1016 if (IS_ERR(sma))
1002 return ERR_PTR(PTR_ERR(sma)); 1017 return ERR_PTR(PTR_ERR(sma));
@@ -1004,15 +1019,19 @@ static struct sem_undo *find_undo(struct ipc_namespace *ns, int semid)
1004 nsems = sma->sem_nsems; 1019 nsems = sma->sem_nsems;
1005 sem_getref_and_unlock(sma); 1020 sem_getref_and_unlock(sma);
1006 1021
1022 /* step 2: allocate new undo structure */
1007 new = kzalloc(sizeof(struct sem_undo) + sizeof(short)*nsems, GFP_KERNEL); 1023 new = kzalloc(sizeof(struct sem_undo) + sizeof(short)*nsems, GFP_KERNEL);
1008 if (!new) { 1024 if (!new) {
1009 sem_putref(sma); 1025 sem_putref(sma);
1010 return ERR_PTR(-ENOMEM); 1026 return ERR_PTR(-ENOMEM);
1011 } 1027 }
1012 new->semadj = (short *) &new[1];
1013 new->semid = semid;
1014 1028
1029 /* step 3: Acquire the lock on the undo list pointer */
1015 spin_lock(&ulp->lock); 1030 spin_lock(&ulp->lock);
1031
1032 /* step 4: check for races: someone else allocated the undo struct,
1033 * semaphore array was destroyed.
1034 */
1016 un = lookup_undo(ulp, semid); 1035 un = lookup_undo(ulp, semid);
1017 if (un) { 1036 if (un) {
1018 spin_unlock(&ulp->lock);