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