/* [<][>][^][v][top][bottom][index][help] */
DEFINITIONS
This source file includes following definitions.
- copyfail
- copycheck
- copyin
- copyout
- copystr
- copyinstr
- copyoutstr
1 /*
2 * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2008, 2009
3 * The President and Fellows of Harvard College.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of the University nor the names of its contributors
14 * may be used to endorse or promote products derived from this software
15 * without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30 #include <types.h>
31 #include <kern/errno.h>
32 #include <lib.h>
33 #include <setjmp.h>
34 #include <thread.h>
35 #include <current.h>
36 #include <vm.h>
37 #include <copyinout.h>
38
39 /*
40 * User/kernel memory copying functions.
41 *
42 * These are arranged to prevent fatal kernel memory faults if invalid
43 * addresses are supplied by user-level code. This code is itself
44 * machine-independent; it uses the machine-dependent C setjmp/longjmp
45 * facility to perform recovery.
46 *
47 * However, it assumes things about the memory subsystem that may not
48 * be true on all platforms.
49 *
50 * (1) It assumes that user memory is mapped into the current address
51 * space while running in the kernel, and can be accessed by just
52 * dereferencing a pointer in the ordinary way. (And not, for example,
53 * with special instructions or via special segment registers.)
54 *
55 * (2) It assumes that the user-space region of memory is contiguous
56 * and extends from 0 to some virtual address USERSPACETOP, and so if
57 * a user process passes a kernel address the logic in copycheck()
58 * will trap it.
59 *
60 * (3) It assumes that access to user memory from the kernel behaves
61 * the same way as access to user memory from user space: for
62 * instance, that the processor honors read-only bits on memory pages
63 * when in kernel mode.
64 *
65 * (4) It assumes that if a proper user-space address that is valid
66 * but not present, or not valid at all, is touched from the kernel,
67 * that the correct faults will occur and the VM system will load the
68 * necessary pages and whatnot.
69 *
70 * (5) It assumes that the machine-dependent trap logic provides and
71 * honors a tm_badfaultfunc field in the thread_machdep structure.
72 * This feature works as follows: if an otherwise fatal fault occurs
73 * in kernel mode, and tm_badfaultfunc is set, execution resumes in
74 * the function pointed to by tm_badfaultfunc.
75 *
76 * This code works by setting tm_badfaultfunc and then copying memory
77 * in an ordinary fashion. If these five assumptions are satisfied,
78 * which is the case for many ordinary CPU types, this code should
79 * function correctly. If the assumptions are not satisfied on some
80 * platform (for instance, certain old 80386 processors violate
81 * assumption 3), this code cannot be used, and cpu- or platform-
82 * specific code must be written.
83 *
84 * To make use of this code, in addition to tm_badfaultfunc the
85 * thread_machdep structure should contain a jmp_buf called
86 * "tm_copyjmp".
87 */
88
89 /*
90 * Recovery function. If a fatal fault occurs during copyin, copyout,
91 * copyinstr, or copyoutstr, execution resumes here. (This behavior is
92 * caused by setting t_machdep.tm_badfaultfunc and is implemented in
93 * machine-dependent code.)
94 *
95 * We use the C standard function longjmp() to teleport up the call
96 * stack to where setjmp() was called. At that point we return EFAULT.
97 */
98 static
99 void
100 copyfail(void)
101 {
102 longjmp(curthread->t_machdep.tm_copyjmp, 1);
103 }
104
105 /*
106 * Memory region check function. This checks to make sure the block of
107 * user memory provided (an address and a length) falls within the
108 * proper userspace region. If it does not, EFAULT is returned.
109 *
110 * stoplen is set to the actual maximum length that can be copied.
111 * This differs from len if and only if the region partially overlaps
112 * the kernel.
113 *
114 * Assumes userspace runs from 0 through USERSPACETOP-1.
115 */
116 static
117 int
118 copycheck(const_userptr_t userptr, size_t len, size_t *stoplen)
119 {
120 vaddr_t bot, top;
121
122 *stoplen = len;
123
124 bot = (vaddr_t) userptr;
125 top = bot+len-1;
126
127 if (top < bot) {
128 /* addresses wrapped around */
129 return EFAULT;
130 }
131
132 if (bot >= USERSPACETOP) {
133 /* region is within the kernel */
134 return EFAULT;
135 }
136
137 if (top >= USERSPACETOP) {
138 /* region overlaps the kernel. adjust the max length. */
139 *stoplen = USERSPACETOP - bot;
140 }
141
142 return 0;
143 }
144
145 /*
146 * copyin
147 *
148 * Copy a block of memory of length LEN from user-level address USERSRC
149 * to kernel address DEST. We can use memcpy because it's protected by
150 * the tm_badfaultfunc/copyfail logic.
151 */
152 int
153 copyin(const_userptr_t usersrc, void *dest, size_t len)
154 {
155 int result;
156 size_t stoplen;
157
158 result = copycheck(usersrc, len, &stoplen);
159 if (result) {
160 return result;
161 }
162 if (stoplen != len) {
163 /* Single block, can't legally truncate it. */
164 return EFAULT;
165 }
166
167 curthread->t_machdep.tm_badfaultfunc = copyfail;
168
169 result = setjmp(curthread->t_machdep.tm_copyjmp);
170 if (result) {
171 curthread->t_machdep.tm_badfaultfunc = NULL;
172 return EFAULT;
173 }
174
175 memcpy(dest, (const void *)usersrc, len);
176
177 curthread->t_machdep.tm_badfaultfunc = NULL;
178 return 0;
179 }
180
181 /*
182 * copyout
183 *
184 * Copy a block of memory of length LEN from kernel address SRC to
185 * user-level address USERDEST. We can use memcpy because it's
186 * protected by the tm_badfaultfunc/copyfail logic.
187 */
188 int
189 copyout(const void *src, userptr_t userdest, size_t len)
190 {
191 int result;
192 size_t stoplen;
193
194 result = copycheck(userdest, len, &stoplen);
195 if (result) {
196 return result;
197 }
198 if (stoplen != len) {
199 /* Single block, can't legally truncate it. */
200 return EFAULT;
201 }
202
203 curthread->t_machdep.tm_badfaultfunc = copyfail;
204
205 result = setjmp(curthread->t_machdep.tm_copyjmp);
206 if (result) {
207 curthread->t_machdep.tm_badfaultfunc = NULL;
208 return EFAULT;
209 }
210
211 memcpy((void *)userdest, src, len);
212
213 curthread->t_machdep.tm_badfaultfunc = NULL;
214 return 0;
215 }
216
217 /*
218 * Common string copying function that behaves the way that's desired
219 * for copyinstr and copyoutstr.
220 *
221 * Copies a null-terminated string of maximum length MAXLEN from SRC
222 * to DEST. If GOTLEN is not null, store the actual length found
223 * there. Both lengths include the null-terminator. If the string
224 * exceeds the available length, the call fails and returns
225 * ENAMETOOLONG.
226 *
227 * STOPLEN is like MAXLEN but is assumed to have come from copycheck.
228 * If we hit MAXLEN it's because the string is too long to fit; if we
229 * hit STOPLEN it's because the string has run into the end of
230 * userspace. Thus in the latter case we return EFAULT, not
231 * ENAMETOOLONG.
232 */
233 static
234 int
235 copystr(char *dest, const char *src, size_t maxlen, size_t stoplen,
236 size_t *gotlen)
237 {
238 size_t i;
239
240 for (i=0; i<maxlen && i<stoplen; i++) {
241 dest[i] = src[i];
242 if (src[i] == 0) {
243 if (gotlen != NULL) {
244 *gotlen = i+1;
245 }
246 return 0;
247 }
248 }
249 if (stoplen < maxlen) {
250 /* ran into user-kernel boundary */
251 return EFAULT;
252 }
253 /* otherwise just ran out of space */
254 return ENAMETOOLONG;
255 }
256
257 /*
258 * copyinstr
259 *
260 * Copy a string from user-level address USERSRC to kernel address
261 * DEST, as per copystr above. Uses the tm_badfaultfunc/copyfail
262 * logic to protect against invalid addresses supplied by a user
263 * process.
264 */
265 int
266 copyinstr(const_userptr_t usersrc, char *dest, size_t len, size_t *actual)
267 {
268 int result;
269 size_t stoplen;
270
271 result = copycheck(usersrc, len, &stoplen);
272 if (result) {
273 return result;
274 }
275
276 curthread->t_machdep.tm_badfaultfunc = copyfail;
277
278 result = setjmp(curthread->t_machdep.tm_copyjmp);
279 if (result) {
280 curthread->t_machdep.tm_badfaultfunc = NULL;
281 return EFAULT;
282 }
283
284 result = copystr(dest, (const char *)usersrc, len, stoplen, actual);
285
286 curthread->t_machdep.tm_badfaultfunc = NULL;
287 return result;
288 }
289
290 /*
291 * copyoutstr
292 *
293 * Copy a string from kernel address SRC to user-level address
294 * USERDEST, as per copystr above. Uses the tm_badfaultfunc/copyfail
295 * logic to protect against invalid addresses supplied by a user
296 * process.
297 */
298 int
299 copyoutstr(const char *src, userptr_t userdest, size_t len, size_t *actual)
300 {
301 int result;
302 size_t stoplen;
303
304 result = copycheck(userdest, len, &stoplen);
305 if (result) {
306 return result;
307 }
308
309 curthread->t_machdep.tm_badfaultfunc = copyfail;
310
311 result = setjmp(curthread->t_machdep.tm_copyjmp);
312 if (result) {
313 curthread->t_machdep.tm_badfaultfunc = NULL;
314 return EFAULT;
315 }
316
317 result = copystr((char *)userdest, src, len, stoplen, actual);
318
319 curthread->t_machdep.tm_badfaultfunc = NULL;
320 return result;
321 }