Blame view

net/core/iovec.c 5.62 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
  /*
   *	iovec manipulation routines.
   *
   *
   *		This program is free software; you can redistribute it and/or
   *		modify it under the terms of the GNU General Public License
   *		as published by the Free Software Foundation; either version
   *		2 of the License, or (at your option) any later version.
   *
   *	Fixes:
   *		Andrew Lunn	:	Errors in iovec copying.
   *		Pedro Roque	:	Added memcpy_fromiovecend and
   *					csum_..._fromiovecend.
   *		Andi Kleen	:	fixed error handling for 2.1
   *		Alexey Kuznetsov:	2.1 optimisations
   *		Andi Kleen	:	Fix csum*fromiovecend for IPv6.
   */
  
  #include <linux/errno.h>
  #include <linux/module.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
21
22
  #include <linux/kernel.h>
  #include <linux/mm.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
23
24
25
26
27
28
29
30
31
32
33
  #include <linux/net.h>
  #include <linux/in6.h>
  #include <asm/uaccess.h>
  #include <asm/byteorder.h>
  #include <net/checksum.h>
  #include <net/sock.h>
  
  /*
   *	Verify iovec. The caller must ensure that the iovec is big enough
   *	to hold the message iovec.
   *
e49332bd1   Jesper Juhl   [PATCH] misc veri...
34
   *	Save time not doing access_ok. copy_*_user will make this work
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
35
36
   *	in any case.
   */
8acfe468b   David S. Miller   net: Limit socket...
37
  int verify_iovec(struct msghdr *m, struct iovec *iov, struct sockaddr *address, int mode)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
38
  {
8acfe468b   David S. Miller   net: Limit socket...
39
  	int size, ct, err;
4ec93edb1   YOSHIFUJI Hideaki   [NET] CORE: Fix w...
40

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
41
42
  	if (m->msg_namelen) {
  		if (mode == VERIFY_READ) {
a700d8be7   Namhyung Kim   net/core: remove ...
43
44
45
  			void __user *namep;
  			namep = (void __user __force *) m->msg_name;
  			err = move_addr_to_kernel(namep, m->msg_namelen,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
46
47
48
49
50
51
52
53
54
55
  						  address);
  			if (err < 0)
  				return err;
  		}
  		m->msg_name = address;
  	} else {
  		m->msg_name = NULL;
  	}
  
  	size = m->msg_iovlen * sizeof(struct iovec);
a700d8be7   Namhyung Kim   net/core: remove ...
56
  	if (copy_from_user(iov, (void __user __force *) m->msg_iov, size))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
57
58
59
60
61
62
  		return -EFAULT;
  
  	m->msg_iov = iov;
  	err = 0;
  
  	for (ct = 0; ct < m->msg_iovlen; ct++) {
8acfe468b   David S. Miller   net: Limit socket...
63
64
65
66
67
68
69
  		size_t len = iov[ct].iov_len;
  
  		if (len > INT_MAX - err) {
  			len = INT_MAX - err;
  			iov[ct].iov_len = len;
  		}
  		err += len;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
70
71
72
73
74
75
76
77
78
79
  	}
  
  	return err;
  }
  
  /*
   *	Copy kernel to iovec. Returns -EFAULT on error.
   *
   *	Note: this modifies the original iovec.
   */
4ec93edb1   YOSHIFUJI Hideaki   [NET] CORE: Fix w...
80

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
  int memcpy_toiovec(struct iovec *iov, unsigned char *kdata, int len)
  {
  	while (len > 0) {
  		if (iov->iov_len) {
  			int copy = min_t(unsigned int, iov->iov_len, len);
  			if (copy_to_user(iov->iov_base, kdata, copy))
  				return -EFAULT;
  			kdata += copy;
  			len -= copy;
  			iov->iov_len -= copy;
  			iov->iov_base += copy;
  		}
  		iov++;
  	}
  
  	return 0;
  }
9e34a5b51   Eric Dumazet   net/core: EXPORT_...
98
  EXPORT_SYMBOL(memcpy_toiovec);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
99
100
  
  /*
0a1ec07a6   Michael S. Tsirkin   net: skb_copy_dat...
101
102
103
104
105
106
107
108
109
110
111
112
113
114
   *	Copy kernel to iovec. Returns -EFAULT on error.
   */
  
  int memcpy_toiovecend(const struct iovec *iov, unsigned char *kdata,
  		      int offset, int len)
  {
  	int copy;
  	for (; len > 0; ++iov) {
  		/* Skip over the finished iovecs */
  		if (unlikely(offset >= iov->iov_len)) {
  			offset -= iov->iov_len;
  			continue;
  		}
  		copy = min_t(unsigned int, iov->iov_len - offset, len);
2faef52b7   Sridhar Samudrala   net: Fix memcpy_t...
115
  		if (copy_to_user(iov->iov_base + offset, kdata, copy))
0a1ec07a6   Michael S. Tsirkin   net: skb_copy_dat...
116
  			return -EFAULT;
2faef52b7   Sridhar Samudrala   net: Fix memcpy_t...
117
  		offset = 0;
0a1ec07a6   Michael S. Tsirkin   net: skb_copy_dat...
118
119
120
121
122
123
  		kdata += copy;
  		len -= copy;
  	}
  
  	return 0;
  }
9e34a5b51   Eric Dumazet   net/core: EXPORT_...
124
  EXPORT_SYMBOL(memcpy_toiovecend);
0a1ec07a6   Michael S. Tsirkin   net: skb_copy_dat...
125
126
  
  /*
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
127
128
129
130
   *	Copy iovec to kernel. Returns -EFAULT on error.
   *
   *	Note: this modifies the original iovec.
   */
4ec93edb1   YOSHIFUJI Hideaki   [NET] CORE: Fix w...
131

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
  int memcpy_fromiovec(unsigned char *kdata, struct iovec *iov, int len)
  {
  	while (len > 0) {
  		if (iov->iov_len) {
  			int copy = min_t(unsigned int, len, iov->iov_len);
  			if (copy_from_user(kdata, iov->iov_base, copy))
  				return -EFAULT;
  			len -= copy;
  			kdata += copy;
  			iov->iov_base += copy;
  			iov->iov_len -= copy;
  		}
  		iov++;
  	}
  
  	return 0;
  }
9e34a5b51   Eric Dumazet   net/core: EXPORT_...
149
  EXPORT_SYMBOL(memcpy_fromiovec);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
150
151
  
  /*
6f26c9a75   Michael S. Tsirkin   tun: fix tun_chr_...
152
   *	Copy iovec from kernel. Returns -EFAULT on error.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
153
   */
6f26c9a75   Michael S. Tsirkin   tun: fix tun_chr_...
154
155
156
  
  int memcpy_fromiovecend(unsigned char *kdata, const struct iovec *iov,
  			int offset, int len)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
  {
  	/* Skip over the finished iovecs */
  	while (offset >= iov->iov_len) {
  		offset -= iov->iov_len;
  		iov++;
  	}
  
  	while (len > 0) {
  		u8 __user *base = iov->iov_base + offset;
  		int copy = min_t(unsigned int, len, iov->iov_len - offset);
  
  		offset = 0;
  		if (copy_from_user(kdata, base, copy))
  			return -EFAULT;
  		len -= copy;
  		kdata += copy;
  		iov++;
  	}
  
  	return 0;
  }
9e34a5b51   Eric Dumazet   net/core: EXPORT_...
178
  EXPORT_SYMBOL(memcpy_fromiovecend);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
179
180
181
182
183
184
185
186
187
188
  
  /*
   *	And now for the all-in-one: copy and checksum from a user iovec
   *	directly to a datagram
   *	Calls to csum_partial but the last must be in 32 bit chunks
   *
   *	ip_build_xmit must ensure that when fragmenting only the last
   *	call to this function will be unaligned also.
   */
  int csum_partial_copy_fromiovecend(unsigned char *kdata, struct iovec *iov,
44bb93633   Al Viro   [NET]: Annotate c...
189
  				 int offset, unsigned int len, __wsum *csump)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
190
  {
44bb93633   Al Viro   [NET]: Annotate c...
191
  	__wsum csum = *csump;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
  	int partial_cnt = 0, err = 0;
  
  	/* Skip over the finished iovecs */
  	while (offset >= iov->iov_len) {
  		offset -= iov->iov_len;
  		iov++;
  	}
  
  	while (len > 0) {
  		u8 __user *base = iov->iov_base + offset;
  		int copy = min_t(unsigned int, len, iov->iov_len - offset);
  
  		offset = 0;
  
  		/* There is a remnant from previous iov. */
  		if (partial_cnt) {
  			int par_len = 4 - partial_cnt;
  
  			/* iov component is too short ... */
  			if (par_len > copy) {
  				if (copy_from_user(kdata, base, copy))
  					goto out_fault;
  				kdata += copy;
  				base += copy;
  				partial_cnt += copy;
  				len -= copy;
  				iov++;
  				if (len)
  					continue;
  				*csump = csum_partial(kdata - partial_cnt,
  							 partial_cnt, csum);
  				goto out;
  			}
  			if (copy_from_user(kdata, base, par_len))
  				goto out_fault;
  			csum = csum_partial(kdata - partial_cnt, 4, csum);
  			kdata += par_len;
  			base  += par_len;
  			copy  -= par_len;
  			len   -= par_len;
  			partial_cnt = 0;
  		}
  
  		if (len > copy) {
  			partial_cnt = copy % 4;
  			if (partial_cnt) {
  				copy -= partial_cnt;
  				if (copy_from_user(kdata + copy, base + copy,
4ec93edb1   YOSHIFUJI Hideaki   [NET] CORE: Fix w...
240
  						partial_cnt))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
241
242
243
244
245
246
247
248
249
250
251
252
253
254
  					goto out_fault;
  			}
  		}
  
  		if (copy) {
  			csum = csum_and_copy_from_user(base, kdata, copy,
  							csum, &err);
  			if (err)
  				goto out;
  		}
  		len   -= copy + partial_cnt;
  		kdata += copy + partial_cnt;
  		iov++;
  	}
4ec93edb1   YOSHIFUJI Hideaki   [NET] CORE: Fix w...
255
  	*csump = csum;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
256
257
258
259
260
261
262
  out:
  	return err;
  
  out_fault:
  	err = -EFAULT;
  	goto out;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
263
  EXPORT_SYMBOL(csum_partial_copy_fromiovecend);