aboutsummaryrefslogtreecommitdiffstats
path: root/ipc
diff options
context:
space:
mode:
authorAl Viro <viro@zeniv.linux.org.uk>2017-07-08 20:58:06 -0400
committerAl Viro <viro@zeniv.linux.org.uk>2017-07-15 20:46:41 -0400
commit9ba720c18622b250c0abeccbcea1b03531a92277 (patch)
tree947541ad7cbb1764691f9fd0ddbca4e5ee690faa /ipc
parent5771a8c08880cdca3bfb4a3fc6d309d6bba20877 (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.c347
1 files changed, 172 insertions, 175 deletions
diff --git a/ipc/shm.c b/ipc/shm.c
index 28a444861a8f..b4073c08d0e8 100644
--- a/ipc/shm.c
+++ b/ipc/shm.c
@@ -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 */
815static int shmctl_down(struct ipc_namespace *ns, int shmid, int cmd, 815static 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
871static int shmctl_nolock(struct ipc_namespace *ns, int shmid, 865static 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)); 884static 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; 904static 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
979out_unlock: 948out_unlock:
980 rcu_read_unlock(); 949 rcu_read_unlock();
981out:
982 return err; 950 return err;
983} 951}
984 952
985SYSCALL_DEFINE3(shmctl, int, shmid, int, cmd, struct shmid_ds __user *, buf) 953static 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
1078out_unlock0: 1022out_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
1029SYSCALL_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 *