diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2017-04-08 18:18:48 -0400 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2017-04-17 12:52:26 -0400 |
commit | f50298556436ef0f32257b8ea19b112f9028b0a5 (patch) | |
tree | 32c3e7022475150ad1cbfb87f9d29c23f446ee05 /fs | |
parent | 2b8910264a7fec4599b620e5206eb877a207f40a (diff) |
move compat_rw_copy_check_uvector() over to fs/read_write.c
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/compat.c | 76 | ||||
-rw-r--r-- | fs/read_write.c | 75 |
2 files changed, 75 insertions, 76 deletions
diff --git a/fs/compat.c b/fs/compat.c index 3e94559c452e..ca2f3ca5dacd 100644 --- a/fs/compat.c +++ b/fs/compat.c | |||
@@ -54,82 +54,6 @@ | |||
54 | #include <asm/ioctls.h> | 54 | #include <asm/ioctls.h> |
55 | #include "internal.h" | 55 | #include "internal.h" |
56 | 56 | ||
57 | /* A write operation does a read from user space and vice versa */ | ||
58 | #define vrfy_dir(type) ((type) == READ ? VERIFY_WRITE : VERIFY_READ) | ||
59 | |||
60 | ssize_t compat_rw_copy_check_uvector(int type, | ||
61 | const struct compat_iovec __user *uvector, unsigned long nr_segs, | ||
62 | unsigned long fast_segs, struct iovec *fast_pointer, | ||
63 | struct iovec **ret_pointer) | ||
64 | { | ||
65 | compat_ssize_t tot_len; | ||
66 | struct iovec *iov = *ret_pointer = fast_pointer; | ||
67 | ssize_t ret = 0; | ||
68 | int seg; | ||
69 | |||
70 | /* | ||
71 | * SuS says "The readv() function *may* fail if the iovcnt argument | ||
72 | * was less than or equal to 0, or greater than {IOV_MAX}. Linux has | ||
73 | * traditionally returned zero for zero segments, so... | ||
74 | */ | ||
75 | if (nr_segs == 0) | ||
76 | goto out; | ||
77 | |||
78 | ret = -EINVAL; | ||
79 | if (nr_segs > UIO_MAXIOV) | ||
80 | goto out; | ||
81 | if (nr_segs > fast_segs) { | ||
82 | ret = -ENOMEM; | ||
83 | iov = kmalloc(nr_segs*sizeof(struct iovec), GFP_KERNEL); | ||
84 | if (iov == NULL) | ||
85 | goto out; | ||
86 | } | ||
87 | *ret_pointer = iov; | ||
88 | |||
89 | ret = -EFAULT; | ||
90 | if (!access_ok(VERIFY_READ, uvector, nr_segs*sizeof(*uvector))) | ||
91 | goto out; | ||
92 | |||
93 | /* | ||
94 | * Single unix specification: | ||
95 | * We should -EINVAL if an element length is not >= 0 and fitting an | ||
96 | * ssize_t. | ||
97 | * | ||
98 | * In Linux, the total length is limited to MAX_RW_COUNT, there is | ||
99 | * no overflow possibility. | ||
100 | */ | ||
101 | tot_len = 0; | ||
102 | ret = -EINVAL; | ||
103 | for (seg = 0; seg < nr_segs; seg++) { | ||
104 | compat_uptr_t buf; | ||
105 | compat_ssize_t len; | ||
106 | |||
107 | if (__get_user(len, &uvector->iov_len) || | ||
108 | __get_user(buf, &uvector->iov_base)) { | ||
109 | ret = -EFAULT; | ||
110 | goto out; | ||
111 | } | ||
112 | if (len < 0) /* size_t not fitting in compat_ssize_t .. */ | ||
113 | goto out; | ||
114 | if (type >= 0 && | ||
115 | !access_ok(vrfy_dir(type), compat_ptr(buf), len)) { | ||
116 | ret = -EFAULT; | ||
117 | goto out; | ||
118 | } | ||
119 | if (len > MAX_RW_COUNT - tot_len) | ||
120 | len = MAX_RW_COUNT - tot_len; | ||
121 | tot_len += len; | ||
122 | iov->iov_base = compat_ptr(buf); | ||
123 | iov->iov_len = (compat_size_t) len; | ||
124 | uvector++; | ||
125 | iov++; | ||
126 | } | ||
127 | ret = tot_len; | ||
128 | |||
129 | out: | ||
130 | return ret; | ||
131 | } | ||
132 | |||
133 | struct compat_ncp_mount_data { | 57 | struct compat_ncp_mount_data { |
134 | compat_int_t version; | 58 | compat_int_t version; |
135 | compat_uint_t ncp_fd; | 59 | compat_uint_t ncp_fd; |
diff --git a/fs/read_write.c b/fs/read_write.c index c4f88afbc67f..47c1d4484df9 100644 --- a/fs/read_write.c +++ b/fs/read_write.c | |||
@@ -841,6 +841,81 @@ out: | |||
841 | return ret; | 841 | return ret; |
842 | } | 842 | } |
843 | 843 | ||
844 | #ifdef CONFIG_COMPAT | ||
845 | ssize_t compat_rw_copy_check_uvector(int type, | ||
846 | const struct compat_iovec __user *uvector, unsigned long nr_segs, | ||
847 | unsigned long fast_segs, struct iovec *fast_pointer, | ||
848 | struct iovec **ret_pointer) | ||
849 | { | ||
850 | compat_ssize_t tot_len; | ||
851 | struct iovec *iov = *ret_pointer = fast_pointer; | ||
852 | ssize_t ret = 0; | ||
853 | int seg; | ||
854 | |||
855 | /* | ||
856 | * SuS says "The readv() function *may* fail if the iovcnt argument | ||
857 | * was less than or equal to 0, or greater than {IOV_MAX}. Linux has | ||
858 | * traditionally returned zero for zero segments, so... | ||
859 | */ | ||
860 | if (nr_segs == 0) | ||
861 | goto out; | ||
862 | |||
863 | ret = -EINVAL; | ||
864 | if (nr_segs > UIO_MAXIOV) | ||
865 | goto out; | ||
866 | if (nr_segs > fast_segs) { | ||
867 | ret = -ENOMEM; | ||
868 | iov = kmalloc(nr_segs*sizeof(struct iovec), GFP_KERNEL); | ||
869 | if (iov == NULL) | ||
870 | goto out; | ||
871 | } | ||
872 | *ret_pointer = iov; | ||
873 | |||
874 | ret = -EFAULT; | ||
875 | if (!access_ok(VERIFY_READ, uvector, nr_segs*sizeof(*uvector))) | ||
876 | goto out; | ||
877 | |||
878 | /* | ||
879 | * Single unix specification: | ||
880 | * We should -EINVAL if an element length is not >= 0 and fitting an | ||
881 | * ssize_t. | ||
882 | * | ||
883 | * In Linux, the total length is limited to MAX_RW_COUNT, there is | ||
884 | * no overflow possibility. | ||
885 | */ | ||
886 | tot_len = 0; | ||
887 | ret = -EINVAL; | ||
888 | for (seg = 0; seg < nr_segs; seg++) { | ||
889 | compat_uptr_t buf; | ||
890 | compat_ssize_t len; | ||
891 | |||
892 | if (__get_user(len, &uvector->iov_len) || | ||
893 | __get_user(buf, &uvector->iov_base)) { | ||
894 | ret = -EFAULT; | ||
895 | goto out; | ||
896 | } | ||
897 | if (len < 0) /* size_t not fitting in compat_ssize_t .. */ | ||
898 | goto out; | ||
899 | if (type >= 0 && | ||
900 | !access_ok(vrfy_dir(type), compat_ptr(buf), len)) { | ||
901 | ret = -EFAULT; | ||
902 | goto out; | ||
903 | } | ||
904 | if (len > MAX_RW_COUNT - tot_len) | ||
905 | len = MAX_RW_COUNT - tot_len; | ||
906 | tot_len += len; | ||
907 | iov->iov_base = compat_ptr(buf); | ||
908 | iov->iov_len = (compat_size_t) len; | ||
909 | uvector++; | ||
910 | iov++; | ||
911 | } | ||
912 | ret = tot_len; | ||
913 | |||
914 | out: | ||
915 | return ret; | ||
916 | } | ||
917 | #endif | ||
918 | |||
844 | static ssize_t __do_readv_writev(int type, struct file *file, | 919 | static ssize_t __do_readv_writev(int type, struct file *file, |
845 | struct iov_iter *iter, loff_t *pos, int flags) | 920 | struct iov_iter *iter, loff_t *pos, int flags) |
846 | { | 921 | { |