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 #include <linux/errno.h>
20 #include <linux/module.h>
21 #include <linux/kernel.h>
22 #include <linux/mm.h>
23 #include <linux/net.h>
24 #include <linux/in6.h>
25 #include <asm/uaccess.h>
26 #include <asm/byteorder.h>
27 #include <net/checksum.h>
28 #include <net/sock.h>
29 
30 /*
31  *	Verify iovec. The caller must ensure that the iovec is big enough
32  *	to hold the message iovec.
33  *
34  *	Save time not doing access_ok. copy_*_user will make this work
35  *	in any case.
36  */
37 
verify_iovec(struct msghdr * m,struct iovec * iov,struct sockaddr * address,int mode)38 int verify_iovec(struct msghdr *m, struct iovec *iov, struct sockaddr *address, int mode)
39 {
40 	int size, ct, err;
41 
42 	if (m->msg_namelen) {
43 		if (mode == VERIFY_READ) {
44 			void __user *namep;
45 			namep = (void __user __force *) m->msg_name;
46 			err = move_addr_to_kernel(namep, m->msg_namelen,
47 						  address);
48 			if (err < 0)
49 				return err;
50 		}
51 		m->msg_name = address;
52 	} else {
53 		m->msg_name = NULL;
54 	}
55 
56 	size = m->msg_iovlen * sizeof(struct iovec);
57 	if (copy_from_user(iov, (void __user __force *) m->msg_iov, size))
58 		return -EFAULT;
59 
60 	m->msg_iov = iov;
61 	err = 0;
62 
63 	for (ct = 0; ct < m->msg_iovlen; ct++) {
64 		size_t len = iov[ct].iov_len;
65 
66 		if (len > INT_MAX - err) {
67 			len = INT_MAX - err;
68 			iov[ct].iov_len = len;
69 		}
70 		err += len;
71 	}
72 
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 	while (len > 0) {
85 		if (iov->iov_len) {
86 			int copy = min_t(unsigned int, iov->iov_len, len);
87 			if (copy_to_user(iov->iov_base, kdata, copy))
88 				return -EFAULT;
89 			kdata += copy;
90 			len -= copy;
91 			iov->iov_len -= copy;
92 			iov->iov_base += copy;
93 		}
94 		iov++;
95 	}
96 
97 	return 0;
98 }
99 EXPORT_SYMBOL(memcpy_toiovec);
100 
101 /*
102  *	Copy kernel to iovec. Returns -EFAULT on error.
103  */
104 
memcpy_toiovecend(const struct iovec * iov,unsigned char * kdata,int offset,int len)105 int memcpy_toiovecend(const struct iovec *iov, unsigned char *kdata,
106 		      int offset, int len)
107 {
108 	int copy;
109 	for (; len > 0; ++iov) {
110 		/* Skip over the finished iovecs */
111 		if (unlikely(offset >= iov->iov_len)) {
112 			offset -= iov->iov_len;
113 			continue;
114 		}
115 		copy = min_t(unsigned int, iov->iov_len - offset, len);
116 		if (copy_to_user(iov->iov_base + offset, kdata, copy))
117 			return -EFAULT;
118 		offset = 0;
119 		kdata += copy;
120 		len -= copy;
121 	}
122 
123 	return 0;
124 }
125 EXPORT_SYMBOL(memcpy_toiovecend);
126 
127 /*
128  *	Copy iovec to kernel. Returns -EFAULT on error.
129  *
130  *	Note: this modifies the original iovec.
131  */
132 
memcpy_fromiovec(unsigned char * kdata,struct iovec * iov,int len)133 int memcpy_fromiovec(unsigned char *kdata, struct iovec *iov, int len)
134 {
135 	while (len > 0) {
136 		if (iov->iov_len) {
137 			int copy = min_t(unsigned int, len, iov->iov_len);
138 			if (copy_from_user(kdata, iov->iov_base, copy))
139 				return -EFAULT;
140 			len -= copy;
141 			kdata += copy;
142 			iov->iov_base += copy;
143 			iov->iov_len -= copy;
144 		}
145 		iov++;
146 	}
147 
148 	return 0;
149 }
150 EXPORT_SYMBOL(memcpy_fromiovec);
151 
152 /*
153  *	Copy iovec from kernel. Returns -EFAULT on error.
154  */
155 
memcpy_fromiovecend(unsigned char * kdata,const struct iovec * iov,int offset,int len)156 int memcpy_fromiovecend(unsigned char *kdata, const struct iovec *iov,
157 			int offset, int len)
158 {
159 	/* Skip over the finished iovecs */
160 	while (offset >= iov->iov_len) {
161 		offset -= iov->iov_len;
162 		iov++;
163 	}
164 
165 	while (len > 0) {
166 		u8 __user *base = iov->iov_base + offset;
167 		int copy = min_t(unsigned int, len, iov->iov_len - offset);
168 
169 		offset = 0;
170 		if (copy_from_user(kdata, base, copy))
171 			return -EFAULT;
172 		len -= copy;
173 		kdata += copy;
174 		iov++;
175 	}
176 
177 	return 0;
178 }
179 EXPORT_SYMBOL(memcpy_fromiovecend);
180 
181 /*
182  *	And now for the all-in-one: copy and checksum from a user iovec
183  *	directly to a datagram
184  *	Calls to csum_partial but the last must be in 32 bit chunks
185  *
186  *	ip_build_xmit must ensure that when fragmenting only the last
187  *	call to this function will be unaligned also.
188  */
csum_partial_copy_fromiovecend(unsigned char * kdata,struct iovec * iov,int offset,unsigned int len,__wsum * csump)189 int csum_partial_copy_fromiovecend(unsigned char *kdata, struct iovec *iov,
190 				 int offset, unsigned int len, __wsum *csump)
191 {
192 	__wsum csum = *csump;
193 	int partial_cnt = 0, err = 0;
194 
195 	/* Skip over the finished iovecs */
196 	while (offset >= iov->iov_len) {
197 		offset -= iov->iov_len;
198 		iov++;
199 	}
200 
201 	while (len > 0) {
202 		u8 __user *base = iov->iov_base + offset;
203 		int copy = min_t(unsigned int, len, iov->iov_len - offset);
204 
205 		offset = 0;
206 
207 		/* There is a remnant from previous iov. */
208 		if (partial_cnt) {
209 			int par_len = 4 - partial_cnt;
210 
211 			/* iov component is too short ... */
212 			if (par_len > copy) {
213 				if (copy_from_user(kdata, base, copy))
214 					goto out_fault;
215 				kdata += copy;
216 				base += copy;
217 				partial_cnt += copy;
218 				len -= copy;
219 				iov++;
220 				if (len)
221 					continue;
222 				*csump = csum_partial(kdata - partial_cnt,
223 							 partial_cnt, csum);
224 				goto out;
225 			}
226 			if (copy_from_user(kdata, base, par_len))
227 				goto out_fault;
228 			csum = csum_partial(kdata - partial_cnt, 4, csum);
229 			kdata += par_len;
230 			base  += par_len;
231 			copy  -= par_len;
232 			len   -= par_len;
233 			partial_cnt = 0;
234 		}
235 
236 		if (len > copy) {
237 			partial_cnt = copy % 4;
238 			if (partial_cnt) {
239 				copy -= partial_cnt;
240 				if (copy_from_user(kdata + copy, base + copy,
241 						partial_cnt))
242 					goto out_fault;
243 			}
244 		}
245 
246 		if (copy) {
247 			csum = csum_and_copy_from_user(base, kdata, copy,
248 							csum, &err);
249 			if (err)
250 				goto out;
251 		}
252 		len   -= copy + partial_cnt;
253 		kdata += copy + partial_cnt;
254 		iov++;
255 	}
256 	*csump = csum;
257 out:
258 	return err;
259 
260 out_fault:
261 	err = -EFAULT;
262 	goto out;
263 }
264 EXPORT_SYMBOL(csum_partial_copy_fromiovecend);
265