]> git.saurik.com Git - apple/xnu.git/blame - osfmk/arm64/copyio.c
xnu-7195.81.3.tar.gz
[apple/xnu.git] / osfmk / arm64 / copyio.c
CommitLineData
5ba3f43e 1/*
f427ee49 2 * Copyright (c) 2012-2020 Apple Inc. All rights reserved.
5ba3f43e
A
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>
f427ee49 32#include <kern/zalloc_internal.h>
5ba3f43e
A
33#include <sys/errno.h>
34#include <vm/pmap.h>
35#include <vm/vm_map.h>
36#include <san/kasan.h>
37
d9a64523
A
38#undef copyin
39#undef copyout
40
5ba3f43e
A
41extern int _bcopyin(const char *src, char *dst, vm_size_t len);
42extern int _bcopyinstr(const char *src, char *dst, vm_size_t max, vm_size_t *actual);
43extern int _bcopyout(const char *src, char *dst, vm_size_t len);
cb323159
A
44extern int _copyin_atomic32(const char *src, uint32_t *dst);
45extern int _copyin_atomic32_wait_if_equals(const char *src, uint32_t dst);
46extern int _copyin_atomic64(const char *src, uint64_t *dst);
47extern int _copyout_atomic32(uint32_t u32, const char *dst);
48extern int _copyout_atomic64(uint64_t u64, const char *dst);
49
50extern int copyoutstr_prevalidate(const void *kaddr, user_addr_t uaddr, size_t len);
5ba3f43e
A
51
52extern pmap_t kernel_pmap;
5ba3f43e 53
f427ee49
A
54extern const vm_map_address_t physmap_base;
55extern const vm_map_address_t physmap_end;
d9a64523 56
cb323159
A
57/*!
58 * @typedef copyio_flags_t
59 *
60 * @const COPYIO_IN
61 * The copy is user -> kernel.
62 * One of COPYIO_IN or COPYIO_OUT should always be specified.
63 *
64 * @const COPYIO_OUT
65 * The copy is kernel -> user
66 * One of COPYIO_IN or COPYIO_OUT should always be specified.
67 *
68 * @const COPYIO_ALLOW_KERNEL_TO_KERNEL
69 * The "user_address" is allowed to be in the VA space of the kernel.
70 *
71 * @const COPYIO_VALIDATE_USER_ONLY
72 * There isn't really a kernel address used, and only the user address
73 * needs to be validated.
74 *
75 * @const COPYIO_ATOMIC
76 * The copyio operation is atomic, ensure that it is properly aligned.
77 */
78__options_decl(copyio_flags_t, uint32_t, {
79 COPYIO_IN = 0x0001,
80 COPYIO_OUT = 0x0002,
81 COPYIO_ALLOW_KERNEL_TO_KERNEL = 0x0004,
82 COPYIO_VALIDATE_USER_ONLY = 0x0008,
83 COPYIO_ATOMIC = 0x0010,
84});
5ba3f43e 85
5ba3f43e
A
86static inline void
87user_access_enable(void)
88{
89#if __ARM_PAN_AVAILABLE__
cb323159 90 assert(__builtin_arm_rsr("pan") != 0);
cc8bc92a 91 __builtin_arm_wsr("pan", 0);
5ba3f43e
A
92#endif /* __ARM_PAN_AVAILABLE__ */
93}
94
95static inline void
96user_access_disable(void)
97{
98#if __ARM_PAN_AVAILABLE__
cc8bc92a 99 __builtin_arm_wsr("pan", 1);
5ba3f43e
A
100#endif /* __ARM_PAN_AVAILABLE__ */
101}
102
cb323159
A
103/*
104 * Copy sizes bigger than this value will cause a kernel panic.
105 *
106 * Yes, this is an arbitrary fixed limit, but it's almost certainly
107 * a programming error to be copying more than this amount between
108 * user and wired kernel memory in a single invocation on this
109 * platform.
110 */
111const int copysize_limit_panic = (64 * 1024 * 1024);
112
113static inline bool
114is_kernel_to_kernel_copy()
115{
116 return current_thread()->map->pmap == kernel_pmap;
117}
118
119/*
120 * Validate the arguments to copy{in,out} on this platform.
121 *
122 * Returns EXDEV when the current thread pmap is the kernel's
123 * which is non fatal for certain routines.
124 */
5ba3f43e 125static int
cb323159
A
126copy_validate(const user_addr_t user_addr, uintptr_t kernel_addr,
127 vm_size_t nbytes, copyio_flags_t flags)
5ba3f43e 128{
cb323159 129 thread_t self = current_thread();
5ba3f43e 130
cb323159
A
131 user_addr_t user_addr_last;
132 uintptr_t kernel_addr_last;
133
134 if (__improbable(nbytes > copysize_limit_panic)) {
135 panic("%s(%p, %p, %lu) - transfer too large", __func__,
136 (void *)user_addr, (void *)kernel_addr, nbytes);
137 }
138
139 if (__improbable((user_addr < vm_map_min(self->map)) ||
140 os_add_overflow(user_addr, nbytes, &user_addr_last) ||
141 (user_addr_last > vm_map_max(self->map)))) {
142 return EFAULT;
143 }
144
145 if (flags & COPYIO_ATOMIC) {
146 if (__improbable(user_addr & (nbytes - 1))) {
5ba3f43e 147 return EINVAL;
0a7de745 148 }
5ba3f43e
A
149 }
150
cb323159 151 if ((flags & COPYIO_VALIDATE_USER_ONLY) == 0) {
f427ee49
A
152 if (__improbable(os_add_overflow(kernel_addr, nbytes, &kernel_addr_last))) {
153 panic("%s(%p, %p, %lu) - kaddr not in kernel", __func__,
154 (void *)user_addr, (void *)kernel_addr, nbytes);
155 }
156
157 bool in_kva = (kernel_addr >= VM_MIN_KERNEL_ADDRESS) && (kernel_addr_last <= VM_MAX_KERNEL_ADDRESS);
158 bool in_physmap = (kernel_addr >= physmap_base) && (kernel_addr_last <= physmap_end);
159
160 if (__improbable(!(in_kva || in_physmap))) {
cb323159
A
161 panic("%s(%p, %p, %lu) - kaddr not in kernel", __func__,
162 (void *)user_addr, (void *)kernel_addr, nbytes);
d9a64523 163 }
cb323159
A
164 }
165
166 if (is_kernel_to_kernel_copy()) {
167 if (__improbable((flags & COPYIO_ALLOW_KERNEL_TO_KERNEL) == 0)) {
168 return EFAULT;
0a7de745 169 }
cb323159 170 return EXDEV;
5ba3f43e
A
171 }
172
cb323159
A
173 if (__improbable(user_addr & TBI_MASK)) {
174 return EINVAL;
5ba3f43e 175 }
5ba3f43e 176
cb323159 177 if ((flags & COPYIO_VALIDATE_USER_ONLY) == 0) {
f427ee49
A
178 if (__probable(!zalloc_disable_copyio_check)) {
179 zone_t src_zone = NULL;
180 vm_size_t kernel_buf_size = zone_element_size((void *)kernel_addr, &src_zone);
181 /*
182 * Size of elements in the permanent zone is not saved as a part of the
183 * zone's info
184 */
185 if (__improbable(src_zone && !src_zone->permanent &&
186 kernel_buf_size < nbytes)) {
cb323159
A
187 panic("copyio_preflight: kernel buffer 0x%lx has size %lu < nbytes %lu",
188 kernel_addr, kernel_buf_size, nbytes);
189 }
190 }
5ba3f43e 191
cb323159
A
192#if KASAN
193 /* For user copies, asan-check the kernel-side buffer */
194 if (flags & COPYIO_IN) {
195 __asan_storeN(kernel_addr, nbytes);
196 } else {
197 __asan_loadN(kernel_addr, nbytes);
198 kasan_check_uninitialized((vm_address_t)kernel_addr, nbytes);
5ba3f43e 199 }
cb323159 200#endif
5ba3f43e 201 }
cb323159 202 return 0;
5ba3f43e
A
203}
204
205int
206copyin_kern(const user_addr_t user_addr, char *kernel_addr, vm_size_t nbytes)
207{
208 bcopy((const char*)(uintptr_t)user_addr, kernel_addr, nbytes);
209
210 return 0;
211}
212
213int
214copyout_kern(const char *kernel_addr, user_addr_t user_addr, vm_size_t nbytes)
215{
216 bcopy(kernel_addr, (char *)(uintptr_t)user_addr, nbytes);
217
218 return 0;
219}
220
221int
d9a64523 222copyin(const user_addr_t user_addr, void *kernel_addr, vm_size_t nbytes)
5ba3f43e
A
223{
224 int result;
225
cb323159 226 if (__improbable(nbytes == 0)) {
d9a64523 227 return 0;
0a7de745 228 }
5ba3f43e 229
cb323159
A
230 result = copy_validate(user_addr, (uintptr_t)kernel_addr, nbytes,
231 COPYIO_IN | COPYIO_ALLOW_KERNEL_TO_KERNEL);
232 if (result == EXDEV) {
233 return copyin_kern(user_addr, kernel_addr, nbytes);
234 }
235 if (__improbable(result)) {
0a7de745
A
236 return result;
237 }
5ba3f43e 238
cb323159
A
239 user_access_enable();
240 result = _bcopyin((const char *)user_addr, kernel_addr, nbytes);
241 user_access_disable();
242 return result;
5ba3f43e
A
243}
244
245/*
cb323159
A
246 * copy{in,out}_atomic{32,64}
247 * Read or store an aligned value from userspace as a single memory transaction.
248 * These functions support userspace synchronization features
5ba3f43e
A
249 */
250int
cb323159 251copyin_atomic32(const user_addr_t user_addr, uint32_t *kernel_addr)
5ba3f43e 252{
cb323159
A
253 int result = copy_validate(user_addr, (uintptr_t)kernel_addr, 4,
254 COPYIO_IN | COPYIO_ATOMIC);
255 if (__improbable(result)) {
256 return result;
257 }
258 user_access_enable();
259 result = _copyin_atomic32((const char *)user_addr, kernel_addr);
260 user_access_disable();
261 return result;
262}
5ba3f43e 263
cb323159
A
264int
265copyin_atomic32_wait_if_equals(const user_addr_t user_addr, uint32_t value)
266{
267 int result = copy_validate(user_addr, 0, 4,
268 COPYIO_OUT | COPYIO_ATOMIC | COPYIO_VALIDATE_USER_ONLY);
269 if (__improbable(result)) {
270 return result;
0a7de745 271 }
cb323159
A
272 user_access_enable();
273 result = _copyin_atomic32_wait_if_equals((const char *)user_addr, value);
274 user_access_disable();
275 return result;
276}
5ba3f43e 277
cb323159
A
278int
279copyin_atomic64(const user_addr_t user_addr, uint64_t *kernel_addr)
280{
281 int result = copy_validate(user_addr, (uintptr_t)kernel_addr, 8,
282 COPYIO_IN | COPYIO_ATOMIC);
283 if (__improbable(result)) {
284 return result;
0a7de745 285 }
cb323159
A
286 user_access_enable();
287 result = _copyin_atomic64((const char *)user_addr, kernel_addr);
288 user_access_disable();
289 return result;
290}
5ba3f43e 291
cb323159
A
292int
293copyout_atomic32(uint32_t value, user_addr_t user_addr)
294{
295 int result = copy_validate(user_addr, 0, 4,
296 COPYIO_OUT | COPYIO_ATOMIC | COPYIO_VALIDATE_USER_ONLY);
297 if (__improbable(result)) {
5ba3f43e 298 return result;
0a7de745 299 }
cb323159
A
300 user_access_enable();
301 result = _copyout_atomic32(value, (const char *)user_addr);
302 user_access_disable();
303 return result;
304}
5ba3f43e 305
cb323159
A
306int
307copyout_atomic64(uint64_t value, user_addr_t user_addr)
308{
309 int result = copy_validate(user_addr, 0, 8,
310 COPYIO_OUT | COPYIO_ATOMIC | COPYIO_VALIDATE_USER_ONLY);
311 if (__improbable(result)) {
312 return result;
313 }
314 user_access_enable();
315 result = _copyout_atomic64(value, (const char *)user_addr);
316 user_access_disable();
317 return result;
5ba3f43e
A
318}
319
320int
321copyinstr(const user_addr_t user_addr, char *kernel_addr, vm_size_t nbytes, vm_size_t *lencopied)
322{
323 int result;
cb323159 324 vm_size_t bytes_copied = 0;
5ba3f43e 325
d9a64523 326 *lencopied = 0;
cb323159 327 if (__improbable(nbytes == 0)) {
d9a64523 328 return ENAMETOOLONG;
0a7de745 329 }
5ba3f43e 330
cb323159
A
331 result = copy_validate(user_addr, (uintptr_t)kernel_addr, nbytes, COPYIO_IN);
332 if (__improbable(result)) {
0a7de745
A
333 return result;
334 }
cb323159
A
335 user_access_enable();
336 result = _bcopyinstr((const char *)user_addr, kernel_addr, nbytes,
337 &bytes_copied);
338 user_access_disable();
339 if (result != EFAULT) {
340 *lencopied = bytes_copied;
341 }
342 return result;
5ba3f43e
A
343}
344
345int
346copyout(const void *kernel_addr, user_addr_t user_addr, vm_size_t nbytes)
347{
348 int result;
349
0a7de745 350 if (nbytes == 0) {
d9a64523 351 return 0;
0a7de745 352 }
5ba3f43e 353
cb323159
A
354 result = copy_validate(user_addr, (uintptr_t)kernel_addr, nbytes,
355 COPYIO_OUT | COPYIO_ALLOW_KERNEL_TO_KERNEL);
356 if (result == EXDEV) {
d9a64523 357 return copyout_kern(kernel_addr, user_addr, nbytes);
0a7de745 358 }
cb323159
A
359 if (__improbable(result)) {
360 return result;
361 }
362 user_access_enable();
363 result = _bcopyout(kernel_addr, (char *)user_addr, nbytes);
364 user_access_disable();
365 return result;
5ba3f43e
A
366}
367
cb323159
A
368int
369copyoutstr_prevalidate(const void *__unused kaddr, user_addr_t __unused uaddr, size_t __unused len)
5ba3f43e 370{
cb323159 371 if (__improbable(is_kernel_to_kernel_copy())) {
0a7de745
A
372 return EFAULT;
373 }
5ba3f43e 374
0a7de745 375 return 0;
5ba3f43e 376}