diff options
Diffstat (limited to 'ipc/sem.c')
| -rw-r--r-- | ipc/sem.c | 159 |
1 files changed, 56 insertions, 103 deletions
| @@ -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 | ||
| 96 | static int newary(struct ipc_namespace *, struct ipc_params *); | 95 | static int newary(struct ipc_namespace *, struct ipc_params *); |
| 97 | static void freeary(struct ipc_namespace *, struct kern_ipc_perm *); | 96 | static 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 | */ | ||
| 148 | static 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 | ||
| 168 | static 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 | |||
| 174 | static inline void sem_getref_and_unlock(struct sem_array *sma) | ||
| 175 | { | ||
| 176 | ipc_rcu_getref(sma); | ||
| 177 | ipc_unlock(&(sma)->sem_perm); | ||
| 178 | } | ||
| 179 | |||
| 180 | static 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 | |||
| 184 | static inline void sem_rmid(struct ipc_namespace *ns, struct sem_array *s) | 187 | static 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 | ||
| 833 | struct sem_setbuf { | 823 | static inline unsigned long |
| 834 | uid_t uid; | 824 | copy_semid_from_user(struct semid64_ds *out, void __user *buf, int version) |
| 835 | gid_t gid; | ||
| 836 | mode_t mode; | ||
| 837 | }; | ||
| 838 | |||
| 839 | static 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 | ||
| 873 | static 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 | */ | ||
| 854 | static 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 | ||
| 931 | out_unlock: | 889 | out_unlock: |
| 932 | sem_unlock(sma); | 890 | sem_unlock(sma); |
| 891 | out_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; |
