diff options
Diffstat (limited to 'ipc/sem.c')
-rw-r--r-- | ipc/sem.c | 123 |
1 files changed, 76 insertions, 47 deletions
@@ -799,7 +799,7 @@ static unsigned long copy_semid_to_user(void __user *buf, struct semid64_ds *in, | |||
799 | } | 799 | } |
800 | 800 | ||
801 | static int semctl_nolock(struct ipc_namespace *ns, int semid, | 801 | static int semctl_nolock(struct ipc_namespace *ns, int semid, |
802 | int cmd, int version, union semun arg) | 802 | int cmd, int version, void __user *p) |
803 | { | 803 | { |
804 | int err; | 804 | int err; |
805 | struct sem_array *sma; | 805 | struct sem_array *sma; |
@@ -834,7 +834,7 @@ static int semctl_nolock(struct ipc_namespace *ns, int semid, | |||
834 | } | 834 | } |
835 | max_id = ipc_get_maxid(&sem_ids(ns)); | 835 | max_id = ipc_get_maxid(&sem_ids(ns)); |
836 | up_read(&sem_ids(ns).rw_mutex); | 836 | up_read(&sem_ids(ns).rw_mutex); |
837 | if (copy_to_user (arg.__buf, &seminfo, sizeof(struct seminfo))) | 837 | if (copy_to_user(p, &seminfo, sizeof(struct seminfo))) |
838 | return -EFAULT; | 838 | return -EFAULT; |
839 | return (max_id < 0) ? 0: max_id; | 839 | return (max_id < 0) ? 0: max_id; |
840 | } | 840 | } |
@@ -871,7 +871,7 @@ static int semctl_nolock(struct ipc_namespace *ns, int semid, | |||
871 | tbuf.sem_ctime = sma->sem_ctime; | 871 | tbuf.sem_ctime = sma->sem_ctime; |
872 | tbuf.sem_nsems = sma->sem_nsems; | 872 | tbuf.sem_nsems = sma->sem_nsems; |
873 | sem_unlock(sma); | 873 | sem_unlock(sma); |
874 | if (copy_semid_to_user (arg.buf, &tbuf, version)) | 874 | if (copy_semid_to_user(p, &tbuf, version)) |
875 | return -EFAULT; | 875 | return -EFAULT; |
876 | return id; | 876 | return id; |
877 | } | 877 | } |
@@ -883,8 +883,67 @@ out_unlock: | |||
883 | return err; | 883 | return err; |
884 | } | 884 | } |
885 | 885 | ||
886 | static int semctl_setval(struct ipc_namespace *ns, int semid, int semnum, | ||
887 | unsigned long arg) | ||
888 | { | ||
889 | struct sem_undo *un; | ||
890 | struct sem_array *sma; | ||
891 | struct sem* curr; | ||
892 | int err; | ||
893 | int nsems; | ||
894 | struct list_head tasks; | ||
895 | int val; | ||
896 | #if defined(CONFIG_64BIT) && defined(__BIG_ENDIAN) | ||
897 | /* big-endian 64bit */ | ||
898 | val = arg >> 32; | ||
899 | #else | ||
900 | /* 32bit or little-endian 64bit */ | ||
901 | val = arg; | ||
902 | #endif | ||
903 | |||
904 | sma = sem_lock_check(ns, semid); | ||
905 | if (IS_ERR(sma)) | ||
906 | return PTR_ERR(sma); | ||
907 | |||
908 | INIT_LIST_HEAD(&tasks); | ||
909 | nsems = sma->sem_nsems; | ||
910 | |||
911 | err = -EACCES; | ||
912 | if (ipcperms(ns, &sma->sem_perm, S_IWUGO)) | ||
913 | goto out_unlock; | ||
914 | |||
915 | err = security_sem_semctl(sma, SETVAL); | ||
916 | if (err) | ||
917 | goto out_unlock; | ||
918 | |||
919 | err = -EINVAL; | ||
920 | if(semnum < 0 || semnum >= nsems) | ||
921 | goto out_unlock; | ||
922 | |||
923 | curr = &sma->sem_base[semnum]; | ||
924 | |||
925 | err = -ERANGE; | ||
926 | if (val > SEMVMX || val < 0) | ||
927 | goto out_unlock; | ||
928 | |||
929 | assert_spin_locked(&sma->sem_perm.lock); | ||
930 | list_for_each_entry(un, &sma->list_id, list_id) | ||
931 | un->semadj[semnum] = 0; | ||
932 | |||
933 | curr->semval = val; | ||
934 | curr->sempid = task_tgid_vnr(current); | ||
935 | sma->sem_ctime = get_seconds(); | ||
936 | /* maybe some queued-up processes were waiting for this */ | ||
937 | do_smart_update(sma, NULL, 0, 0, &tasks); | ||
938 | err = 0; | ||
939 | out_unlock: | ||
940 | sem_unlock(sma); | ||
941 | wake_up_sem_queue_do(&tasks); | ||
942 | return err; | ||
943 | } | ||
944 | |||
886 | static int semctl_main(struct ipc_namespace *ns, int semid, int semnum, | 945 | static int semctl_main(struct ipc_namespace *ns, int semid, int semnum, |
887 | int cmd, int version, union semun arg) | 946 | int cmd, void __user *p) |
888 | { | 947 | { |
889 | struct sem_array *sma; | 948 | struct sem_array *sma; |
890 | struct sem* curr; | 949 | struct sem* curr; |
@@ -903,7 +962,7 @@ static int semctl_main(struct ipc_namespace *ns, int semid, int semnum, | |||
903 | 962 | ||
904 | err = -EACCES; | 963 | err = -EACCES; |
905 | if (ipcperms(ns, &sma->sem_perm, | 964 | if (ipcperms(ns, &sma->sem_perm, |
906 | (cmd == SETVAL || cmd == SETALL) ? S_IWUGO : S_IRUGO)) | 965 | cmd == SETALL ? S_IWUGO : S_IRUGO)) |
907 | goto out_unlock; | 966 | goto out_unlock; |
908 | 967 | ||
909 | err = security_sem_semctl(sma, cmd); | 968 | err = security_sem_semctl(sma, cmd); |
@@ -914,7 +973,7 @@ static int semctl_main(struct ipc_namespace *ns, int semid, int semnum, | |||
914 | switch (cmd) { | 973 | switch (cmd) { |
915 | case GETALL: | 974 | case GETALL: |
916 | { | 975 | { |
917 | ushort __user *array = arg.array; | 976 | ushort __user *array = p; |
918 | int i; | 977 | int i; |
919 | 978 | ||
920 | if(nsems > SEMMSL_FAST) { | 979 | if(nsems > SEMMSL_FAST) { |
@@ -957,7 +1016,7 @@ static int semctl_main(struct ipc_namespace *ns, int semid, int semnum, | |||
957 | } | 1016 | } |
958 | } | 1017 | } |
959 | 1018 | ||
960 | if (copy_from_user (sem_io, arg.array, nsems*sizeof(ushort))) { | 1019 | if (copy_from_user (sem_io, p, nsems*sizeof(ushort))) { |
961 | sem_putref(sma); | 1020 | sem_putref(sma); |
962 | err = -EFAULT; | 1021 | err = -EFAULT; |
963 | goto out_free; | 1022 | goto out_free; |
@@ -991,7 +1050,7 @@ static int semctl_main(struct ipc_namespace *ns, int semid, int semnum, | |||
991 | err = 0; | 1050 | err = 0; |
992 | goto out_unlock; | 1051 | goto out_unlock; |
993 | } | 1052 | } |
994 | /* GETVAL, GETPID, GETNCTN, GETZCNT, SETVAL: fall-through */ | 1053 | /* GETVAL, GETPID, GETNCTN, GETZCNT: fall-through */ |
995 | } | 1054 | } |
996 | err = -EINVAL; | 1055 | err = -EINVAL; |
997 | if(semnum < 0 || semnum >= nsems) | 1056 | if(semnum < 0 || semnum >= nsems) |
@@ -1012,27 +1071,6 @@ static int semctl_main(struct ipc_namespace *ns, int semid, int semnum, | |||
1012 | case GETZCNT: | 1071 | case GETZCNT: |
1013 | err = count_semzcnt(sma,semnum); | 1072 | err = count_semzcnt(sma,semnum); |
1014 | goto out_unlock; | 1073 | goto out_unlock; |
1015 | case SETVAL: | ||
1016 | { | ||
1017 | int val = arg.val; | ||
1018 | struct sem_undo *un; | ||
1019 | |||
1020 | err = -ERANGE; | ||
1021 | if (val > SEMVMX || val < 0) | ||
1022 | goto out_unlock; | ||
1023 | |||
1024 | assert_spin_locked(&sma->sem_perm.lock); | ||
1025 | list_for_each_entry(un, &sma->list_id, list_id) | ||
1026 | un->semadj[semnum] = 0; | ||
1027 | |||
1028 | curr->semval = val; | ||
1029 | curr->sempid = task_tgid_vnr(current); | ||
1030 | sma->sem_ctime = get_seconds(); | ||
1031 | /* maybe some queued-up processes were waiting for this */ | ||
1032 | do_smart_update(sma, NULL, 0, 0, &tasks); | ||
1033 | err = 0; | ||
1034 | goto out_unlock; | ||
1035 | } | ||
1036 | } | 1074 | } |
1037 | out_unlock: | 1075 | out_unlock: |
1038 | sem_unlock(sma); | 1076 | sem_unlock(sma); |
@@ -1076,7 +1114,7 @@ copy_semid_from_user(struct semid64_ds *out, void __user *buf, int version) | |||
1076 | * NOTE: no locks must be held, the rw_mutex is taken inside this function. | 1114 | * NOTE: no locks must be held, the rw_mutex is taken inside this function. |
1077 | */ | 1115 | */ |
1078 | static int semctl_down(struct ipc_namespace *ns, int semid, | 1116 | static int semctl_down(struct ipc_namespace *ns, int semid, |
1079 | int cmd, int version, union semun arg) | 1117 | int cmd, int version, void __user *p) |
1080 | { | 1118 | { |
1081 | struct sem_array *sma; | 1119 | struct sem_array *sma; |
1082 | int err; | 1120 | int err; |
@@ -1084,7 +1122,7 @@ static int semctl_down(struct ipc_namespace *ns, int semid, | |||
1084 | struct kern_ipc_perm *ipcp; | 1122 | struct kern_ipc_perm *ipcp; |
1085 | 1123 | ||
1086 | if(cmd == IPC_SET) { | 1124 | if(cmd == IPC_SET) { |
1087 | if (copy_semid_from_user(&semid64, arg.buf, version)) | 1125 | if (copy_semid_from_user(&semid64, p, version)) |
1088 | return -EFAULT; | 1126 | return -EFAULT; |
1089 | } | 1127 | } |
1090 | 1128 | ||
@@ -1120,11 +1158,11 @@ out_up: | |||
1120 | return err; | 1158 | return err; |
1121 | } | 1159 | } |
1122 | 1160 | ||
1123 | SYSCALL_DEFINE(semctl)(int semid, int semnum, int cmd, union semun arg) | 1161 | SYSCALL_DEFINE4(semctl, int, semid, int, semnum, int, cmd, unsigned long, arg) |
1124 | { | 1162 | { |
1125 | int err = -EINVAL; | ||
1126 | int version; | 1163 | int version; |
1127 | struct ipc_namespace *ns; | 1164 | struct ipc_namespace *ns; |
1165 | void __user *p = (void __user *)arg; | ||
1128 | 1166 | ||
1129 | if (semid < 0) | 1167 | if (semid < 0) |
1130 | return -EINVAL; | 1168 | return -EINVAL; |
@@ -1137,32 +1175,23 @@ SYSCALL_DEFINE(semctl)(int semid, int semnum, int cmd, union semun arg) | |||
1137 | case SEM_INFO: | 1175 | case SEM_INFO: |
1138 | case IPC_STAT: | 1176 | case IPC_STAT: |
1139 | case SEM_STAT: | 1177 | case SEM_STAT: |
1140 | err = semctl_nolock(ns, semid, cmd, version, arg); | 1178 | return semctl_nolock(ns, semid, cmd, version, p); |
1141 | return err; | ||
1142 | case GETALL: | 1179 | case GETALL: |
1143 | case GETVAL: | 1180 | case GETVAL: |
1144 | case GETPID: | 1181 | case GETPID: |
1145 | case GETNCNT: | 1182 | case GETNCNT: |
1146 | case GETZCNT: | 1183 | case GETZCNT: |
1147 | case SETVAL: | ||
1148 | case SETALL: | 1184 | case SETALL: |
1149 | err = semctl_main(ns,semid,semnum,cmd,version,arg); | 1185 | return semctl_main(ns, semid, semnum, cmd, p); |
1150 | return err; | 1186 | case SETVAL: |
1187 | return semctl_setval(ns, semid, semnum, arg); | ||
1151 | case IPC_RMID: | 1188 | case IPC_RMID: |
1152 | case IPC_SET: | 1189 | case IPC_SET: |
1153 | err = semctl_down(ns, semid, cmd, version, arg); | 1190 | return semctl_down(ns, semid, cmd, version, p); |
1154 | return err; | ||
1155 | default: | 1191 | default: |
1156 | return -EINVAL; | 1192 | return -EINVAL; |
1157 | } | 1193 | } |
1158 | } | 1194 | } |
1159 | #ifdef CONFIG_HAVE_SYSCALL_WRAPPERS | ||
1160 | asmlinkage long SyS_semctl(int semid, int semnum, int cmd, union semun arg) | ||
1161 | { | ||
1162 | return SYSC_semctl((int) semid, (int) semnum, (int) cmd, arg); | ||
1163 | } | ||
1164 | SYSCALL_ALIAS(sys_semctl, SyS_semctl); | ||
1165 | #endif | ||
1166 | 1195 | ||
1167 | /* If the task doesn't already have a undo_list, then allocate one | 1196 | /* If the task doesn't already have a undo_list, then allocate one |
1168 | * here. We guarantee there is only one thread using this undo list, | 1197 | * here. We guarantee there is only one thread using this undo list, |