aboutsummaryrefslogtreecommitdiffstats
path: root/ipc/sem.c
diff options
context:
space:
mode:
Diffstat (limited to 'ipc/sem.c')
-rw-r--r--ipc/sem.c159
1 files changed, 56 insertions, 103 deletions
diff --git a/ipc/sem.c b/ipc/sem.c
index 0b45a4d383c6..e9418df5ff3e 100644
--- a/ipc/sem.c
+++ b/ipc/sem.c
@@ -91,7 +91,6 @@
91 91
92#define sem_unlock(sma) ipc_unlock(&(sma)->sem_perm) 92#define sem_unlock(sma) ipc_unlock(&(sma)->sem_perm)
93#define sem_checkid(sma, semid) ipc_checkid(&sma->sem_perm, semid) 93#define sem_checkid(sma, semid) ipc_checkid(&sma->sem_perm, semid)
94#define sem_buildid(id, seq) ipc_buildid(id, seq)
95 94
96static int newary(struct ipc_namespace *, struct ipc_params *); 95static int newary(struct ipc_namespace *, struct ipc_params *);
97static void freeary(struct ipc_namespace *, struct kern_ipc_perm *); 96static void freeary(struct ipc_namespace *, struct kern_ipc_perm *);
@@ -142,21 +141,6 @@ void __init sem_init (void)
142} 141}
143 142
144/* 143/*
145 * This routine is called in the paths where the rw_mutex is held to protect
146 * access to the idr tree.
147 */
148static inline struct sem_array *sem_lock_check_down(struct ipc_namespace *ns,
149 int id)
150{
151 struct kern_ipc_perm *ipcp = ipc_lock_check_down(&sem_ids(ns), id);
152
153 if (IS_ERR(ipcp))
154 return (struct sem_array *)ipcp;
155
156 return container_of(ipcp, struct sem_array, sem_perm);
157}
158
159/*
160 * sem_lock_(check_) routines are called in the paths where the rw_mutex 144 * sem_lock_(check_) routines are called in the paths where the rw_mutex
161 * is not held. 145 * is not held.
162 */ 146 */
@@ -181,6 +165,25 @@ static inline struct sem_array *sem_lock_check(struct ipc_namespace *ns,
181 return container_of(ipcp, struct sem_array, sem_perm); 165 return container_of(ipcp, struct sem_array, sem_perm);
182} 166}
183 167
168static inline void sem_lock_and_putref(struct sem_array *sma)
169{
170 ipc_lock_by_ptr(&sma->sem_perm);
171 ipc_rcu_putref(sma);
172}
173
174static inline void sem_getref_and_unlock(struct sem_array *sma)
175{
176 ipc_rcu_getref(sma);
177 ipc_unlock(&(sma)->sem_perm);
178}
179
180static inline void sem_putref(struct sem_array *sma)
181{
182 ipc_lock_by_ptr(&sma->sem_perm);
183 ipc_rcu_putref(sma);
184 ipc_unlock(&(sma)->sem_perm);
185}
186
184static inline void sem_rmid(struct ipc_namespace *ns, struct sem_array *s) 187static inline void sem_rmid(struct ipc_namespace *ns, struct sem_array *s)
185{ 188{
186 ipc_rmid(&sem_ids(ns), &s->sem_perm); 189 ipc_rmid(&sem_ids(ns), &s->sem_perm);
@@ -268,7 +271,6 @@ static int newary(struct ipc_namespace *ns, struct ipc_params *params)
268 } 271 }
269 ns->used_sems += nsems; 272 ns->used_sems += nsems;
270 273
271 sma->sem_perm.id = sem_buildid(id, sma->sem_perm.seq);
272 sma->sem_base = (struct sem *) &sma[1]; 274 sma->sem_base = (struct sem *) &sma[1];
273 /* sma->sem_pending = NULL; */ 275 /* sma->sem_pending = NULL; */
274 sma->sem_pending_last = &sma->sem_pending; 276 sma->sem_pending_last = &sma->sem_pending;
@@ -700,19 +702,15 @@ static int semctl_main(struct ipc_namespace *ns, int semid, int semnum,
700 int i; 702 int i;
701 703
702 if(nsems > SEMMSL_FAST) { 704 if(nsems > SEMMSL_FAST) {
703 ipc_rcu_getref(sma); 705 sem_getref_and_unlock(sma);
704 sem_unlock(sma);
705 706
706 sem_io = ipc_alloc(sizeof(ushort)*nsems); 707 sem_io = ipc_alloc(sizeof(ushort)*nsems);
707 if(sem_io == NULL) { 708 if(sem_io == NULL) {
708 ipc_lock_by_ptr(&sma->sem_perm); 709 sem_putref(sma);
709 ipc_rcu_putref(sma);
710 sem_unlock(sma);
711 return -ENOMEM; 710 return -ENOMEM;
712 } 711 }
713 712
714 ipc_lock_by_ptr(&sma->sem_perm); 713 sem_lock_and_putref(sma);
715 ipc_rcu_putref(sma);
716 if (sma->sem_perm.deleted) { 714 if (sma->sem_perm.deleted) {
717 sem_unlock(sma); 715 sem_unlock(sma);
718 err = -EIDRM; 716 err = -EIDRM;
@@ -733,38 +731,30 @@ static int semctl_main(struct ipc_namespace *ns, int semid, int semnum,
733 int i; 731 int i;
734 struct sem_undo *un; 732 struct sem_undo *un;
735 733
736 ipc_rcu_getref(sma); 734 sem_getref_and_unlock(sma);
737 sem_unlock(sma);
738 735
739 if(nsems > SEMMSL_FAST) { 736 if(nsems > SEMMSL_FAST) {
740 sem_io = ipc_alloc(sizeof(ushort)*nsems); 737 sem_io = ipc_alloc(sizeof(ushort)*nsems);
741 if(sem_io == NULL) { 738 if(sem_io == NULL) {
742 ipc_lock_by_ptr(&sma->sem_perm); 739 sem_putref(sma);
743 ipc_rcu_putref(sma);
744 sem_unlock(sma);
745 return -ENOMEM; 740 return -ENOMEM;
746 } 741 }
747 } 742 }
748 743
749 if (copy_from_user (sem_io, arg.array, nsems*sizeof(ushort))) { 744 if (copy_from_user (sem_io, arg.array, nsems*sizeof(ushort))) {
750 ipc_lock_by_ptr(&sma->sem_perm); 745 sem_putref(sma);
751 ipc_rcu_putref(sma);
752 sem_unlock(sma);
753 err = -EFAULT; 746 err = -EFAULT;
754 goto out_free; 747 goto out_free;
755 } 748 }
756 749
757 for (i = 0; i < nsems; i++) { 750 for (i = 0; i < nsems; i++) {
758 if (sem_io[i] > SEMVMX) { 751 if (sem_io[i] > SEMVMX) {
759 ipc_lock_by_ptr(&sma->sem_perm); 752 sem_putref(sma);
760 ipc_rcu_putref(sma);
761 sem_unlock(sma);
762 err = -ERANGE; 753 err = -ERANGE;
763 goto out_free; 754 goto out_free;
764 } 755 }
765 } 756 }
766 ipc_lock_by_ptr(&sma->sem_perm); 757 sem_lock_and_putref(sma);
767 ipc_rcu_putref(sma);
768 if (sma->sem_perm.deleted) { 758 if (sma->sem_perm.deleted) {
769 sem_unlock(sma); 759 sem_unlock(sma);
770 err = -EIDRM; 760 err = -EIDRM;
@@ -830,28 +820,14 @@ out_free:
830 return err; 820 return err;
831} 821}
832 822
833struct sem_setbuf { 823static inline unsigned long
834 uid_t uid; 824copy_semid_from_user(struct semid64_ds *out, void __user *buf, int version)
835 gid_t gid;
836 mode_t mode;
837};
838
839static inline unsigned long copy_semid_from_user(struct sem_setbuf *out, void __user *buf, int version)
840{ 825{
841 switch(version) { 826 switch(version) {
842 case IPC_64: 827 case IPC_64:
843 { 828 if (copy_from_user(out, buf, sizeof(*out)))
844 struct semid64_ds tbuf;
845
846 if(copy_from_user(&tbuf, buf, sizeof(tbuf)))
847 return -EFAULT; 829 return -EFAULT;
848
849 out->uid = tbuf.sem_perm.uid;
850 out->gid = tbuf.sem_perm.gid;
851 out->mode = tbuf.sem_perm.mode;
852
853 return 0; 830 return 0;
854 }
855 case IPC_OLD: 831 case IPC_OLD:
856 { 832 {
857 struct semid_ds tbuf_old; 833 struct semid_ds tbuf_old;
@@ -859,9 +835,9 @@ static inline unsigned long copy_semid_from_user(struct sem_setbuf *out, void __
859 if(copy_from_user(&tbuf_old, buf, sizeof(tbuf_old))) 835 if(copy_from_user(&tbuf_old, buf, sizeof(tbuf_old)))
860 return -EFAULT; 836 return -EFAULT;
861 837
862 out->uid = tbuf_old.sem_perm.uid; 838 out->sem_perm.uid = tbuf_old.sem_perm.uid;
863 out->gid = tbuf_old.sem_perm.gid; 839 out->sem_perm.gid = tbuf_old.sem_perm.gid;
864 out->mode = tbuf_old.sem_perm.mode; 840 out->sem_perm.mode = tbuf_old.sem_perm.mode;
865 841
866 return 0; 842 return 0;
867 } 843 }
@@ -870,38 +846,29 @@ static inline unsigned long copy_semid_from_user(struct sem_setbuf *out, void __
870 } 846 }
871} 847}
872 848
873static int semctl_down(struct ipc_namespace *ns, int semid, int semnum, 849/*
874 int cmd, int version, union semun arg) 850 * This function handles some semctl commands which require the rw_mutex
851 * to be held in write mode.
852 * NOTE: no locks must be held, the rw_mutex is taken inside this function.
853 */
854static int semctl_down(struct ipc_namespace *ns, int semid,
855 int cmd, int version, union semun arg)
875{ 856{
876 struct sem_array *sma; 857 struct sem_array *sma;
877 int err; 858 int err;
878 struct sem_setbuf uninitialized_var(setbuf); 859 struct semid64_ds semid64;
879 struct kern_ipc_perm *ipcp; 860 struct kern_ipc_perm *ipcp;
880 861
881 if(cmd == IPC_SET) { 862 if(cmd == IPC_SET) {
882 if(copy_semid_from_user (&setbuf, arg.buf, version)) 863 if (copy_semid_from_user(&semid64, arg.buf, version))
883 return -EFAULT; 864 return -EFAULT;
884 } 865 }
885 sma = sem_lock_check_down(ns, semid);
886 if (IS_ERR(sma))
887 return PTR_ERR(sma);
888 866
889 ipcp = &sma->sem_perm; 867 ipcp = ipcctl_pre_down(&sem_ids(ns), semid, cmd, &semid64.sem_perm, 0);
890 868 if (IS_ERR(ipcp))
891 err = audit_ipc_obj(ipcp); 869 return PTR_ERR(ipcp);
892 if (err)
893 goto out_unlock;
894 870
895 if (cmd == IPC_SET) { 871 sma = container_of(ipcp, struct sem_array, sem_perm);
896 err = audit_ipc_set_perm(0, setbuf.uid, setbuf.gid, setbuf.mode);
897 if (err)
898 goto out_unlock;
899 }
900 if (current->euid != ipcp->cuid &&
901 current->euid != ipcp->uid && !capable(CAP_SYS_ADMIN)) {
902 err=-EPERM;
903 goto out_unlock;
904 }
905 872
906 err = security_sem_semctl(sma, cmd); 873 err = security_sem_semctl(sma, cmd);
907 if (err) 874 if (err)
@@ -910,26 +877,19 @@ static int semctl_down(struct ipc_namespace *ns, int semid, int semnum,
910 switch(cmd){ 877 switch(cmd){
911 case IPC_RMID: 878 case IPC_RMID:
912 freeary(ns, ipcp); 879 freeary(ns, ipcp);
913 err = 0; 880 goto out_up;
914 break;
915 case IPC_SET: 881 case IPC_SET:
916 ipcp->uid = setbuf.uid; 882 ipc_update_perm(&semid64.sem_perm, ipcp);
917 ipcp->gid = setbuf.gid;
918 ipcp->mode = (ipcp->mode & ~S_IRWXUGO)
919 | (setbuf.mode & S_IRWXUGO);
920 sma->sem_ctime = get_seconds(); 883 sma->sem_ctime = get_seconds();
921 sem_unlock(sma);
922 err = 0;
923 break; 884 break;
924 default: 885 default:
925 sem_unlock(sma);
926 err = -EINVAL; 886 err = -EINVAL;
927 break;
928 } 887 }
929 return err;
930 888
931out_unlock: 889out_unlock:
932 sem_unlock(sma); 890 sem_unlock(sma);
891out_up:
892 up_write(&sem_ids(ns).rw_mutex);
933 return err; 893 return err;
934} 894}
935 895
@@ -963,9 +923,7 @@ asmlinkage long sys_semctl (int semid, int semnum, int cmd, union semun arg)
963 return err; 923 return err;
964 case IPC_RMID: 924 case IPC_RMID:
965 case IPC_SET: 925 case IPC_SET:
966 down_write(&sem_ids(ns).rw_mutex); 926 err = semctl_down(ns, semid, cmd, version, arg);
967 err = semctl_down(ns,semid,semnum,cmd,version,arg);
968 up_write(&sem_ids(ns).rw_mutex);
969 return err; 927 return err;
970 default: 928 default:
971 return -EINVAL; 929 return -EINVAL;
@@ -1044,14 +1002,11 @@ static struct sem_undo *find_undo(struct ipc_namespace *ns, int semid)
1044 return ERR_PTR(PTR_ERR(sma)); 1002 return ERR_PTR(PTR_ERR(sma));
1045 1003
1046 nsems = sma->sem_nsems; 1004 nsems = sma->sem_nsems;
1047 ipc_rcu_getref(sma); 1005 sem_getref_and_unlock(sma);
1048 sem_unlock(sma);
1049 1006
1050 new = kzalloc(sizeof(struct sem_undo) + sizeof(short)*nsems, GFP_KERNEL); 1007 new = kzalloc(sizeof(struct sem_undo) + sizeof(short)*nsems, GFP_KERNEL);
1051 if (!new) { 1008 if (!new) {
1052 ipc_lock_by_ptr(&sma->sem_perm); 1009 sem_putref(sma);
1053 ipc_rcu_putref(sma);
1054 sem_unlock(sma);
1055 return ERR_PTR(-ENOMEM); 1010 return ERR_PTR(-ENOMEM);
1056 } 1011 }
1057 new->semadj = (short *) &new[1]; 1012 new->semadj = (short *) &new[1];
@@ -1062,13 +1017,10 @@ static struct sem_undo *find_undo(struct ipc_namespace *ns, int semid)
1062 if (un) { 1017 if (un) {
1063 spin_unlock(&ulp->lock); 1018 spin_unlock(&ulp->lock);
1064 kfree(new); 1019 kfree(new);
1065 ipc_lock_by_ptr(&sma->sem_perm); 1020 sem_putref(sma);
1066 ipc_rcu_putref(sma);
1067 sem_unlock(sma);
1068 goto out; 1021 goto out;
1069 } 1022 }
1070 ipc_lock_by_ptr(&sma->sem_perm); 1023 sem_lock_and_putref(sma);
1071 ipc_rcu_putref(sma);
1072 if (sma->sem_perm.deleted) { 1024 if (sma->sem_perm.deleted) {
1073 sem_unlock(sma); 1025 sem_unlock(sma);
1074 spin_unlock(&ulp->lock); 1026 spin_unlock(&ulp->lock);
@@ -1298,6 +1250,7 @@ void exit_sem(struct task_struct *tsk)
1298 undo_list = tsk->sysvsem.undo_list; 1250 undo_list = tsk->sysvsem.undo_list;
1299 if (!undo_list) 1251 if (!undo_list)
1300 return; 1252 return;
1253 tsk->sysvsem.undo_list = NULL;
1301 1254
1302 if (!atomic_dec_and_test(&undo_list->refcnt)) 1255 if (!atomic_dec_and_test(&undo_list->refcnt))
1303 return; 1256 return;