Branch data Line data Source code
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 : :
38 : 0 : int verify_iovec(struct msghdr *m, struct iovec *iov, struct sockaddr_storage *address, int mode)
39 : : {
40 : : int size, ct, err;
41 : :
42 [ + + ]: 465736 : if (m->msg_namelen) {
43 [ + + ]: 450830 : if (mode == VERIFY_READ) {
44 : : void __user *namep;
45 : 447843 : namep = (void __user __force *) m->msg_name;
46 : 447843 : err = move_addr_to_kernel(namep, m->msg_namelen,
47 : : address);
48 [ + ]: 455231 : if (err < 0)
49 : : return err;
50 : : }
51 [ + ]: 473143 : if (m->msg_name)
52 : 475635 : m->msg_name = address;
53 : : } else {
54 : 14906 : m->msg_name = NULL;
55 : : }
56 : :
57 : 488049 : size = m->msg_iovlen * sizeof(struct iovec);
58 [ + + ]: 475162 : if (copy_from_user(iov, (void __user __force *) m->msg_iov, size))
59 : : return -EFAULT;
60 : :
61 : 475160 : m->msg_iov = iov;
62 : : err = 0;
63 : :
64 [ + + ]: 964828 : for (ct = 0; ct < m->msg_iovlen; ct++) {
65 : 489668 : size_t len = iov[ct].iov_len;
66 : :
67 [ - + ]: 489668 : if (len > INT_MAX - err) {
68 : : len = INT_MAX - err;
69 : 0 : iov[ct].iov_len = len;
70 : : }
71 : 489668 : err += len;
72 : : }
73 : :
74 : : return err;
75 : : }
76 : :
77 : : /*
78 : : * Copy kernel to iovec. Returns -EFAULT on error.
79 : : */
80 : :
81 : 0 : int memcpy_toiovecend(const struct iovec *iov, unsigned char *kdata,
82 : : int offset, int len)
83 : : {
84 : : int copy;
85 [ # # ]: 0 : for (; len > 0; ++iov) {
86 : : /* Skip over the finished iovecs */
87 [ # # ]: 0 : if (unlikely(offset >= iov->iov_len)) {
88 : 0 : offset -= iov->iov_len;
89 : 0 : continue;
90 : : }
91 : 0 : copy = min_t(unsigned int, iov->iov_len - offset, len);
92 [ # # ]: 0 : if (copy_to_user(iov->iov_base + offset, kdata, copy))
93 : : return -EFAULT;
94 : : offset = 0;
95 : 0 : kdata += copy;
96 : 0 : len -= copy;
97 : : }
98 : :
99 : : return 0;
100 : : }
101 : : EXPORT_SYMBOL(memcpy_toiovecend);
102 : :
103 : : /*
104 : : * Copy iovec to kernel. Returns -EFAULT on error.
105 : : */
106 : :
107 : 0 : int memcpy_fromiovecend(unsigned char *kdata, const struct iovec *iov,
108 : : int offset, int len)
109 : : {
110 : : /* Skip over the finished iovecs */
111 [ - + ]: 1204085 : while (offset >= iov->iov_len) {
112 : 0 : offset -= iov->iov_len;
113 : 0 : iov++;
114 : : }
115 : :
116 [ + + ]: 2454135 : while (len > 0) {
117 : 1213605 : u8 __user *base = iov->iov_base + offset;
118 : 1213605 : int copy = min_t(unsigned int, len, iov->iov_len - offset);
119 : :
120 : : offset = 0;
121 [ + + ]: 1250052 : if (copy_from_user(kdata, base, copy))
122 : : return -EFAULT;
123 : 1250050 : len -= copy;
124 : 1250050 : kdata += copy;
125 : 1250050 : iov++;
126 : : }
127 : :
128 : : return 0;
129 : : }
130 : : EXPORT_SYMBOL(memcpy_fromiovecend);
131 : :
132 : : /*
133 : : * And now for the all-in-one: copy and checksum from a user iovec
134 : : * directly to a datagram
135 : : * Calls to csum_partial but the last must be in 32 bit chunks
136 : : *
137 : : * ip_build_xmit must ensure that when fragmenting only the last
138 : : * call to this function will be unaligned also.
139 : : */
140 : 0 : int csum_partial_copy_fromiovecend(unsigned char *kdata, struct iovec *iov,
141 : : int offset, unsigned int len, __wsum *csump)
142 : : {
143 : 8 : __wsum csum = *csump;
144 : 8 : int partial_cnt = 0, err = 0;
145 : :
146 : : /* Skip over the finished iovecs */
147 [ - + ]: 8 : while (offset >= iov->iov_len) {
148 : 0 : offset -= iov->iov_len;
149 : 0 : iov++;
150 : : }
151 : :
152 [ + + ]: 16 : while (len > 0) {
153 : 8 : u8 __user *base = iov->iov_base + offset;
154 : 8 : int copy = min_t(unsigned int, len, iov->iov_len - offset);
155 : :
156 : : offset = 0;
157 : :
158 : : /* There is a remnant from previous iov. */
159 [ - + ]: 8 : if (partial_cnt) {
160 : 0 : int par_len = 4 - partial_cnt;
161 : :
162 : : /* iov component is too short ... */
163 [ # # ]: 0 : if (par_len > copy) {
164 [ # # ]: 8 : if (copy_from_user(kdata, base, copy))
165 : : goto out_fault;
166 : 0 : kdata += copy;
167 : : base += copy;
168 : 0 : partial_cnt += copy;
169 : 0 : len -= copy;
170 : 0 : iov++;
171 [ # # ]: 0 : if (len)
172 : 0 : continue;
173 : 0 : *csump = csum_partial(kdata - partial_cnt,
174 : : partial_cnt, csum);
175 : 0 : goto out;
176 : : }
177 [ # # ]: 0 : if (copy_from_user(kdata, base, par_len))
178 : : goto out_fault;
179 : 0 : csum = csum_partial(kdata - partial_cnt, 4, csum);
180 : 0 : kdata += par_len;
181 : 0 : base += par_len;
182 : 0 : copy -= par_len;
183 : 0 : len -= par_len;
184 : : partial_cnt = 0;
185 : : }
186 : :
187 [ - + ]: 8 : if (len > copy) {
188 : 0 : partial_cnt = copy % 4;
189 [ # # ]: 0 : if (partial_cnt) {
190 : 0 : copy -= partial_cnt;
191 [ # # ]: 0 : if (copy_from_user(kdata + copy, base + copy,
192 : : partial_cnt))
193 : : goto out_fault;
194 : : }
195 : : }
196 : :
197 [ + - ]: 8 : if (copy) {
198 : : csum = csum_and_copy_from_user(base, kdata, copy,
199 : : csum, &err);
200 [ + - ]: 8 : if (err)
201 : : goto out;
202 : : }
203 : 8 : len -= copy + partial_cnt;
204 : 8 : kdata += copy + partial_cnt;
205 : 8 : iov++;
206 : : }
207 : 8 : *csump = csum;
208 : : out:
209 : 8 : return err;
210 : :
211 : : out_fault:
212 : 0 : err = -EFAULT;
213 : 0 : goto out;
214 : : }
215 : : EXPORT_SYMBOL(csum_partial_copy_fromiovecend);
216 : :
217 : 0 : unsigned long iov_pages(const struct iovec *iov, int offset,
218 : : unsigned long nr_segs)
219 : : {
220 : : unsigned long seg, base;
221 : : int pages = 0, len, size;
222 : :
223 [ # # ][ # # ]: 0 : while (nr_segs && (offset >= iov->iov_len)) {
224 : 0 : offset -= iov->iov_len;
225 : 0 : ++iov;
226 : 0 : --nr_segs;
227 : : }
228 : :
229 [ # # ]: 0 : for (seg = 0; seg < nr_segs; seg++) {
230 : 0 : base = (unsigned long)iov[seg].iov_base + offset;
231 : 0 : len = iov[seg].iov_len - offset;
232 : 0 : size = ((base & ~PAGE_MASK) + len + ~PAGE_MASK) >> PAGE_SHIFT;
233 : 0 : pages += size;
234 : : offset = 0;
235 : : }
236 : :
237 : 0 : return pages;
238 : : }
239 : : EXPORT_SYMBOL(iov_pages);
|