]> git.saurik.com Git - apple/xnu.git/blob - osfmk/arm64/copyio.c
2e47825ddeea49d5103d333b28af23aefa143210
[apple/xnu.git] / osfmk / arm64 / copyio.c
1 /*
2 * Copyright (c) 2012-2013 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29 #include <arm/cpu_data_internal.h>
30 #include <arm/misc_protos.h>
31 #include <kern/thread.h>
32 #include <sys/errno.h>
33 #include <vm/pmap.h>
34 #include <vm/vm_map.h>
35 #include <san/kasan.h>
36
37 extern int _bcopyin(const char *src, char *dst, vm_size_t len);
38 extern int _bcopyinstr(const char *src, char *dst, vm_size_t max, vm_size_t *actual);
39 extern int _bcopyout(const char *src, char *dst, vm_size_t len);
40 extern int _copyin_word(const char *src, uint64_t *dst, vm_size_t len);
41
42 extern pmap_t kernel_pmap;
43
44 typedef enum copyio_type {
45 COPYIO_IN,
46 COPYIO_IN_WORD,
47 COPYIO_INSTR,
48 COPYIO_OUT,
49 } copyio_type_t;
50
51 int
52 copyio_check_user_addr(user_addr_t user_addr, vm_size_t nbytes)
53 {
54 if (nbytes && (user_addr + nbytes <= user_addr))
55 return EFAULT;
56
57 if ((user_addr + nbytes) > vm_map_max(current_thread()->map))
58 return EFAULT;
59
60 return 0;
61 }
62
63 static inline void
64 user_access_enable(void)
65 {
66 #if __ARM_PAN_AVAILABLE__
67 __builtin_arm_wsr("pan", 0);
68 #endif /* __ARM_PAN_AVAILABLE__ */
69 }
70
71 static inline void
72 user_access_disable(void)
73 {
74 #if __ARM_PAN_AVAILABLE__
75 __builtin_arm_wsr("pan", 1);
76 #endif /* __ARM_PAN_AVAILABLE__ */
77 }
78
79 static int
80 copyio(copyio_type_t copytype, const char *src, char *dst,
81 vm_size_t nbytes, vm_size_t *lencopied)
82 {
83 int result = 0;
84 vm_size_t bytes_copied = 0;
85
86 /* Reject TBI addresses */
87 if (copytype == COPYIO_OUT) {
88 if ((uintptr_t)dst & TBI_MASK)
89 return EINVAL;
90 } else {
91 if ((uintptr_t)src & TBI_MASK)
92 return EINVAL;
93 }
94
95 if (!nbytes) {
96 return 0;
97 }
98
99 #if KASAN
100 /* For user copies, asan-check the kernel-side buffer */
101 if (copytype == COPYIO_IN || copytype == COPYIO_INSTR || copytype == COPYIO_IN_WORD) {
102 __asan_storeN((uintptr_t)dst, nbytes);
103 } else if (copytype == COPYIO_OUT) {
104 __asan_loadN((uintptr_t)src, nbytes);
105 }
106 #endif
107
108 user_access_enable();
109
110 /* Select copy routines based on direction:
111 * COPYIO_IN - Use unprivileged loads to read from user address
112 * COPYIO_OUT - Use unprivleged stores to write to user address
113 */
114
115 switch (copytype) {
116 case COPYIO_IN:
117 result = _bcopyin(src, dst, nbytes);
118 break;
119 case COPYIO_INSTR:
120 result = _bcopyinstr(src, dst, nbytes, &bytes_copied);
121 if (result != EFAULT) {
122 *lencopied = bytes_copied;
123 }
124 break;
125 case COPYIO_IN_WORD:
126 result = _copyin_word(src, (uint64_t *)(uintptr_t)dst, nbytes);
127 break;
128 case COPYIO_OUT:
129 result = _bcopyout(src, dst, nbytes);
130 break;
131 default:
132 result = EINVAL;
133 }
134
135 user_access_disable();
136 return result;
137 }
138
139 int
140 copyin_kern(const user_addr_t user_addr, char *kernel_addr, vm_size_t nbytes)
141 {
142 bcopy((const char*)(uintptr_t)user_addr, kernel_addr, nbytes);
143
144 return 0;
145 }
146
147 int
148 copyout_kern(const char *kernel_addr, user_addr_t user_addr, vm_size_t nbytes)
149 {
150 bcopy(kernel_addr, (char *)(uintptr_t)user_addr, nbytes);
151
152 return 0;
153 }
154
155 int
156 copyin(const user_addr_t user_addr, char *kernel_addr, vm_size_t nbytes)
157 {
158 int result;
159
160 if (user_addr >= VM_MIN_KERNEL_ADDRESS || user_addr + nbytes >= VM_MIN_KERNEL_ADDRESS) {
161 if (current_thread()->map->pmap == kernel_pmap)
162 return copyin_kern(user_addr, kernel_addr, nbytes);
163 else
164 return EFAULT;
165 }
166
167 if (nbytes >= 4096) {
168 result = copyin_validate(user_addr, (uintptr_t)kernel_addr, nbytes);
169 if (result) return result;
170 }
171
172 result = copyio_check_user_addr(user_addr, nbytes);
173
174 if (result) return result;
175
176 return copyio(COPYIO_IN, (const char *)(uintptr_t)user_addr, kernel_addr, nbytes, NULL);
177 }
178
179 /*
180 * copyin_word
181 * Read an aligned value from userspace as a single memory transaction.
182 * This function supports userspace synchronization features
183 */
184 int
185 copyin_word(const user_addr_t user_addr, uint64_t *kernel_addr, vm_size_t nbytes)
186 {
187 int result;
188
189 /* Verify sizes */
190 if ((nbytes != 4) && (nbytes != 8))
191 return EINVAL;
192
193 /* Test alignment */
194 if (user_addr & (nbytes - 1))
195 return EINVAL;
196
197 /* Address must be user */
198 if (user_addr >= VM_MIN_KERNEL_ADDRESS || user_addr + nbytes >= VM_MIN_KERNEL_ADDRESS)
199 return EFAULT;
200
201 result = copyio_check_user_addr(user_addr, nbytes);
202 if (result)
203 return result;
204
205 return copyio(COPYIO_IN_WORD, (const char *)user_addr, (char *)(uintptr_t)kernel_addr, nbytes, NULL);
206 }
207
208 int
209 copyinstr(const user_addr_t user_addr, char *kernel_addr, vm_size_t nbytes, vm_size_t *lencopied)
210 {
211 int result;
212
213 if (user_addr >= VM_MIN_KERNEL_ADDRESS || user_addr + nbytes >= VM_MIN_KERNEL_ADDRESS) {
214 return EFAULT;
215 }
216
217 result = copyio_check_user_addr(user_addr, nbytes);
218
219 if (result) return result;
220
221 if (!nbytes) {
222 return ENAMETOOLONG;
223 }
224
225 return copyio(COPYIO_INSTR, (const char *)(uintptr_t)user_addr, kernel_addr, nbytes, lencopied);
226 }
227
228 int
229 copyout(const void *kernel_addr, user_addr_t user_addr, vm_size_t nbytes)
230 {
231 int result;
232
233 if (user_addr >= VM_MIN_KERNEL_ADDRESS || user_addr + nbytes >= VM_MIN_KERNEL_ADDRESS) {
234 if (current_thread()->map->pmap == kernel_pmap)
235 return copyout_kern(kernel_addr, user_addr, nbytes);
236 else
237 return EFAULT;
238 }
239
240 if (nbytes >= 4096) {
241 result = copyout_validate((uintptr_t)kernel_addr, user_addr, nbytes);
242 if (result) return result;
243 }
244
245 result = copyio_check_user_addr(user_addr, nbytes);
246
247 if (result) return result;
248
249 return copyio(COPYIO_OUT, kernel_addr, (char *)(uintptr_t)user_addr, nbytes, NULL);
250 }
251
252
253 /*
254 * Copy sizes bigger than this value will cause a kernel panic.
255 *
256 * Yes, this is an arbitrary fixed limit, but it's almost certainly
257 * a programming error to be copying more than this amount between
258 * user and wired kernel memory in a single invocation on this
259 * platform.
260 */
261 const int copysize_limit_panic = (64 * 1024 * 1024);
262
263 /*
264 * Validate the arguments to copy{in,out} on this platform.
265 *
266 * Called when nbytes is "large" e.g. more than a page. Such sizes are
267 * infrequent, and very large sizes are likely indications of attempts
268 * to exploit kernel programming errors (bugs).
269 */
270 static int
271 copy_validate(const user_addr_t user_addr,
272 uintptr_t kernel_addr, vm_size_t nbytes)
273 {
274 uintptr_t kernel_addr_last = kernel_addr + nbytes;
275
276 if (kernel_addr < VM_MIN_KERNEL_ADDRESS ||
277 kernel_addr > VM_MAX_KERNEL_ADDRESS ||
278 kernel_addr_last < kernel_addr ||
279 kernel_addr_last > VM_MAX_KERNEL_ADDRESS)
280 panic("%s(%p, %p, %lu) - kaddr not in kernel", __func__,
281 (void *)user_addr, (void *)kernel_addr, nbytes);
282
283 user_addr_t user_addr_last = user_addr + nbytes;
284
285 if (user_addr_last < user_addr || user_addr_last > VM_MIN_KERNEL_ADDRESS)
286 return (EFAULT);
287
288 if (__improbable(nbytes > copysize_limit_panic))
289 panic("%s(%p, %p, %lu) - transfer too large", __func__,
290 (void *)user_addr, (void *)kernel_addr, nbytes);
291
292 return (0);
293 }
294
295 int
296 copyin_validate(const user_addr_t ua, uintptr_t ka, vm_size_t nbytes)
297 {
298 return (copy_validate(ua, ka, nbytes));
299 }
300
301 int
302 copyout_validate(uintptr_t ka, const user_addr_t ua, vm_size_t nbytes)
303 {
304 return (copy_validate(ua, ka, nbytes));
305 }
306