diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2017-07-08 20:58:06 -0400 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2017-07-15 20:46:41 -0400 |
commit | 9ba720c18622b250c0abeccbcea1b03531a92277 (patch) | |
tree | 947541ad7cbb1764691f9fd0ddbca4e5ee690faa /ipc | |
parent | 5771a8c08880cdca3bfb4a3fc6d309d6bba20877 (diff) |
shmctl: split the work from copyin/copyout
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'ipc')
-rw-r--r-- | ipc/shm.c | 347 |
1 files changed, 172 insertions, 175 deletions
@@ -813,23 +813,17 @@ static void shm_get_stat(struct ipc_namespace *ns, unsigned long *rss, | |||
813 | * NOTE: no locks must be held, the rwsem is taken inside this function. | 813 | * NOTE: no locks must be held, the rwsem is taken inside this function. |
814 | */ | 814 | */ |
815 | static int shmctl_down(struct ipc_namespace *ns, int shmid, int cmd, | 815 | static int shmctl_down(struct ipc_namespace *ns, int shmid, int cmd, |
816 | struct shmid_ds __user *buf, int version) | 816 | struct shmid64_ds *shmid64) |
817 | { | 817 | { |
818 | struct kern_ipc_perm *ipcp; | 818 | struct kern_ipc_perm *ipcp; |
819 | struct shmid64_ds shmid64; | ||
820 | struct shmid_kernel *shp; | 819 | struct shmid_kernel *shp; |
821 | int err; | 820 | int err; |
822 | 821 | ||
823 | if (cmd == IPC_SET) { | ||
824 | if (copy_shmid_from_user(&shmid64, buf, version)) | ||
825 | return -EFAULT; | ||
826 | } | ||
827 | |||
828 | down_write(&shm_ids(ns).rwsem); | 822 | down_write(&shm_ids(ns).rwsem); |
829 | rcu_read_lock(); | 823 | rcu_read_lock(); |
830 | 824 | ||
831 | ipcp = ipcctl_pre_down_nolock(ns, &shm_ids(ns), shmid, cmd, | 825 | ipcp = ipcctl_pre_down_nolock(ns, &shm_ids(ns), shmid, cmd, |
832 | &shmid64.shm_perm, 0); | 826 | &shmid64->shm_perm, 0); |
833 | if (IS_ERR(ipcp)) { | 827 | if (IS_ERR(ipcp)) { |
834 | err = PTR_ERR(ipcp); | 828 | err = PTR_ERR(ipcp); |
835 | goto out_unlock1; | 829 | goto out_unlock1; |
@@ -849,7 +843,7 @@ static int shmctl_down(struct ipc_namespace *ns, int shmid, int cmd, | |||
849 | goto out_up; | 843 | goto out_up; |
850 | case IPC_SET: | 844 | case IPC_SET: |
851 | ipc_lock_object(&shp->shm_perm); | 845 | ipc_lock_object(&shp->shm_perm); |
852 | err = ipc_update_perm(&shmid64.shm_perm, ipcp); | 846 | err = ipc_update_perm(&shmid64->shm_perm, ipcp); |
853 | if (err) | 847 | if (err) |
854 | goto out_unlock0; | 848 | goto out_unlock0; |
855 | shp->shm_ctim = get_seconds(); | 849 | shp->shm_ctim = get_seconds(); |
@@ -868,212 +862,162 @@ out_up: | |||
868 | return err; | 862 | return err; |
869 | } | 863 | } |
870 | 864 | ||
871 | static int shmctl_nolock(struct ipc_namespace *ns, int shmid, | 865 | static int shmctl_ipc_info(struct ipc_namespace *ns, |
872 | int cmd, int version, void __user *buf) | 866 | struct shminfo64 *shminfo) |
873 | { | 867 | { |
874 | int err; | 868 | int err = security_shm_shmctl(NULL, IPC_INFO); |
875 | struct shmid_kernel *shp; | 869 | if (!err) { |
876 | 870 | memset(shminfo, 0, sizeof(*shminfo)); | |
877 | /* preliminary security checks for *_INFO */ | 871 | shminfo->shmmni = shminfo->shmseg = ns->shm_ctlmni; |
878 | if (cmd == IPC_INFO || cmd == SHM_INFO) { | 872 | shminfo->shmmax = ns->shm_ctlmax; |
879 | err = security_shm_shmctl(NULL, cmd); | 873 | shminfo->shmall = ns->shm_ctlall; |
880 | if (err) | 874 | shminfo->shmmin = SHMMIN; |
881 | return err; | ||
882 | } | ||
883 | |||
884 | switch (cmd) { | ||
885 | case IPC_INFO: | ||
886 | { | ||
887 | struct shminfo64 shminfo; | ||
888 | |||
889 | memset(&shminfo, 0, sizeof(shminfo)); | ||
890 | shminfo.shmmni = shminfo.shmseg = ns->shm_ctlmni; | ||
891 | shminfo.shmmax = ns->shm_ctlmax; | ||
892 | shminfo.shmall = ns->shm_ctlall; | ||
893 | |||
894 | shminfo.shmmin = SHMMIN; | ||
895 | if (copy_shminfo_to_user(buf, &shminfo, version)) | ||
896 | return -EFAULT; | ||
897 | |||
898 | down_read(&shm_ids(ns).rwsem); | 875 | down_read(&shm_ids(ns).rwsem); |
899 | err = ipc_get_maxid(&shm_ids(ns)); | 876 | err = ipc_get_maxid(&shm_ids(ns)); |
900 | up_read(&shm_ids(ns).rwsem); | 877 | up_read(&shm_ids(ns).rwsem); |
901 | |||
902 | if (err < 0) | 878 | if (err < 0) |
903 | err = 0; | 879 | err = 0; |
904 | goto out; | ||
905 | } | 880 | } |
906 | case SHM_INFO: | 881 | return err; |
907 | { | 882 | } |
908 | struct shm_info shm_info; | ||
909 | 883 | ||
910 | memset(&shm_info, 0, sizeof(shm_info)); | 884 | static int shmctl_shm_info(struct ipc_namespace *ns, |
885 | struct shm_info *shm_info) | ||
886 | { | ||
887 | int err = security_shm_shmctl(NULL, SHM_INFO); | ||
888 | if (!err) { | ||
889 | memset(shm_info, 0, sizeof(*shm_info)); | ||
911 | down_read(&shm_ids(ns).rwsem); | 890 | down_read(&shm_ids(ns).rwsem); |
912 | shm_info.used_ids = shm_ids(ns).in_use; | 891 | shm_info->used_ids = shm_ids(ns).in_use; |
913 | shm_get_stat(ns, &shm_info.shm_rss, &shm_info.shm_swp); | 892 | shm_get_stat(ns, &shm_info->shm_rss, &shm_info->shm_swp); |
914 | shm_info.shm_tot = ns->shm_tot; | 893 | shm_info->shm_tot = ns->shm_tot; |
915 | shm_info.swap_attempts = 0; | 894 | shm_info->swap_attempts = 0; |
916 | shm_info.swap_successes = 0; | 895 | shm_info->swap_successes = 0; |
917 | err = ipc_get_maxid(&shm_ids(ns)); | 896 | err = ipc_get_maxid(&shm_ids(ns)); |
918 | up_read(&shm_ids(ns).rwsem); | 897 | up_read(&shm_ids(ns).rwsem); |
919 | if (copy_to_user(buf, &shm_info, sizeof(shm_info))) { | 898 | if (err < 0) |
920 | err = -EFAULT; | 899 | err = 0; |
921 | goto out; | ||
922 | } | ||
923 | |||
924 | err = err < 0 ? 0 : err; | ||
925 | goto out; | ||
926 | } | 900 | } |
927 | case SHM_STAT: | 901 | return err; |
928 | case IPC_STAT: | 902 | } |
929 | { | ||
930 | struct shmid64_ds tbuf; | ||
931 | int result; | ||
932 | |||
933 | rcu_read_lock(); | ||
934 | if (cmd == SHM_STAT) { | ||
935 | shp = shm_obtain_object(ns, shmid); | ||
936 | if (IS_ERR(shp)) { | ||
937 | err = PTR_ERR(shp); | ||
938 | goto out_unlock; | ||
939 | } | ||
940 | result = shp->shm_perm.id; | ||
941 | } else { | ||
942 | shp = shm_obtain_object_check(ns, shmid); | ||
943 | if (IS_ERR(shp)) { | ||
944 | err = PTR_ERR(shp); | ||
945 | goto out_unlock; | ||
946 | } | ||
947 | result = 0; | ||
948 | } | ||
949 | 903 | ||
950 | err = -EACCES; | 904 | static int shmctl_stat(struct ipc_namespace *ns, int shmid, |
951 | if (ipcperms(ns, &shp->shm_perm, S_IRUGO)) | 905 | int cmd, struct shmid64_ds *tbuf) |
952 | goto out_unlock; | 906 | { |
907 | struct shmid_kernel *shp; | ||
908 | int result; | ||
909 | int err; | ||
953 | 910 | ||
954 | err = security_shm_shmctl(shp, cmd); | 911 | rcu_read_lock(); |
955 | if (err) | 912 | if (cmd == SHM_STAT) { |
913 | shp = shm_obtain_object(ns, shmid); | ||
914 | if (IS_ERR(shp)) { | ||
915 | err = PTR_ERR(shp); | ||
956 | goto out_unlock; | 916 | goto out_unlock; |
917 | } | ||
918 | result = shp->shm_perm.id; | ||
919 | } else { | ||
920 | shp = shm_obtain_object_check(ns, shmid); | ||
921 | if (IS_ERR(shp)) { | ||
922 | err = PTR_ERR(shp); | ||
923 | goto out_unlock; | ||
924 | } | ||
925 | result = 0; | ||
926 | } | ||
957 | 927 | ||
958 | memset(&tbuf, 0, sizeof(tbuf)); | 928 | err = -EACCES; |
959 | kernel_to_ipc64_perm(&shp->shm_perm, &tbuf.shm_perm); | 929 | if (ipcperms(ns, &shp->shm_perm, S_IRUGO)) |
960 | tbuf.shm_segsz = shp->shm_segsz; | 930 | goto out_unlock; |
961 | tbuf.shm_atime = shp->shm_atim; | ||
962 | tbuf.shm_dtime = shp->shm_dtim; | ||
963 | tbuf.shm_ctime = shp->shm_ctim; | ||
964 | tbuf.shm_cpid = shp->shm_cprid; | ||
965 | tbuf.shm_lpid = shp->shm_lprid; | ||
966 | tbuf.shm_nattch = shp->shm_nattch; | ||
967 | rcu_read_unlock(); | ||
968 | 931 | ||
969 | if (copy_shmid_to_user(buf, &tbuf, version)) | 932 | err = security_shm_shmctl(shp, cmd); |
970 | err = -EFAULT; | 933 | if (err) |
971 | else | 934 | goto out_unlock; |
972 | err = result; | 935 | |
973 | goto out; | 936 | memset(tbuf, 0, sizeof(*tbuf)); |
974 | } | 937 | kernel_to_ipc64_perm(&shp->shm_perm, &tbuf->shm_perm); |
975 | default: | 938 | tbuf->shm_segsz = shp->shm_segsz; |
976 | return -EINVAL; | 939 | tbuf->shm_atime = shp->shm_atim; |
977 | } | 940 | tbuf->shm_dtime = shp->shm_dtim; |
941 | tbuf->shm_ctime = shp->shm_ctim; | ||
942 | tbuf->shm_cpid = shp->shm_cprid; | ||
943 | tbuf->shm_lpid = shp->shm_lprid; | ||
944 | tbuf->shm_nattch = shp->shm_nattch; | ||
945 | rcu_read_unlock(); | ||
946 | return result; | ||
978 | 947 | ||
979 | out_unlock: | 948 | out_unlock: |
980 | rcu_read_unlock(); | 949 | rcu_read_unlock(); |
981 | out: | ||
982 | return err; | 950 | return err; |
983 | } | 951 | } |
984 | 952 | ||
985 | SYSCALL_DEFINE3(shmctl, int, shmid, int, cmd, struct shmid_ds __user *, buf) | 953 | static int shmctl_do_lock(struct ipc_namespace *ns, int shmid, int cmd) |
986 | { | 954 | { |
987 | struct shmid_kernel *shp; | 955 | struct shmid_kernel *shp; |
988 | int err, version; | 956 | struct file *shm_file; |
989 | struct ipc_namespace *ns; | 957 | int err; |
990 | |||
991 | if (cmd < 0 || shmid < 0) | ||
992 | return -EINVAL; | ||
993 | 958 | ||
994 | version = ipc_parse_version(&cmd); | 959 | rcu_read_lock(); |
995 | ns = current->nsproxy->ipc_ns; | 960 | shp = shm_obtain_object_check(ns, shmid); |
961 | if (IS_ERR(shp)) { | ||
962 | err = PTR_ERR(shp); | ||
963 | goto out_unlock1; | ||
964 | } | ||
996 | 965 | ||
997 | switch (cmd) { | 966 | audit_ipc_obj(&(shp->shm_perm)); |
998 | case IPC_INFO: | 967 | err = security_shm_shmctl(shp, cmd); |
999 | case SHM_INFO: | 968 | if (err) |
1000 | case SHM_STAT: | 969 | goto out_unlock1; |
1001 | case IPC_STAT: | ||
1002 | return shmctl_nolock(ns, shmid, cmd, version, buf); | ||
1003 | case IPC_RMID: | ||
1004 | case IPC_SET: | ||
1005 | return shmctl_down(ns, shmid, cmd, buf, version); | ||
1006 | case SHM_LOCK: | ||
1007 | case SHM_UNLOCK: | ||
1008 | { | ||
1009 | struct file *shm_file; | ||
1010 | 970 | ||
1011 | rcu_read_lock(); | 971 | ipc_lock_object(&shp->shm_perm); |
1012 | shp = shm_obtain_object_check(ns, shmid); | ||
1013 | if (IS_ERR(shp)) { | ||
1014 | err = PTR_ERR(shp); | ||
1015 | goto out_unlock1; | ||
1016 | } | ||
1017 | 972 | ||
1018 | audit_ipc_obj(&(shp->shm_perm)); | 973 | /* check if shm_destroy() is tearing down shp */ |
1019 | err = security_shm_shmctl(shp, cmd); | 974 | if (!ipc_valid_object(&shp->shm_perm)) { |
1020 | if (err) | 975 | err = -EIDRM; |
1021 | goto out_unlock1; | 976 | goto out_unlock0; |
977 | } | ||
1022 | 978 | ||
1023 | ipc_lock_object(&shp->shm_perm); | 979 | if (!ns_capable(ns->user_ns, CAP_IPC_LOCK)) { |
980 | kuid_t euid = current_euid(); | ||
1024 | 981 | ||
1025 | /* check if shm_destroy() is tearing down shp */ | 982 | if (!uid_eq(euid, shp->shm_perm.uid) && |
1026 | if (!ipc_valid_object(&shp->shm_perm)) { | 983 | !uid_eq(euid, shp->shm_perm.cuid)) { |
1027 | err = -EIDRM; | 984 | err = -EPERM; |
1028 | goto out_unlock0; | 985 | goto out_unlock0; |
1029 | } | 986 | } |
1030 | 987 | if (cmd == SHM_LOCK && !rlimit(RLIMIT_MEMLOCK)) { | |
1031 | if (!ns_capable(ns->user_ns, CAP_IPC_LOCK)) { | 988 | err = -EPERM; |
1032 | kuid_t euid = current_euid(); | 989 | goto out_unlock0; |
1033 | |||
1034 | if (!uid_eq(euid, shp->shm_perm.uid) && | ||
1035 | !uid_eq(euid, shp->shm_perm.cuid)) { | ||
1036 | err = -EPERM; | ||
1037 | goto out_unlock0; | ||
1038 | } | ||
1039 | if (cmd == SHM_LOCK && !rlimit(RLIMIT_MEMLOCK)) { | ||
1040 | err = -EPERM; | ||
1041 | goto out_unlock0; | ||
1042 | } | ||
1043 | } | 990 | } |
991 | } | ||
1044 | 992 | ||
1045 | shm_file = shp->shm_file; | 993 | shm_file = shp->shm_file; |
1046 | if (is_file_hugepages(shm_file)) | 994 | if (is_file_hugepages(shm_file)) |
1047 | goto out_unlock0; | 995 | goto out_unlock0; |
1048 | 996 | ||
1049 | if (cmd == SHM_LOCK) { | 997 | if (cmd == SHM_LOCK) { |
1050 | struct user_struct *user = current_user(); | 998 | struct user_struct *user = current_user(); |
1051 | 999 | ||
1052 | err = shmem_lock(shm_file, 1, user); | 1000 | err = shmem_lock(shm_file, 1, user); |
1053 | if (!err && !(shp->shm_perm.mode & SHM_LOCKED)) { | 1001 | if (!err && !(shp->shm_perm.mode & SHM_LOCKED)) { |
1054 | shp->shm_perm.mode |= SHM_LOCKED; | 1002 | shp->shm_perm.mode |= SHM_LOCKED; |
1055 | shp->mlock_user = user; | 1003 | shp->mlock_user = user; |
1056 | } | ||
1057 | goto out_unlock0; | ||
1058 | } | 1004 | } |
1005 | goto out_unlock0; | ||
1006 | } | ||
1059 | 1007 | ||
1060 | /* SHM_UNLOCK */ | 1008 | /* SHM_UNLOCK */ |
1061 | if (!(shp->shm_perm.mode & SHM_LOCKED)) | 1009 | if (!(shp->shm_perm.mode & SHM_LOCKED)) |
1062 | goto out_unlock0; | 1010 | goto out_unlock0; |
1063 | shmem_lock(shm_file, 0, shp->mlock_user); | 1011 | shmem_lock(shm_file, 0, shp->mlock_user); |
1064 | shp->shm_perm.mode &= ~SHM_LOCKED; | 1012 | shp->shm_perm.mode &= ~SHM_LOCKED; |
1065 | shp->mlock_user = NULL; | 1013 | shp->mlock_user = NULL; |
1066 | get_file(shm_file); | 1014 | get_file(shm_file); |
1067 | ipc_unlock_object(&shp->shm_perm); | 1015 | ipc_unlock_object(&shp->shm_perm); |
1068 | rcu_read_unlock(); | 1016 | rcu_read_unlock(); |
1069 | shmem_unlock_mapping(shm_file->f_mapping); | 1017 | shmem_unlock_mapping(shm_file->f_mapping); |
1070 | 1018 | ||
1071 | fput(shm_file); | 1019 | fput(shm_file); |
1072 | return err; | 1020 | return err; |
1073 | } | ||
1074 | default: | ||
1075 | return -EINVAL; | ||
1076 | } | ||
1077 | 1021 | ||
1078 | out_unlock0: | 1022 | out_unlock0: |
1079 | ipc_unlock_object(&shp->shm_perm); | 1023 | ipc_unlock_object(&shp->shm_perm); |
@@ -1082,6 +1026,59 @@ out_unlock1: | |||
1082 | return err; | 1026 | return err; |
1083 | } | 1027 | } |
1084 | 1028 | ||
1029 | SYSCALL_DEFINE3(shmctl, int, shmid, int, cmd, struct shmid_ds __user *, buf) | ||
1030 | { | ||
1031 | int err, version; | ||
1032 | struct ipc_namespace *ns; | ||
1033 | struct shmid64_ds tbuf; | ||
1034 | |||
1035 | if (cmd < 0 || shmid < 0) | ||
1036 | return -EINVAL; | ||
1037 | |||
1038 | version = ipc_parse_version(&cmd); | ||
1039 | ns = current->nsproxy->ipc_ns; | ||
1040 | |||
1041 | switch (cmd) { | ||
1042 | case IPC_INFO: { | ||
1043 | struct shminfo64 shminfo; | ||
1044 | err = shmctl_ipc_info(ns, &shminfo); | ||
1045 | if (err < 0) | ||
1046 | return err; | ||
1047 | if (copy_shminfo_to_user(buf, &shminfo, version)) | ||
1048 | err = -EFAULT; | ||
1049 | return err; | ||
1050 | } | ||
1051 | case SHM_INFO: { | ||
1052 | struct shm_info shm_info; | ||
1053 | err = shmctl_shm_info(ns, &shm_info); | ||
1054 | if (err < 0) | ||
1055 | return err; | ||
1056 | if (copy_to_user(buf, &shm_info, sizeof(shm_info))) | ||
1057 | err = -EFAULT; | ||
1058 | return err; | ||
1059 | } | ||
1060 | case SHM_STAT: | ||
1061 | case IPC_STAT: { | ||
1062 | err = shmctl_stat(ns, shmid, cmd, &tbuf); | ||
1063 | if (err < 0) | ||
1064 | return err; | ||
1065 | if (copy_shmid_to_user(buf, &tbuf, version)) | ||
1066 | err = -EFAULT; | ||
1067 | return err; | ||
1068 | } | ||
1069 | case IPC_SET: | ||
1070 | if (copy_shmid_from_user(&tbuf, buf, version)) | ||
1071 | return -EFAULT; | ||
1072 | case IPC_RMID: | ||
1073 | return shmctl_down(ns, shmid, cmd, &tbuf); | ||
1074 | case SHM_LOCK: | ||
1075 | case SHM_UNLOCK: | ||
1076 | return shmctl_do_lock(ns, shmid, cmd); | ||
1077 | default: | ||
1078 | return -EINVAL; | ||
1079 | } | ||
1080 | } | ||
1081 | |||
1085 | /* | 1082 | /* |
1086 | * Fix shmaddr, allocate descriptor, map shm, add attach descriptor to lists. | 1083 | * Fix shmaddr, allocate descriptor, map shm, add attach descriptor to lists. |
1087 | * | 1084 | * |