diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2017-09-14 20:37:26 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2017-09-14 20:37:26 -0400 |
commit | cc73fee0bae2d66594d1fa2df92bbd783aa98e04 (patch) | |
tree | d1e7fe7f76cae4cbc941fc3bb43a46d237a9df77 /ipc/shm.c | |
parent | e7cdb60fd28b252f1c15a0e50f79a01906124915 (diff) | |
parent | aaed2dd8a31359e5767ee099ecbb078d55be4d29 (diff) |
Merge branch 'work.ipc' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
Pull ipc compat cleanup and 64-bit time_t from Al Viro:
"IPC copyin/copyout sanitizing, including 64bit time_t work from Deepa
Dinamani"
* 'work.ipc' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs:
utimes: Make utimes y2038 safe
ipc: shm: Make shmid_kernel timestamps y2038 safe
ipc: sem: Make sem_array timestamps y2038 safe
ipc: msg: Make msg_queue timestamps y2038 safe
ipc: mqueue: Replace timespec with timespec64
ipc: Make sys_semtimedop() y2038 safe
get rid of SYSVIPC_COMPAT on ia64
semtimedop(): move compat to native
shmat(2): move compat to native
msgrcv(2), msgsnd(2): move compat to native
ipc(2): move compat to native
ipc: make use of compat ipc_perm helpers
semctl(): move compat to native
semctl(): separate all layout-dependent copyin/copyout
msgctl(): move compat to native
msgctl(): split the actual work from copyin/copyout
ipc: move compat shmctl to native
shmctl: split the work from copyin/copyout
Diffstat (limited to 'ipc/shm.c')
-rw-r--r-- | ipc/shm.c | 533 |
1 files changed, 363 insertions, 170 deletions
@@ -202,7 +202,7 @@ static int __shm_open(struct vm_area_struct *vma) | |||
202 | if (IS_ERR(shp)) | 202 | if (IS_ERR(shp)) |
203 | return PTR_ERR(shp); | 203 | return PTR_ERR(shp); |
204 | 204 | ||
205 | shp->shm_atim = get_seconds(); | 205 | shp->shm_atim = ktime_get_real_seconds(); |
206 | shp->shm_lprid = task_tgid_vnr(current); | 206 | shp->shm_lprid = task_tgid_vnr(current); |
207 | shp->shm_nattch++; | 207 | shp->shm_nattch++; |
208 | shm_unlock(shp); | 208 | shm_unlock(shp); |
@@ -289,7 +289,7 @@ static void shm_close(struct vm_area_struct *vma) | |||
289 | goto done; /* no-op */ | 289 | goto done; /* no-op */ |
290 | 290 | ||
291 | shp->shm_lprid = task_tgid_vnr(current); | 291 | shp->shm_lprid = task_tgid_vnr(current); |
292 | shp->shm_dtim = get_seconds(); | 292 | shp->shm_dtim = ktime_get_real_seconds(); |
293 | shp->shm_nattch--; | 293 | shp->shm_nattch--; |
294 | if (shm_may_destroy(ns, shp)) | 294 | if (shm_may_destroy(ns, shp)) |
295 | shm_destroy(ns, shp); | 295 | shm_destroy(ns, shp); |
@@ -594,7 +594,7 @@ static int newseg(struct ipc_namespace *ns, struct ipc_params *params) | |||
594 | shp->shm_cprid = task_tgid_vnr(current); | 594 | shp->shm_cprid = task_tgid_vnr(current); |
595 | shp->shm_lprid = 0; | 595 | shp->shm_lprid = 0; |
596 | shp->shm_atim = shp->shm_dtim = 0; | 596 | shp->shm_atim = shp->shm_dtim = 0; |
597 | shp->shm_ctim = get_seconds(); | 597 | shp->shm_ctim = ktime_get_real_seconds(); |
598 | shp->shm_segsz = size; | 598 | shp->shm_segsz = size; |
599 | shp->shm_nattch = 0; | 599 | shp->shm_nattch = 0; |
600 | shp->shm_file = file; | 600 | shp->shm_file = file; |
@@ -815,23 +815,17 @@ static void shm_get_stat(struct ipc_namespace *ns, unsigned long *rss, | |||
815 | * NOTE: no locks must be held, the rwsem is taken inside this function. | 815 | * NOTE: no locks must be held, the rwsem is taken inside this function. |
816 | */ | 816 | */ |
817 | static int shmctl_down(struct ipc_namespace *ns, int shmid, int cmd, | 817 | static int shmctl_down(struct ipc_namespace *ns, int shmid, int cmd, |
818 | struct shmid_ds __user *buf, int version) | 818 | struct shmid64_ds *shmid64) |
819 | { | 819 | { |
820 | struct kern_ipc_perm *ipcp; | 820 | struct kern_ipc_perm *ipcp; |
821 | struct shmid64_ds shmid64; | ||
822 | struct shmid_kernel *shp; | 821 | struct shmid_kernel *shp; |
823 | int err; | 822 | int err; |
824 | 823 | ||
825 | if (cmd == IPC_SET) { | ||
826 | if (copy_shmid_from_user(&shmid64, buf, version)) | ||
827 | return -EFAULT; | ||
828 | } | ||
829 | |||
830 | down_write(&shm_ids(ns).rwsem); | 824 | down_write(&shm_ids(ns).rwsem); |
831 | rcu_read_lock(); | 825 | rcu_read_lock(); |
832 | 826 | ||
833 | ipcp = ipcctl_pre_down_nolock(ns, &shm_ids(ns), shmid, cmd, | 827 | ipcp = ipcctl_pre_down_nolock(ns, &shm_ids(ns), shmid, cmd, |
834 | &shmid64.shm_perm, 0); | 828 | &shmid64->shm_perm, 0); |
835 | if (IS_ERR(ipcp)) { | 829 | if (IS_ERR(ipcp)) { |
836 | err = PTR_ERR(ipcp); | 830 | err = PTR_ERR(ipcp); |
837 | goto out_unlock1; | 831 | goto out_unlock1; |
@@ -851,10 +845,10 @@ static int shmctl_down(struct ipc_namespace *ns, int shmid, int cmd, | |||
851 | goto out_up; | 845 | goto out_up; |
852 | case IPC_SET: | 846 | case IPC_SET: |
853 | ipc_lock_object(&shp->shm_perm); | 847 | ipc_lock_object(&shp->shm_perm); |
854 | err = ipc_update_perm(&shmid64.shm_perm, ipcp); | 848 | err = ipc_update_perm(&shmid64->shm_perm, ipcp); |
855 | if (err) | 849 | if (err) |
856 | goto out_unlock0; | 850 | goto out_unlock0; |
857 | shp->shm_ctim = get_seconds(); | 851 | shp->shm_ctim = ktime_get_real_seconds(); |
858 | break; | 852 | break; |
859 | default: | 853 | default: |
860 | err = -EINVAL; | 854 | err = -EINVAL; |
@@ -870,125 +864,175 @@ out_up: | |||
870 | return err; | 864 | return err; |
871 | } | 865 | } |
872 | 866 | ||
873 | static int shmctl_nolock(struct ipc_namespace *ns, int shmid, | 867 | static int shmctl_ipc_info(struct ipc_namespace *ns, |
874 | int cmd, int version, void __user *buf) | 868 | struct shminfo64 *shminfo) |
875 | { | 869 | { |
876 | int err; | 870 | int err = security_shm_shmctl(NULL, IPC_INFO); |
877 | struct shmid_kernel *shp; | 871 | if (!err) { |
878 | 872 | memset(shminfo, 0, sizeof(*shminfo)); | |
879 | /* preliminary security checks for *_INFO */ | 873 | shminfo->shmmni = shminfo->shmseg = ns->shm_ctlmni; |
880 | if (cmd == IPC_INFO || cmd == SHM_INFO) { | 874 | shminfo->shmmax = ns->shm_ctlmax; |
881 | err = security_shm_shmctl(NULL, cmd); | 875 | shminfo->shmall = ns->shm_ctlall; |
882 | if (err) | 876 | shminfo->shmmin = SHMMIN; |
883 | return err; | ||
884 | } | ||
885 | |||
886 | switch (cmd) { | ||
887 | case IPC_INFO: | ||
888 | { | ||
889 | struct shminfo64 shminfo; | ||
890 | |||
891 | memset(&shminfo, 0, sizeof(shminfo)); | ||
892 | shminfo.shmmni = shminfo.shmseg = ns->shm_ctlmni; | ||
893 | shminfo.shmmax = ns->shm_ctlmax; | ||
894 | shminfo.shmall = ns->shm_ctlall; | ||
895 | |||
896 | shminfo.shmmin = SHMMIN; | ||
897 | if (copy_shminfo_to_user(buf, &shminfo, version)) | ||
898 | return -EFAULT; | ||
899 | |||
900 | down_read(&shm_ids(ns).rwsem); | 877 | down_read(&shm_ids(ns).rwsem); |
901 | err = ipc_get_maxid(&shm_ids(ns)); | 878 | err = ipc_get_maxid(&shm_ids(ns)); |
902 | up_read(&shm_ids(ns).rwsem); | 879 | up_read(&shm_ids(ns).rwsem); |
903 | |||
904 | if (err < 0) | 880 | if (err < 0) |
905 | err = 0; | 881 | err = 0; |
906 | goto out; | ||
907 | } | 882 | } |
908 | case SHM_INFO: | 883 | return err; |
909 | { | 884 | } |
910 | struct shm_info shm_info; | ||
911 | 885 | ||
912 | memset(&shm_info, 0, sizeof(shm_info)); | 886 | static int shmctl_shm_info(struct ipc_namespace *ns, |
887 | struct shm_info *shm_info) | ||
888 | { | ||
889 | int err = security_shm_shmctl(NULL, SHM_INFO); | ||
890 | if (!err) { | ||
891 | memset(shm_info, 0, sizeof(*shm_info)); | ||
913 | down_read(&shm_ids(ns).rwsem); | 892 | down_read(&shm_ids(ns).rwsem); |
914 | shm_info.used_ids = shm_ids(ns).in_use; | 893 | shm_info->used_ids = shm_ids(ns).in_use; |
915 | shm_get_stat(ns, &shm_info.shm_rss, &shm_info.shm_swp); | 894 | shm_get_stat(ns, &shm_info->shm_rss, &shm_info->shm_swp); |
916 | shm_info.shm_tot = ns->shm_tot; | 895 | shm_info->shm_tot = ns->shm_tot; |
917 | shm_info.swap_attempts = 0; | 896 | shm_info->swap_attempts = 0; |
918 | shm_info.swap_successes = 0; | 897 | shm_info->swap_successes = 0; |
919 | err = ipc_get_maxid(&shm_ids(ns)); | 898 | err = ipc_get_maxid(&shm_ids(ns)); |
920 | up_read(&shm_ids(ns).rwsem); | 899 | up_read(&shm_ids(ns).rwsem); |
921 | if (copy_to_user(buf, &shm_info, sizeof(shm_info))) { | 900 | if (err < 0) |
922 | err = -EFAULT; | 901 | err = 0; |
923 | goto out; | 902 | } |
903 | return err; | ||
904 | } | ||
905 | |||
906 | static int shmctl_stat(struct ipc_namespace *ns, int shmid, | ||
907 | int cmd, struct shmid64_ds *tbuf) | ||
908 | { | ||
909 | struct shmid_kernel *shp; | ||
910 | int result; | ||
911 | int err; | ||
912 | |||
913 | rcu_read_lock(); | ||
914 | if (cmd == SHM_STAT) { | ||
915 | shp = shm_obtain_object(ns, shmid); | ||
916 | if (IS_ERR(shp)) { | ||
917 | err = PTR_ERR(shp); | ||
918 | goto out_unlock; | ||
919 | } | ||
920 | result = shp->shm_perm.id; | ||
921 | } else { | ||
922 | shp = shm_obtain_object_check(ns, shmid); | ||
923 | if (IS_ERR(shp)) { | ||
924 | err = PTR_ERR(shp); | ||
925 | goto out_unlock; | ||
924 | } | 926 | } |
927 | result = 0; | ||
928 | } | ||
925 | 929 | ||
926 | err = err < 0 ? 0 : err; | 930 | err = -EACCES; |
927 | goto out; | 931 | if (ipcperms(ns, &shp->shm_perm, S_IRUGO)) |
932 | goto out_unlock; | ||
933 | |||
934 | err = security_shm_shmctl(shp, cmd); | ||
935 | if (err) | ||
936 | goto out_unlock; | ||
937 | |||
938 | memset(tbuf, 0, sizeof(*tbuf)); | ||
939 | kernel_to_ipc64_perm(&shp->shm_perm, &tbuf->shm_perm); | ||
940 | tbuf->shm_segsz = shp->shm_segsz; | ||
941 | tbuf->shm_atime = shp->shm_atim; | ||
942 | tbuf->shm_dtime = shp->shm_dtim; | ||
943 | tbuf->shm_ctime = shp->shm_ctim; | ||
944 | tbuf->shm_cpid = shp->shm_cprid; | ||
945 | tbuf->shm_lpid = shp->shm_lprid; | ||
946 | tbuf->shm_nattch = shp->shm_nattch; | ||
947 | rcu_read_unlock(); | ||
948 | return result; | ||
949 | |||
950 | out_unlock: | ||
951 | rcu_read_unlock(); | ||
952 | return err; | ||
953 | } | ||
954 | |||
955 | static int shmctl_do_lock(struct ipc_namespace *ns, int shmid, int cmd) | ||
956 | { | ||
957 | struct shmid_kernel *shp; | ||
958 | struct file *shm_file; | ||
959 | int err; | ||
960 | |||
961 | rcu_read_lock(); | ||
962 | shp = shm_obtain_object_check(ns, shmid); | ||
963 | if (IS_ERR(shp)) { | ||
964 | err = PTR_ERR(shp); | ||
965 | goto out_unlock1; | ||
928 | } | 966 | } |
929 | case SHM_STAT: | ||
930 | case IPC_STAT: | ||
931 | { | ||
932 | struct shmid64_ds tbuf; | ||
933 | int result; | ||
934 | |||
935 | rcu_read_lock(); | ||
936 | if (cmd == SHM_STAT) { | ||
937 | shp = shm_obtain_object(ns, shmid); | ||
938 | if (IS_ERR(shp)) { | ||
939 | err = PTR_ERR(shp); | ||
940 | goto out_unlock; | ||
941 | } | ||
942 | result = shp->shm_perm.id; | ||
943 | } else { | ||
944 | shp = shm_obtain_object_check(ns, shmid); | ||
945 | if (IS_ERR(shp)) { | ||
946 | err = PTR_ERR(shp); | ||
947 | goto out_unlock; | ||
948 | } | ||
949 | result = 0; | ||
950 | } | ||
951 | 967 | ||
952 | err = -EACCES; | 968 | audit_ipc_obj(&(shp->shm_perm)); |
953 | if (ipcperms(ns, &shp->shm_perm, S_IRUGO)) | 969 | err = security_shm_shmctl(shp, cmd); |
954 | goto out_unlock; | 970 | if (err) |
971 | goto out_unlock1; | ||
955 | 972 | ||
956 | err = security_shm_shmctl(shp, cmd); | 973 | ipc_lock_object(&shp->shm_perm); |
957 | if (err) | ||
958 | goto out_unlock; | ||
959 | 974 | ||
960 | memset(&tbuf, 0, sizeof(tbuf)); | 975 | /* check if shm_destroy() is tearing down shp */ |
961 | kernel_to_ipc64_perm(&shp->shm_perm, &tbuf.shm_perm); | 976 | if (!ipc_valid_object(&shp->shm_perm)) { |
962 | tbuf.shm_segsz = shp->shm_segsz; | 977 | err = -EIDRM; |
963 | tbuf.shm_atime = shp->shm_atim; | 978 | goto out_unlock0; |
964 | tbuf.shm_dtime = shp->shm_dtim; | ||
965 | tbuf.shm_ctime = shp->shm_ctim; | ||
966 | tbuf.shm_cpid = shp->shm_cprid; | ||
967 | tbuf.shm_lpid = shp->shm_lprid; | ||
968 | tbuf.shm_nattch = shp->shm_nattch; | ||
969 | rcu_read_unlock(); | ||
970 | |||
971 | if (copy_shmid_to_user(buf, &tbuf, version)) | ||
972 | err = -EFAULT; | ||
973 | else | ||
974 | err = result; | ||
975 | goto out; | ||
976 | } | 979 | } |
977 | default: | 980 | |
978 | return -EINVAL; | 981 | if (!ns_capable(ns->user_ns, CAP_IPC_LOCK)) { |
982 | kuid_t euid = current_euid(); | ||
983 | |||
984 | if (!uid_eq(euid, shp->shm_perm.uid) && | ||
985 | !uid_eq(euid, shp->shm_perm.cuid)) { | ||
986 | err = -EPERM; | ||
987 | goto out_unlock0; | ||
988 | } | ||
989 | if (cmd == SHM_LOCK && !rlimit(RLIMIT_MEMLOCK)) { | ||
990 | err = -EPERM; | ||
991 | goto out_unlock0; | ||
992 | } | ||
979 | } | 993 | } |
980 | 994 | ||
981 | out_unlock: | 995 | shm_file = shp->shm_file; |
996 | if (is_file_hugepages(shm_file)) | ||
997 | goto out_unlock0; | ||
998 | |||
999 | if (cmd == SHM_LOCK) { | ||
1000 | struct user_struct *user = current_user(); | ||
1001 | |||
1002 | err = shmem_lock(shm_file, 1, user); | ||
1003 | if (!err && !(shp->shm_perm.mode & SHM_LOCKED)) { | ||
1004 | shp->shm_perm.mode |= SHM_LOCKED; | ||
1005 | shp->mlock_user = user; | ||
1006 | } | ||
1007 | goto out_unlock0; | ||
1008 | } | ||
1009 | |||
1010 | /* SHM_UNLOCK */ | ||
1011 | if (!(shp->shm_perm.mode & SHM_LOCKED)) | ||
1012 | goto out_unlock0; | ||
1013 | shmem_lock(shm_file, 0, shp->mlock_user); | ||
1014 | shp->shm_perm.mode &= ~SHM_LOCKED; | ||
1015 | shp->mlock_user = NULL; | ||
1016 | get_file(shm_file); | ||
1017 | ipc_unlock_object(&shp->shm_perm); | ||
1018 | rcu_read_unlock(); | ||
1019 | shmem_unlock_mapping(shm_file->f_mapping); | ||
1020 | |||
1021 | fput(shm_file); | ||
1022 | return err; | ||
1023 | |||
1024 | out_unlock0: | ||
1025 | ipc_unlock_object(&shp->shm_perm); | ||
1026 | out_unlock1: | ||
982 | rcu_read_unlock(); | 1027 | rcu_read_unlock(); |
983 | out: | ||
984 | return err; | 1028 | return err; |
985 | } | 1029 | } |
986 | 1030 | ||
987 | SYSCALL_DEFINE3(shmctl, int, shmid, int, cmd, struct shmid_ds __user *, buf) | 1031 | SYSCALL_DEFINE3(shmctl, int, shmid, int, cmd, struct shmid_ds __user *, buf) |
988 | { | 1032 | { |
989 | struct shmid_kernel *shp; | ||
990 | int err, version; | 1033 | int err, version; |
991 | struct ipc_namespace *ns; | 1034 | struct ipc_namespace *ns; |
1035 | struct shmid64_ds sem64; | ||
992 | 1036 | ||
993 | if (cmd < 0 || shmid < 0) | 1037 | if (cmd < 0 || shmid < 0) |
994 | return -EINVAL; | 1038 | return -EINVAL; |
@@ -997,92 +1041,222 @@ SYSCALL_DEFINE3(shmctl, int, shmid, int, cmd, struct shmid_ds __user *, buf) | |||
997 | ns = current->nsproxy->ipc_ns; | 1041 | ns = current->nsproxy->ipc_ns; |
998 | 1042 | ||
999 | switch (cmd) { | 1043 | switch (cmd) { |
1000 | case IPC_INFO: | 1044 | case IPC_INFO: { |
1001 | case SHM_INFO: | 1045 | struct shminfo64 shminfo; |
1046 | err = shmctl_ipc_info(ns, &shminfo); | ||
1047 | if (err < 0) | ||
1048 | return err; | ||
1049 | if (copy_shminfo_to_user(buf, &shminfo, version)) | ||
1050 | err = -EFAULT; | ||
1051 | return err; | ||
1052 | } | ||
1053 | case SHM_INFO: { | ||
1054 | struct shm_info shm_info; | ||
1055 | err = shmctl_shm_info(ns, &shm_info); | ||
1056 | if (err < 0) | ||
1057 | return err; | ||
1058 | if (copy_to_user(buf, &shm_info, sizeof(shm_info))) | ||
1059 | err = -EFAULT; | ||
1060 | return err; | ||
1061 | } | ||
1002 | case SHM_STAT: | 1062 | case SHM_STAT: |
1003 | case IPC_STAT: | 1063 | case IPC_STAT: { |
1004 | return shmctl_nolock(ns, shmid, cmd, version, buf); | 1064 | err = shmctl_stat(ns, shmid, cmd, &sem64); |
1005 | case IPC_RMID: | 1065 | if (err < 0) |
1066 | return err; | ||
1067 | if (copy_shmid_to_user(buf, &sem64, version)) | ||
1068 | err = -EFAULT; | ||
1069 | return err; | ||
1070 | } | ||
1006 | case IPC_SET: | 1071 | case IPC_SET: |
1007 | return shmctl_down(ns, shmid, cmd, buf, version); | 1072 | if (copy_shmid_from_user(&sem64, buf, version)) |
1073 | return -EFAULT; | ||
1074 | /* fallthru */ | ||
1075 | case IPC_RMID: | ||
1076 | return shmctl_down(ns, shmid, cmd, &sem64); | ||
1008 | case SHM_LOCK: | 1077 | case SHM_LOCK: |
1009 | case SHM_UNLOCK: | 1078 | case SHM_UNLOCK: |
1010 | { | 1079 | return shmctl_do_lock(ns, shmid, cmd); |
1011 | struct file *shm_file; | 1080 | default: |
1081 | return -EINVAL; | ||
1082 | } | ||
1083 | } | ||
1012 | 1084 | ||
1013 | rcu_read_lock(); | 1085 | #ifdef CONFIG_COMPAT |
1014 | shp = shm_obtain_object_check(ns, shmid); | 1086 | |
1015 | if (IS_ERR(shp)) { | 1087 | struct compat_shmid_ds { |
1016 | err = PTR_ERR(shp); | 1088 | struct compat_ipc_perm shm_perm; |
1017 | goto out_unlock1; | 1089 | int shm_segsz; |
1018 | } | 1090 | compat_time_t shm_atime; |
1091 | compat_time_t shm_dtime; | ||
1092 | compat_time_t shm_ctime; | ||
1093 | compat_ipc_pid_t shm_cpid; | ||
1094 | compat_ipc_pid_t shm_lpid; | ||
1095 | unsigned short shm_nattch; | ||
1096 | unsigned short shm_unused; | ||
1097 | compat_uptr_t shm_unused2; | ||
1098 | compat_uptr_t shm_unused3; | ||
1099 | }; | ||
1019 | 1100 | ||
1020 | audit_ipc_obj(&(shp->shm_perm)); | 1101 | struct compat_shminfo64 { |
1021 | err = security_shm_shmctl(shp, cmd); | 1102 | compat_ulong_t shmmax; |
1022 | if (err) | 1103 | compat_ulong_t shmmin; |
1023 | goto out_unlock1; | 1104 | compat_ulong_t shmmni; |
1105 | compat_ulong_t shmseg; | ||
1106 | compat_ulong_t shmall; | ||
1107 | compat_ulong_t __unused1; | ||
1108 | compat_ulong_t __unused2; | ||
1109 | compat_ulong_t __unused3; | ||
1110 | compat_ulong_t __unused4; | ||
1111 | }; | ||
1024 | 1112 | ||
1025 | ipc_lock_object(&shp->shm_perm); | 1113 | struct compat_shm_info { |
1114 | compat_int_t used_ids; | ||
1115 | compat_ulong_t shm_tot, shm_rss, shm_swp; | ||
1116 | compat_ulong_t swap_attempts, swap_successes; | ||
1117 | }; | ||
1026 | 1118 | ||
1027 | /* check if shm_destroy() is tearing down shp */ | 1119 | static int copy_compat_shminfo_to_user(void __user *buf, struct shminfo64 *in, |
1028 | if (!ipc_valid_object(&shp->shm_perm)) { | 1120 | int version) |
1029 | err = -EIDRM; | 1121 | { |
1030 | goto out_unlock0; | 1122 | if (in->shmmax > INT_MAX) |
1031 | } | 1123 | in->shmmax = INT_MAX; |
1124 | if (version == IPC_64) { | ||
1125 | struct compat_shminfo64 info; | ||
1126 | memset(&info, 0, sizeof(info)); | ||
1127 | info.shmmax = in->shmmax; | ||
1128 | info.shmmin = in->shmmin; | ||
1129 | info.shmmni = in->shmmni; | ||
1130 | info.shmseg = in->shmseg; | ||
1131 | info.shmall = in->shmall; | ||
1132 | return copy_to_user(buf, &info, sizeof(info)); | ||
1133 | } else { | ||
1134 | struct shminfo info; | ||
1135 | memset(&info, 0, sizeof(info)); | ||
1136 | info.shmmax = in->shmmax; | ||
1137 | info.shmmin = in->shmmin; | ||
1138 | info.shmmni = in->shmmni; | ||
1139 | info.shmseg = in->shmseg; | ||
1140 | info.shmall = in->shmall; | ||
1141 | return copy_to_user(buf, &info, sizeof(info)); | ||
1142 | } | ||
1143 | } | ||
1032 | 1144 | ||
1033 | if (!ns_capable(ns->user_ns, CAP_IPC_LOCK)) { | 1145 | static int put_compat_shm_info(struct shm_info *ip, |
1034 | kuid_t euid = current_euid(); | 1146 | struct compat_shm_info __user *uip) |
1035 | 1147 | { | |
1036 | if (!uid_eq(euid, shp->shm_perm.uid) && | 1148 | struct compat_shm_info info; |
1037 | !uid_eq(euid, shp->shm_perm.cuid)) { | 1149 | |
1038 | err = -EPERM; | 1150 | memset(&info, 0, sizeof(info)); |
1039 | goto out_unlock0; | 1151 | info.used_ids = ip->used_ids; |
1040 | } | 1152 | info.shm_tot = ip->shm_tot; |
1041 | if (cmd == SHM_LOCK && !rlimit(RLIMIT_MEMLOCK)) { | 1153 | info.shm_rss = ip->shm_rss; |
1042 | err = -EPERM; | 1154 | info.shm_swp = ip->shm_swp; |
1043 | goto out_unlock0; | 1155 | info.swap_attempts = ip->swap_attempts; |
1044 | } | 1156 | info.swap_successes = ip->swap_successes; |
1045 | } | 1157 | return copy_to_user(up, &info, sizeof(info)); |
1158 | } | ||
1046 | 1159 | ||
1047 | shm_file = shp->shm_file; | 1160 | static int copy_compat_shmid_to_user(void __user *buf, struct shmid64_ds *in, |
1048 | if (is_file_hugepages(shm_file)) | 1161 | int version) |
1049 | goto out_unlock0; | 1162 | { |
1163 | if (version == IPC_64) { | ||
1164 | struct compat_shmid64_ds v; | ||
1165 | memset(&v, 0, sizeof(v)); | ||
1166 | to_compat_ipc64_perm(&v.shm_perm, &in->shm_perm); | ||
1167 | v.shm_atime = in->shm_atime; | ||
1168 | v.shm_dtime = in->shm_dtime; | ||
1169 | v.shm_ctime = in->shm_ctime; | ||
1170 | v.shm_segsz = in->shm_segsz; | ||
1171 | v.shm_nattch = in->shm_nattch; | ||
1172 | v.shm_cpid = in->shm_cpid; | ||
1173 | v.shm_lpid = in->shm_lpid; | ||
1174 | return copy_to_user(buf, &v, sizeof(v)); | ||
1175 | } else { | ||
1176 | struct compat_shmid_ds v; | ||
1177 | memset(&v, 0, sizeof(v)); | ||
1178 | to_compat_ipc_perm(&v.shm_perm, &in->shm_perm); | ||
1179 | v.shm_perm.key = in->shm_perm.key; | ||
1180 | v.shm_atime = in->shm_atime; | ||
1181 | v.shm_dtime = in->shm_dtime; | ||
1182 | v.shm_ctime = in->shm_ctime; | ||
1183 | v.shm_segsz = in->shm_segsz; | ||
1184 | v.shm_nattch = in->shm_nattch; | ||
1185 | v.shm_cpid = in->shm_cpid; | ||
1186 | v.shm_lpid = in->shm_lpid; | ||
1187 | return copy_to_user(buf, &v, sizeof(v)); | ||
1188 | } | ||
1189 | } | ||
1050 | 1190 | ||
1051 | if (cmd == SHM_LOCK) { | 1191 | static int copy_compat_shmid_from_user(struct shmid64_ds *out, void __user *buf, |
1052 | struct user_struct *user = current_user(); | 1192 | int version) |
1193 | { | ||
1194 | memset(out, 0, sizeof(*out)); | ||
1195 | if (version == IPC_64) { | ||
1196 | struct compat_shmid64_ds *p = buf; | ||
1197 | return get_compat_ipc64_perm(&out->shm_perm, &p->shm_perm); | ||
1198 | } else { | ||
1199 | struct compat_shmid_ds *p = buf; | ||
1200 | return get_compat_ipc_perm(&out->shm_perm, &p->shm_perm); | ||
1201 | } | ||
1202 | } | ||
1053 | 1203 | ||
1054 | err = shmem_lock(shm_file, 1, user); | 1204 | COMPAT_SYSCALL_DEFINE3(shmctl, int, shmid, int, cmd, void __user *, uptr) |
1055 | if (!err && !(shp->shm_perm.mode & SHM_LOCKED)) { | 1205 | { |
1056 | shp->shm_perm.mode |= SHM_LOCKED; | 1206 | struct ipc_namespace *ns; |
1057 | shp->mlock_user = user; | 1207 | struct shmid64_ds sem64; |
1058 | } | 1208 | int version = compat_ipc_parse_version(&cmd); |
1059 | goto out_unlock0; | 1209 | int err; |
1060 | } | ||
1061 | 1210 | ||
1062 | /* SHM_UNLOCK */ | 1211 | ns = current->nsproxy->ipc_ns; |
1063 | if (!(shp->shm_perm.mode & SHM_LOCKED)) | 1212 | |
1064 | goto out_unlock0; | 1213 | if (cmd < 0 || shmid < 0) |
1065 | shmem_lock(shm_file, 0, shp->mlock_user); | 1214 | return -EINVAL; |
1066 | shp->shm_perm.mode &= ~SHM_LOCKED; | ||
1067 | shp->mlock_user = NULL; | ||
1068 | get_file(shm_file); | ||
1069 | ipc_unlock_object(&shp->shm_perm); | ||
1070 | rcu_read_unlock(); | ||
1071 | shmem_unlock_mapping(shm_file->f_mapping); | ||
1072 | 1215 | ||
1073 | fput(shm_file); | 1216 | switch (cmd) { |
1217 | case IPC_INFO: { | ||
1218 | struct shminfo64 shminfo; | ||
1219 | err = shmctl_ipc_info(ns, &shminfo); | ||
1220 | if (err < 0) | ||
1221 | return err; | ||
1222 | if (copy_compat_shminfo_to_user(uptr, &shminfo, version)) | ||
1223 | err = -EFAULT; | ||
1224 | return err; | ||
1225 | } | ||
1226 | case SHM_INFO: { | ||
1227 | struct shm_info shm_info; | ||
1228 | err = shmctl_shm_info(ns, &shm_info); | ||
1229 | if (err < 0) | ||
1230 | return err; | ||
1231 | if (put_compat_shm_info(&shm_info, uptr)) | ||
1232 | err = -EFAULT; | ||
1074 | return err; | 1233 | return err; |
1075 | } | 1234 | } |
1235 | case IPC_STAT: | ||
1236 | case SHM_STAT: | ||
1237 | err = shmctl_stat(ns, shmid, cmd, &sem64); | ||
1238 | if (err < 0) | ||
1239 | return err; | ||
1240 | if (copy_compat_shmid_to_user(&sem64, uptr, version)) | ||
1241 | err = -EFAULT; | ||
1242 | return err; | ||
1243 | |||
1244 | case IPC_SET: | ||
1245 | if (copy_compat_shmid_from_user(&sem64, uptr, version)) | ||
1246 | return -EFAULT; | ||
1247 | /* fallthru */ | ||
1248 | case IPC_RMID: | ||
1249 | return shmctl_down(ns, shmid, cmd, &sem64); | ||
1250 | case SHM_LOCK: | ||
1251 | case SHM_UNLOCK: | ||
1252 | return shmctl_do_lock(ns, shmid, cmd); | ||
1253 | break; | ||
1076 | default: | 1254 | default: |
1077 | return -EINVAL; | 1255 | return -EINVAL; |
1078 | } | 1256 | } |
1079 | |||
1080 | out_unlock0: | ||
1081 | ipc_unlock_object(&shp->shm_perm); | ||
1082 | out_unlock1: | ||
1083 | rcu_read_unlock(); | ||
1084 | return err; | 1257 | return err; |
1085 | } | 1258 | } |
1259 | #endif | ||
1086 | 1260 | ||
1087 | /* | 1261 | /* |
1088 | * Fix shmaddr, allocate descriptor, map shm, add attach descriptor to lists. | 1262 | * Fix shmaddr, allocate descriptor, map shm, add attach descriptor to lists. |
@@ -1267,6 +1441,25 @@ SYSCALL_DEFINE3(shmat, int, shmid, char __user *, shmaddr, int, shmflg) | |||
1267 | return (long)ret; | 1441 | return (long)ret; |
1268 | } | 1442 | } |
1269 | 1443 | ||
1444 | #ifdef CONFIG_COMPAT | ||
1445 | |||
1446 | #ifndef COMPAT_SHMLBA | ||
1447 | #define COMPAT_SHMLBA SHMLBA | ||
1448 | #endif | ||
1449 | |||
1450 | COMPAT_SYSCALL_DEFINE3(shmat, int, shmid, compat_uptr_t, shmaddr, int, shmflg) | ||
1451 | { | ||
1452 | unsigned long ret; | ||
1453 | long err; | ||
1454 | |||
1455 | err = do_shmat(shmid, compat_ptr(shmaddr), shmflg, &ret, COMPAT_SHMLBA); | ||
1456 | if (err) | ||
1457 | return err; | ||
1458 | force_successful_syscall_return(); | ||
1459 | return (long)ret; | ||
1460 | } | ||
1461 | #endif | ||
1462 | |||
1270 | /* | 1463 | /* |
1271 | * detach and kill segment if marked destroyed. | 1464 | * detach and kill segment if marked destroyed. |
1272 | * The work is done in shm_close. | 1465 | * The work is done in shm_close. |
@@ -1397,7 +1590,7 @@ static int sysvipc_shm_proc_show(struct seq_file *s, void *it) | |||
1397 | 1590 | ||
1398 | seq_printf(s, | 1591 | seq_printf(s, |
1399 | "%10d %10d %4o " SIZE_SPEC " %5u %5u " | 1592 | "%10d %10d %4o " SIZE_SPEC " %5u %5u " |
1400 | "%5lu %5u %5u %5u %5u %10lu %10lu %10lu " | 1593 | "%5lu %5u %5u %5u %5u %10llu %10llu %10llu " |
1401 | SIZE_SPEC " " SIZE_SPEC "\n", | 1594 | SIZE_SPEC " " SIZE_SPEC "\n", |
1402 | shp->shm_perm.key, | 1595 | shp->shm_perm.key, |
1403 | shp->shm_perm.id, | 1596 | shp->shm_perm.id, |