shmctl: split the work from copyin/copyout

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>

Al Viro 9ba720c1 5771a8c0

+178 -181
+178 -181
ipc/shm.c
··· 813 813 * NOTE: no locks must be held, the rwsem is taken inside this function. 814 814 */ 815 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 818 struct kern_ipc_perm *ipcp; 819 - struct shmid64_ds shmid64; 820 819 struct shmid_kernel *shp; 821 820 int err; 822 - 823 - if (cmd == IPC_SET) { 824 - if (copy_shmid_from_user(&shmid64, buf, version)) 825 - return -EFAULT; 826 - } 827 821 828 822 down_write(&shm_ids(ns).rwsem); 829 823 rcu_read_lock(); 830 824 831 825 ipcp = ipcctl_pre_down_nolock(ns, &shm_ids(ns), shmid, cmd, 832 - &shmid64.shm_perm, 0); 826 + &shmid64->shm_perm, 0); 833 827 if (IS_ERR(ipcp)) { 834 828 err = PTR_ERR(ipcp); 835 829 goto out_unlock1; ··· 843 849 goto out_up; 844 850 case IPC_SET: 845 851 ipc_lock_object(&shp->shm_perm); 846 - err = ipc_update_perm(&shmid64.shm_perm, ipcp); 852 + err = ipc_update_perm(&shmid64->shm_perm, ipcp); 847 853 if (err) 848 854 goto out_unlock0; 849 855 shp->shm_ctim = get_seconds(); ··· 862 868 return err; 863 869 } 864 870 865 - static int shmctl_nolock(struct ipc_namespace *ns, int shmid, 866 - int cmd, int version, void __user *buf) 871 + static int shmctl_ipc_info(struct ipc_namespace *ns, 872 + struct shminfo64 *shminfo) 867 873 { 868 - int err; 869 - struct shmid_kernel *shp; 870 - 871 - /* preliminary security checks for *_INFO */ 872 - if (cmd == IPC_INFO || cmd == SHM_INFO) { 873 - err = security_shm_shmctl(NULL, cmd); 874 - if (err) 875 - return err; 876 - } 877 - 878 - switch (cmd) { 879 - case IPC_INFO: 880 - { 881 - struct shminfo64 shminfo; 882 - 883 - memset(&shminfo, 0, sizeof(shminfo)); 884 - shminfo.shmmni = shminfo.shmseg = ns->shm_ctlmni; 885 - shminfo.shmmax = ns->shm_ctlmax; 886 - shminfo.shmall = ns->shm_ctlall; 887 - 888 - shminfo.shmmin = SHMMIN; 889 - if (copy_shminfo_to_user(buf, &shminfo, version)) 890 - return -EFAULT; 891 - 874 + int err = security_shm_shmctl(NULL, IPC_INFO); 875 + if (!err) { 876 + memset(shminfo, 0, sizeof(*shminfo)); 877 + shminfo->shmmni = shminfo->shmseg = ns->shm_ctlmni; 878 + shminfo->shmmax = ns->shm_ctlmax; 879 + shminfo->shmall = ns->shm_ctlall; 880 + shminfo->shmmin = SHMMIN; 892 881 down_read(&shm_ids(ns).rwsem); 893 882 err = ipc_get_maxid(&shm_ids(ns)); 894 883 up_read(&shm_ids(ns).rwsem); 895 - 896 884 if (err < 0) 897 885 err = 0; 898 - goto out; 899 886 } 900 - case SHM_INFO: 901 - { 902 - struct shm_info shm_info; 887 + return err; 888 + } 903 889 904 - memset(&shm_info, 0, sizeof(shm_info)); 890 + static int shmctl_shm_info(struct ipc_namespace *ns, 891 + struct shm_info *shm_info) 892 + { 893 + int err = security_shm_shmctl(NULL, SHM_INFO); 894 + if (!err) { 895 + memset(shm_info, 0, sizeof(*shm_info)); 905 896 down_read(&shm_ids(ns).rwsem); 906 - shm_info.used_ids = shm_ids(ns).in_use; 907 - shm_get_stat(ns, &shm_info.shm_rss, &shm_info.shm_swp); 908 - shm_info.shm_tot = ns->shm_tot; 909 - shm_info.swap_attempts = 0; 910 - shm_info.swap_successes = 0; 897 + shm_info->used_ids = shm_ids(ns).in_use; 898 + shm_get_stat(ns, &shm_info->shm_rss, &shm_info->shm_swp); 899 + shm_info->shm_tot = ns->shm_tot; 900 + shm_info->swap_attempts = 0; 901 + shm_info->swap_successes = 0; 911 902 err = ipc_get_maxid(&shm_ids(ns)); 912 903 up_read(&shm_ids(ns).rwsem); 913 - if (copy_to_user(buf, &shm_info, sizeof(shm_info))) { 914 - err = -EFAULT; 915 - goto out; 916 - } 917 - 918 - err = err < 0 ? 0 : err; 919 - goto out; 904 + if (err < 0) 905 + err = 0; 920 906 } 921 - case SHM_STAT: 922 - case IPC_STAT: 923 - { 924 - struct shmid64_ds tbuf; 925 - int result; 907 + return err; 908 + } 926 909 927 - rcu_read_lock(); 928 - if (cmd == SHM_STAT) { 929 - shp = shm_obtain_object(ns, shmid); 930 - if (IS_ERR(shp)) { 931 - err = PTR_ERR(shp); 932 - goto out_unlock; 933 - } 934 - result = shp->shm_perm.id; 935 - } else { 936 - shp = shm_obtain_object_check(ns, shmid); 937 - if (IS_ERR(shp)) { 938 - err = PTR_ERR(shp); 939 - goto out_unlock; 940 - } 941 - result = 0; 942 - } 910 + static int shmctl_stat(struct ipc_namespace *ns, int shmid, 911 + int cmd, struct shmid64_ds *tbuf) 912 + { 913 + struct shmid_kernel *shp; 914 + int result; 915 + int err; 943 916 944 - err = -EACCES; 945 - if (ipcperms(ns, &shp->shm_perm, S_IRUGO)) 917 + rcu_read_lock(); 918 + if (cmd == SHM_STAT) { 919 + shp = shm_obtain_object(ns, shmid); 920 + if (IS_ERR(shp)) { 921 + err = PTR_ERR(shp); 946 922 goto out_unlock; 947 - 948 - err = security_shm_shmctl(shp, cmd); 949 - if (err) 923 + } 924 + result = shp->shm_perm.id; 925 + } else { 926 + shp = shm_obtain_object_check(ns, shmid); 927 + if (IS_ERR(shp)) { 928 + err = PTR_ERR(shp); 950 929 goto out_unlock; 951 - 952 - memset(&tbuf, 0, sizeof(tbuf)); 953 - kernel_to_ipc64_perm(&shp->shm_perm, &tbuf.shm_perm); 954 - tbuf.shm_segsz = shp->shm_segsz; 955 - tbuf.shm_atime = shp->shm_atim; 956 - tbuf.shm_dtime = shp->shm_dtim; 957 - tbuf.shm_ctime = shp->shm_ctim; 958 - tbuf.shm_cpid = shp->shm_cprid; 959 - tbuf.shm_lpid = shp->shm_lprid; 960 - tbuf.shm_nattch = shp->shm_nattch; 961 - rcu_read_unlock(); 962 - 963 - if (copy_shmid_to_user(buf, &tbuf, version)) 964 - err = -EFAULT; 965 - else 966 - err = result; 967 - goto out; 930 + } 931 + result = 0; 968 932 } 969 - default: 970 - return -EINVAL; 971 - } 933 + 934 + err = -EACCES; 935 + if (ipcperms(ns, &shp->shm_perm, S_IRUGO)) 936 + goto out_unlock; 937 + 938 + err = security_shm_shmctl(shp, cmd); 939 + if (err) 940 + goto out_unlock; 941 + 942 + memset(tbuf, 0, sizeof(*tbuf)); 943 + kernel_to_ipc64_perm(&shp->shm_perm, &tbuf->shm_perm); 944 + tbuf->shm_segsz = shp->shm_segsz; 945 + tbuf->shm_atime = shp->shm_atim; 946 + tbuf->shm_dtime = shp->shm_dtim; 947 + tbuf->shm_ctime = shp->shm_ctim; 948 + tbuf->shm_cpid = shp->shm_cprid; 949 + tbuf->shm_lpid = shp->shm_lprid; 950 + tbuf->shm_nattch = shp->shm_nattch; 951 + rcu_read_unlock(); 952 + return result; 972 953 973 954 out_unlock: 974 955 rcu_read_unlock(); 975 - out: 956 + return err; 957 + } 958 + 959 + static int shmctl_do_lock(struct ipc_namespace *ns, int shmid, int cmd) 960 + { 961 + struct shmid_kernel *shp; 962 + struct file *shm_file; 963 + int err; 964 + 965 + rcu_read_lock(); 966 + shp = shm_obtain_object_check(ns, shmid); 967 + if (IS_ERR(shp)) { 968 + err = PTR_ERR(shp); 969 + goto out_unlock1; 970 + } 971 + 972 + audit_ipc_obj(&(shp->shm_perm)); 973 + err = security_shm_shmctl(shp, cmd); 974 + if (err) 975 + goto out_unlock1; 976 + 977 + ipc_lock_object(&shp->shm_perm); 978 + 979 + /* check if shm_destroy() is tearing down shp */ 980 + if (!ipc_valid_object(&shp->shm_perm)) { 981 + err = -EIDRM; 982 + goto out_unlock0; 983 + } 984 + 985 + if (!ns_capable(ns->user_ns, CAP_IPC_LOCK)) { 986 + kuid_t euid = current_euid(); 987 + 988 + if (!uid_eq(euid, shp->shm_perm.uid) && 989 + !uid_eq(euid, shp->shm_perm.cuid)) { 990 + err = -EPERM; 991 + goto out_unlock0; 992 + } 993 + if (cmd == SHM_LOCK && !rlimit(RLIMIT_MEMLOCK)) { 994 + err = -EPERM; 995 + goto out_unlock0; 996 + } 997 + } 998 + 999 + shm_file = shp->shm_file; 1000 + if (is_file_hugepages(shm_file)) 1001 + goto out_unlock0; 1002 + 1003 + if (cmd == SHM_LOCK) { 1004 + struct user_struct *user = current_user(); 1005 + 1006 + err = shmem_lock(shm_file, 1, user); 1007 + if (!err && !(shp->shm_perm.mode & SHM_LOCKED)) { 1008 + shp->shm_perm.mode |= SHM_LOCKED; 1009 + shp->mlock_user = user; 1010 + } 1011 + goto out_unlock0; 1012 + } 1013 + 1014 + /* SHM_UNLOCK */ 1015 + if (!(shp->shm_perm.mode & SHM_LOCKED)) 1016 + goto out_unlock0; 1017 + shmem_lock(shm_file, 0, shp->mlock_user); 1018 + shp->shm_perm.mode &= ~SHM_LOCKED; 1019 + shp->mlock_user = NULL; 1020 + get_file(shm_file); 1021 + ipc_unlock_object(&shp->shm_perm); 1022 + rcu_read_unlock(); 1023 + shmem_unlock_mapping(shm_file->f_mapping); 1024 + 1025 + fput(shm_file); 1026 + return err; 1027 + 1028 + out_unlock0: 1029 + ipc_unlock_object(&shp->shm_perm); 1030 + out_unlock1: 1031 + rcu_read_unlock(); 976 1032 return err; 977 1033 } 978 1034 979 1035 SYSCALL_DEFINE3(shmctl, int, shmid, int, cmd, struct shmid_ds __user *, buf) 980 1036 { 981 - struct shmid_kernel *shp; 982 1037 int err, version; 983 1038 struct ipc_namespace *ns; 1039 + struct shmid64_ds tbuf; 984 1040 985 1041 if (cmd < 0 || shmid < 0) 986 1042 return -EINVAL; ··· 1039 995 ns = current->nsproxy->ipc_ns; 1040 996 1041 997 switch (cmd) { 1042 - case IPC_INFO: 1043 - case SHM_INFO: 1044 - case SHM_STAT: 1045 - case IPC_STAT: 1046 - return shmctl_nolock(ns, shmid, cmd, version, buf); 1047 - case IPC_RMID: 1048 - case IPC_SET: 1049 - return shmctl_down(ns, shmid, cmd, buf, version); 1050 - case SHM_LOCK: 1051 - case SHM_UNLOCK: 1052 - { 1053 - struct file *shm_file; 1054 - 1055 - rcu_read_lock(); 1056 - shp = shm_obtain_object_check(ns, shmid); 1057 - if (IS_ERR(shp)) { 1058 - err = PTR_ERR(shp); 1059 - goto out_unlock1; 1060 - } 1061 - 1062 - audit_ipc_obj(&(shp->shm_perm)); 1063 - err = security_shm_shmctl(shp, cmd); 1064 - if (err) 1065 - goto out_unlock1; 1066 - 1067 - ipc_lock_object(&shp->shm_perm); 1068 - 1069 - /* check if shm_destroy() is tearing down shp */ 1070 - if (!ipc_valid_object(&shp->shm_perm)) { 1071 - err = -EIDRM; 1072 - goto out_unlock0; 1073 - } 1074 - 1075 - if (!ns_capable(ns->user_ns, CAP_IPC_LOCK)) { 1076 - kuid_t euid = current_euid(); 1077 - 1078 - if (!uid_eq(euid, shp->shm_perm.uid) && 1079 - !uid_eq(euid, shp->shm_perm.cuid)) { 1080 - err = -EPERM; 1081 - goto out_unlock0; 1082 - } 1083 - if (cmd == SHM_LOCK && !rlimit(RLIMIT_MEMLOCK)) { 1084 - err = -EPERM; 1085 - goto out_unlock0; 1086 - } 1087 - } 1088 - 1089 - shm_file = shp->shm_file; 1090 - if (is_file_hugepages(shm_file)) 1091 - goto out_unlock0; 1092 - 1093 - if (cmd == SHM_LOCK) { 1094 - struct user_struct *user = current_user(); 1095 - 1096 - err = shmem_lock(shm_file, 1, user); 1097 - if (!err && !(shp->shm_perm.mode & SHM_LOCKED)) { 1098 - shp->shm_perm.mode |= SHM_LOCKED; 1099 - shp->mlock_user = user; 1100 - } 1101 - goto out_unlock0; 1102 - } 1103 - 1104 - /* SHM_UNLOCK */ 1105 - if (!(shp->shm_perm.mode & SHM_LOCKED)) 1106 - goto out_unlock0; 1107 - shmem_lock(shm_file, 0, shp->mlock_user); 1108 - shp->shm_perm.mode &= ~SHM_LOCKED; 1109 - shp->mlock_user = NULL; 1110 - get_file(shm_file); 1111 - ipc_unlock_object(&shp->shm_perm); 1112 - rcu_read_unlock(); 1113 - shmem_unlock_mapping(shm_file->f_mapping); 1114 - 1115 - fput(shm_file); 998 + case IPC_INFO: { 999 + struct shminfo64 shminfo; 1000 + err = shmctl_ipc_info(ns, &shminfo); 1001 + if (err < 0) 1002 + return err; 1003 + if (copy_shminfo_to_user(buf, &shminfo, version)) 1004 + err = -EFAULT; 1116 1005 return err; 1117 1006 } 1007 + case SHM_INFO: { 1008 + struct shm_info shm_info; 1009 + err = shmctl_shm_info(ns, &shm_info); 1010 + if (err < 0) 1011 + return err; 1012 + if (copy_to_user(buf, &shm_info, sizeof(shm_info))) 1013 + err = -EFAULT; 1014 + return err; 1015 + } 1016 + case SHM_STAT: 1017 + case IPC_STAT: { 1018 + err = shmctl_stat(ns, shmid, cmd, &tbuf); 1019 + if (err < 0) 1020 + return err; 1021 + if (copy_shmid_to_user(buf, &tbuf, version)) 1022 + err = -EFAULT; 1023 + return err; 1024 + } 1025 + case IPC_SET: 1026 + if (copy_shmid_from_user(&tbuf, buf, version)) 1027 + return -EFAULT; 1028 + case IPC_RMID: 1029 + return shmctl_down(ns, shmid, cmd, &tbuf); 1030 + case SHM_LOCK: 1031 + case SHM_UNLOCK: 1032 + return shmctl_do_lock(ns, shmid, cmd); 1118 1033 default: 1119 1034 return -EINVAL; 1120 1035 } 1121 - 1122 - out_unlock0: 1123 - ipc_unlock_object(&shp->shm_perm); 1124 - out_unlock1: 1125 - rcu_read_unlock(); 1126 - return err; 1127 1036 } 1128 1037 1129 1038 /*