1 /*
2 * iovec manipulation routines.
3 *
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version
8 * 2 of the License, or (at your option) any later version.
9 *
10 * Fixes:
11 * Andrew Lunn : Errors in iovec copying.
12 * Pedro Roque : Added memcpy_fromiovecend and
13 * csum_..._fromiovecend.
14 * Andi Kleen : fixed error handling for 2.1
15 * Alexey Kuznetsov: 2.1 optimisations
16 * Andi Kleen : Fix csum*fromiovecend for IPv6.
17 */
18
19
20 #include <linux/errno.h>
21 #include <linux/sched.h>
22 #include <linux/kernel.h>
23 #include <linux/mm.h>
24 #include <linux/slab.h>
25 #include <linux/net.h>
26 #include <linux/in6.h>
27 #include <asm/uaccess.h>
28 #include <asm/byteorder.h>
29 #include <net/checksum.h>
30 #include <net/sock.h>
31
32 /*
33 * Verify iovec. The caller must ensure that the iovec is big enough
34 * to hold the message iovec.
35 *
36 * Save time not doing verify_area. copy_*_user will make this work
37 * in any case.
38 */
39
verify_iovec(struct msghdr * m,struct iovec * iov,char * address,int mode)40 int verify_iovec(struct msghdr *m, struct iovec *iov, char *address, int mode)
41 {
42 int size, err, ct;
43
44 if(m->msg_namelen)
45 {
46 if(mode==VERIFY_READ)
47 {
48 err=move_addr_to_kernel(m->msg_name, m->msg_namelen, address);
49 if(err<0)
50 goto out;
51 }
52
53 m->msg_name = address;
54 } else
55 m->msg_name = NULL;
56
57 err = -EFAULT;
58 size = m->msg_iovlen * sizeof(struct iovec);
59 if (copy_from_user(iov, m->msg_iov, size))
60 goto out;
61 m->msg_iov=iov;
62
63 for (err = 0, ct = 0; ct < m->msg_iovlen; ct++) {
64 err += iov[ct].iov_len;
65 /* Goal is not to verify user data, but to prevent returning
66 negative value, which is interpreted as errno.
67 Overflow is still possible, but it is harmless.
68 */
69 if (err < 0)
70 return -EMSGSIZE;
71 }
72 out:
73 return err;
74 }
75
76 /*
77 * Copy kernel to iovec. Returns -EFAULT on error.
78 *
79 * Note: this modifies the original iovec.
80 */
81
memcpy_toiovec(struct iovec * iov,unsigned char * kdata,int len)82 int memcpy_toiovec(struct iovec *iov, unsigned char *kdata, int len)
83 {
84 int err = -EFAULT;
85
86 while(len>0)
87 {
88 if(iov->iov_len)
89 {
90 int copy = min_t(unsigned int, iov->iov_len, len);
91 if (copy_to_user(iov->iov_base, kdata, copy))
92 goto out;
93 kdata+=copy;
94 len-=copy;
95 iov->iov_len-=copy;
96 iov->iov_base+=copy;
97 }
98 iov++;
99 }
100 err = 0;
101 out:
102 return err;
103 }
104
105 /*
106 * In kernel copy to iovec. Returns -EFAULT on error.
107 *
108 * Note: this modifies the original iovec.
109 */
110
memcpy_tokerneliovec(struct iovec * iov,unsigned char * kdata,int len)111 void memcpy_tokerneliovec(struct iovec *iov, unsigned char *kdata, int len)
112 {
113 while(len>0)
114 {
115 if(iov->iov_len)
116 {
117 int copy = min_t(unsigned int, iov->iov_len, len);
118 memcpy(iov->iov_base, kdata, copy);
119 kdata+=copy;
120 len-=copy;
121 iov->iov_len-=copy;
122 iov->iov_base+=copy;
123 }
124 iov++;
125 }
126 }
127
128
129 /*
130 * Copy iovec to kernel. Returns -EFAULT on error.
131 *
132 * Note: this modifies the original iovec.
133 */
134
memcpy_fromiovec(unsigned char * kdata,struct iovec * iov,int len)135 int memcpy_fromiovec(unsigned char *kdata, struct iovec *iov, int len)
136 {
137 int err = -EFAULT;
138
139 while(len>0)
140 {
141 if(iov->iov_len)
142 {
143 int copy = min_t(unsigned int, len, iov->iov_len);
144 if (copy_from_user(kdata, iov->iov_base, copy))
145 goto out;
146 len-=copy;
147 kdata+=copy;
148 iov->iov_base+=copy;
149 iov->iov_len-=copy;
150 }
151 iov++;
152 }
153 err = 0;
154 out:
155 return err;
156 }
157
158
159 /*
160 * For use with ip_build_xmit
161 */
162
memcpy_fromiovecend(unsigned char * kdata,struct iovec * iov,int offset,int len)163 int memcpy_fromiovecend(unsigned char *kdata, struct iovec *iov, int offset,
164 int len)
165 {
166 int err = -EFAULT;
167
168 /* Skip over the finished iovecs */
169 while(offset >= iov->iov_len)
170 {
171 offset -= iov->iov_len;
172 iov++;
173 }
174
175 while (len > 0)
176 {
177 u8 *base = iov->iov_base + offset;
178 int copy = min_t(unsigned int, len, iov->iov_len - offset);
179
180 offset = 0;
181 if (copy_from_user(kdata, base, copy))
182 goto out;
183 len -= copy;
184 kdata += copy;
185 iov++;
186 }
187 err = 0;
188 out:
189 return err;
190 }
191
192 /*
193 * And now for the all-in-one: copy and checksum from a user iovec
194 * directly to a datagram
195 * Calls to csum_partial but the last must be in 32 bit chunks
196 *
197 * ip_build_xmit must ensure that when fragmenting only the last
198 * call to this function will be unaligned also.
199 */
200
csum_partial_copy_fromiovecend(unsigned char * kdata,struct iovec * iov,int offset,unsigned int len,int * csump)201 int csum_partial_copy_fromiovecend(unsigned char *kdata, struct iovec *iov,
202 int offset, unsigned int len, int *csump)
203 {
204 int csum = *csump;
205 int partial_cnt = 0, err = 0;
206
207 /* Skip over the finished iovecs */
208 while (offset >= iov->iov_len)
209 {
210 offset -= iov->iov_len;
211 iov++;
212 }
213
214 while (len > 0)
215 {
216 u8 *base = iov->iov_base + offset;
217 int copy = min_t(unsigned int, len, iov->iov_len - offset);
218
219 offset = 0;
220 /* There is a remnant from previous iov. */
221 if (partial_cnt)
222 {
223 int par_len = 4 - partial_cnt;
224
225 /* iov component is too short ... */
226 if (par_len > copy) {
227 if (copy_from_user(kdata, base, copy))
228 goto out_fault;
229 kdata += copy;
230 base += copy;
231 partial_cnt += copy;
232 len -= copy;
233 iov++;
234 if (len)
235 continue;
236 *csump = csum_partial(kdata - partial_cnt,
237 partial_cnt, csum);
238 goto out;
239 }
240 if (copy_from_user(kdata, base, par_len))
241 goto out_fault;
242 csum = csum_partial(kdata - partial_cnt, 4, csum);
243 kdata += par_len;
244 base += par_len;
245 copy -= par_len;
246 len -= par_len;
247 partial_cnt = 0;
248 }
249
250 if (len > copy)
251 {
252 partial_cnt = copy % 4;
253 if (partial_cnt)
254 {
255 copy -= partial_cnt;
256 if (copy_from_user(kdata + copy, base + copy,
257 partial_cnt))
258 goto out_fault;
259 }
260 }
261
262 if (copy) {
263 csum = csum_and_copy_from_user(base, kdata, copy,
264 csum, &err);
265 if (err)
266 goto out;
267 }
268 len -= copy + partial_cnt;
269 kdata += copy + partial_cnt;
270 iov++;
271 }
272 *csump = csum;
273 out:
274 return err;
275
276 out_fault:
277 err = -EFAULT;
278 goto out;
279 }
280