2 * Copyright (c) 2019 Apple Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
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.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
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.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
29 #include <kern/kalloc.h>
30 #include <kern/task.h>
31 #include <kern/thread.h>
32 #include <libkern/libkern.h>
33 #include <mach/mach_vm.h>
34 #include <mach/semaphore.h>
35 #include <mach/task.h>
36 #include <vm/vm_kern.h>
37 #include <vm/vm_map.h>
38 #include <vm/vm_protos.h>
39 #include <sys/errno.h>
41 #include <sys/proc_internal.h>
43 #include <tests/ktest.h>
45 kern_return_t
copyio_test(void);
47 #define copyio_test_buf_size (PAGE_SIZE * 16)
48 static const char copyio_test_string
[] = {'T', 'e', 's', 't', ' ', 'S', 't', 'r', 'i', 'n', 'g', '!', '\0', 'A', 'B', 'C'};
50 struct copyio_test_data
{
51 /* VM map of the current userspace process. */
53 /* The start of a `copyio_test_buf_size'-sized region mapped into userspace. */
54 user_addr_t user_addr
;
55 /* The start of a page-sized region that guaranteed to be unmapped in userspace. */
56 user_addr_t unmapped_addr
;
57 /* The start of a page-sized region mapped at the largest possible userspace address. */
58 user_addr_t user_lastpage_addr
;
59 /* Kernel mapping of the physical pages mapped at `user_addr'. */
62 /* Scratch buffers of size `copyio_test_buf_size'. */
64 /* Scratch data to pass to helper threads */
71 typedef int (*copyio_thread_fn_t
)(struct copyio_test_data
*);
73 struct copyio_test_thread_data
{
74 copyio_thread_fn_t fn
;
75 struct copyio_test_data
*data
;
81 copyio_thread_call_fn(void *arg
, wait_result_t __unused res
)
83 struct copyio_test_thread_data
*tdata
= arg
;
84 tdata
->ret
= tdata
->fn(tdata
->data
);
85 semaphore_signal(tdata
->done
);
89 copyio_test_run_in_thread(copyio_thread_fn_t fn
, struct copyio_test_data
*data
)
91 struct copyio_test_thread_data tdata
= {
97 semaphore_create(current_task(), &tdata
.done
, SYNC_POLICY_FIFO
, 0);
98 kernel_thread_start(copyio_thread_call_fn
, &tdata
, &thread
);
100 semaphore_wait(tdata
.done
);
102 thread_deallocate(thread
);
103 semaphore_destroy(current_task(), tdata
.done
);
109 copyio_test_protect(struct copyio_test_data
*data
, vm_prot_t prot
)
111 kern_return_t ret
= mach_vm_protect(data
->user_map
, data
->user_addr
, copyio_test_buf_size
, false, prot
);
112 assert(ret
== KERN_SUCCESS
);
116 copyin_from_kernel(struct copyio_test_data
*data
)
118 char *in_buf
= data
->buf2
;
119 return copyin((uintptr_t)data
->kern_addr
, in_buf
, copyio_test_buf_size
);
123 copyin_test(struct copyio_test_data
*data
)
125 char *out_buf
= data
->buf1
;
126 char *in_buf
= data
->buf2
;
128 for (size_t i
= 0; i
< copyio_test_buf_size
; i
++) {
129 out_buf
[i
] = (char)i
;
131 memcpy(data
->kern_addr
, out_buf
, copyio_test_buf_size
);
133 int err
= copyin(data
->user_addr
, in_buf
, copyio_test_buf_size
);
134 T_EXPECT_EQ_INT(err
, 0, "copyin() with valid parameters should succeed");
135 int cmp
= memcmp(out_buf
, in_buf
, copyio_test_buf_size
);
136 T_EXPECT_EQ_INT(cmp
, 0, "copyin() should correctly copy in data");
138 err
= copyin(data
->unmapped_addr
, NULL
, 0);
139 T_EXPECT_EQ_INT(err
, 0, "copyin() with 0 size should always succeed");
141 err
= copyin(data
->unmapped_addr
, in_buf
, copyio_test_buf_size
);
142 T_EXPECT_EQ_INT(err
, EFAULT
, "copyin() from unmapped userspace address should return EFAULT");
143 err
= copyin(data
->unmapped_addr
- PAGE_SIZE
, in_buf
, PAGE_SIZE
* 2);
144 T_EXPECT_EQ_INT(err
, EFAULT
, "copyin() from partially valid userspace range should return EFAULT");
145 err
= copyin(data
->user_lastpage_addr
, in_buf
, PAGE_SIZE
* 2);
146 T_EXPECT_EQ_INT(err
, EFAULT
, "copyin() past end of userspace address space should return EFAULT");
148 bzero(in_buf
, copyio_test_buf_size
);
149 err
= copyio_test_run_in_thread(copyin_from_kernel
, data
);
150 T_EXPECT_EQ_INT(err
, 0, "copyin() from kernel address in kernel_task thread should succeed");
151 cmp
= memcmp(data
->kern_addr
, in_buf
, copyio_test_buf_size
);
152 T_EXPECT_EQ_INT(cmp
, 0, "copyin() from kernel address should correctly copy in data");
153 err
= copyin_from_kernel(data
);
154 T_EXPECT_EQ_INT(err
, EFAULT
, "copyin() from kernel address in other threads should return EFAULT");
156 copyio_test_protect(data
, VM_PROT_WRITE
);
157 err
= copyin(data
->user_addr
, in_buf
, copyio_test_buf_size
);
158 T_EXPECT_EQ_INT(err
, EFAULT
, "copyin() from write-only address should return EFAULT");
159 copyio_test_protect(data
, VM_PROT_READ
| VM_PROT_WRITE
);
163 copyout_to_kernel(struct copyio_test_data
*data
)
165 char *out_buf
= data
->buf1
;
166 return copyout(out_buf
, (uintptr_t)data
->kern_addr
, copyio_test_buf_size
);
170 copyout_test(struct copyio_test_data
*data
)
172 char *out_buf
= data
->buf1
;
174 bzero(data
->kern_addr
, copyio_test_buf_size
);
176 for (size_t i
= 0; i
< copyio_test_buf_size
; i
++) {
177 out_buf
[i
] = ~(char)i
;
179 int err
= copyout(out_buf
, data
->user_addr
, copyio_test_buf_size
);
180 T_EXPECT_EQ_INT(err
, 0, "copyout() with valid parameters should succeed");
182 int cmp
= memcmp(data
->kern_addr
, out_buf
, copyio_test_buf_size
);
183 T_EXPECT_EQ_INT(cmp
, 0, "copyout() should correctly copy out data");
185 err
= copyout(NULL
, data
->unmapped_addr
, 0);
186 T_EXPECT_EQ_INT(err
, 0, "copyout() with 0 size should always succeed");
188 err
= copyout(out_buf
, data
->unmapped_addr
, copyio_test_buf_size
);
189 T_EXPECT_EQ_INT(err
, EFAULT
, "copyout() to unmapped userspace address should return EFAULT");
190 err
= copyout(out_buf
, data
->unmapped_addr
- PAGE_SIZE
, PAGE_SIZE
* 2);
191 T_EXPECT_EQ_INT(err
, EFAULT
, "copyout() to partially valid userspace range should return EFAULT");
192 err
= copyout(out_buf
, data
->user_lastpage_addr
, PAGE_SIZE
* 2);
193 T_EXPECT_EQ_INT(err
, EFAULT
, "copyout() past end of userspace address space should return EFAULT");
195 bzero(data
->kern_addr
, copyio_test_buf_size
);
197 err
= copyio_test_run_in_thread(copyout_to_kernel
, data
);
198 T_EXPECT_EQ_INT(err
, 0, "copyout() to kernel address in kernel_task thread should succeed");
199 cmp
= memcmp(out_buf
, data
->kern_addr
, copyio_test_buf_size
);
200 T_EXPECT_EQ_INT(cmp
, 0, "copyout() to kernel address should correctly copy out data");
201 err
= copyout_to_kernel(data
);
202 T_EXPECT_EQ_INT(err
, EFAULT
, "copyout() to kernel address in other threads should return EFAULT");
204 copyio_test_protect(data
, VM_PROT_READ
);
205 err
= copyout(out_buf
, data
->user_addr
, copyio_test_buf_size
);
206 T_EXPECT_EQ_INT(err
, EFAULT
, "copyout() to read-only address should return EFAULT");
207 copyio_test_protect(data
, VM_PROT_READ
| VM_PROT_WRITE
);
211 copyinstr_from_kernel(struct copyio_test_data
*data
)
213 char *in_buf
= data
->buf1
;
214 size_t *lencopied
= data
->thread_ptr
;
215 return copyinstr((user_addr_t
)data
->kern_addr
, in_buf
, copyio_test_buf_size
, lencopied
);
219 copyinstr_test(struct copyio_test_data
*data
)
221 char *in_buf
= data
->buf1
;
223 memcpy(data
->kern_addr
, copyio_test_string
, sizeof(copyio_test_string
));
225 bzero(in_buf
, copyio_test_buf_size
);
227 int err
= copyinstr(data
->user_addr
, in_buf
, copyio_test_buf_size
, &lencopied
);
228 T_EXPECT_EQ_INT(err
, 0, "copyinstr() with valid parameters should succeed");
229 T_EXPECT_EQ_ULONG(lencopied
, strlen(copyio_test_string
) + 1, "copyinstr() with a large enough buffer should read entire string");
231 int cmp
= strncmp(in_buf
, copyio_test_string
, lencopied
);
232 T_EXPECT_EQ_INT(cmp
, 0, "copyinstr() should correctly copy string up to NULL terminator");
233 cmp
= memcmp(in_buf
, copyio_test_string
, sizeof(copyio_test_string
));
234 T_EXPECT_NE_INT(cmp
, 0, "copyinstr() should not read past NULL terminator");
236 bzero(in_buf
, copyio_test_buf_size
);
237 const vm_size_t trunc_size
= strlen(copyio_test_string
) - 4;
238 err
= copyinstr(data
->user_addr
, in_buf
, trunc_size
, &lencopied
);
239 T_EXPECT_EQ_INT(err
, ENAMETOOLONG
, "truncated copyinstr() should return ENAMETOOLONG");
240 T_EXPECT_EQ_ULONG(lencopied
, trunc_size
, "truncated copyinstr() should copy exactly `maxlen' bytes");
241 cmp
= memcmp(in_buf
, copyio_test_string
, trunc_size
);
242 T_EXPECT_EQ_INT(cmp
, 0, "copyinstr() should correctly copy in truncated string");
243 cmp
= memcmp(in_buf
, copyio_test_string
, strlen(copyio_test_string
));
244 T_EXPECT_NE_INT(cmp
, 0, "copyinstr() should stop copying at `maxlen' bytes");
246 err
= copyinstr(data
->unmapped_addr
, in_buf
, copyio_test_buf_size
, &lencopied
);
247 T_EXPECT_EQ_INT(err
, EFAULT
, "copyinstr() from unmapped userspace address should return EFAULT");
248 err
= copyinstr(data
->user_lastpage_addr
, in_buf
, PAGE_SIZE
* 2, &lencopied
);
249 T_EXPECT_EQ_INT(err
, EFAULT
, "copyinstr() past end of userspace address space should return EFAULT");
251 bzero(in_buf
, copyio_test_buf_size
);
252 data
->thread_ptr
= &lencopied
;
254 err
= copyio_test_run_in_thread(copyinstr_from_kernel
, data
);
255 #if defined(__arm__) || defined (__arm64__)
256 T_EXPECT_EQ_INT(err
, EFAULT
, "copyinstr() from kernel address in kernel_task thread should return EFAULT");
258 T_EXPECT_EQ_INT(err
, 0, "copyinstr() from kernel address in kernel_task thread should succeed");
259 T_EXPECT_EQ_ULONG(lencopied
, strlen(copyio_test_string
) + 1, "copyinstr() from kernel address should read entire string");
260 cmp
= strncmp(in_buf
, copyio_test_string
, lencopied
);
261 T_EXPECT_EQ_INT(cmp
, 0, "copyinstr() from kernel address should correctly copy string up to NULL terminator");
262 cmp
= memcmp(in_buf
, copyio_test_string
, sizeof(copyio_test_string
));
263 T_EXPECT_NE_INT(cmp
, 0, "copyinstr() from kernel address should not read past NULL terminator");
265 err
= copyinstr_from_kernel(data
);
266 T_EXPECT_EQ_INT(err
, EFAULT
, "copyinstr() from kernel address in other threads should return EFAULT");
268 copyio_test_protect(data
, VM_PROT_WRITE
);
269 err
= copyinstr(data
->user_addr
, in_buf
, copyio_test_buf_size
, &lencopied
);
270 T_EXPECT_EQ_INT(err
, EFAULT
, "copyinstr() from write-only address should return EFAULT");
271 copyio_test_protect(data
, VM_PROT_READ
| VM_PROT_WRITE
);
273 /* Place an unterminated string at the end of the mapped region */
274 const size_t unterminated_size
= 16;
275 char *kern_unterminated_addr
= (char *)data
->kern_addr
+ copyio_test_buf_size
- unterminated_size
;
276 memset(kern_unterminated_addr
, 'A', unterminated_size
);
278 user_addr_t user_unterminated_addr
= data
->user_addr
+ copyio_test_buf_size
- unterminated_size
;
279 err
= copyinstr(user_unterminated_addr
, in_buf
, copyio_test_buf_size
, &lencopied
);
280 T_EXPECT_EQ_INT(err
, EFAULT
, "copyinstr() from userspace region without NULL terminator should return EFAULT");
284 copyoutstr_to_kernel(struct copyio_test_data
*data
)
286 size_t *lencopied
= data
->thread_ptr
;
287 return copyoutstr(copyio_test_string
, (user_addr_t
)data
->kern_addr
, sizeof(copyio_test_string
), lencopied
);
291 copyoutstr_test(struct copyio_test_data
*data
)
293 bzero(data
->kern_addr
, sizeof(copyio_test_string
));
296 int err
= copyoutstr(copyio_test_string
, data
->user_addr
, sizeof(copyio_test_string
), &lencopied
);
297 T_EXPECT_EQ_INT(err
, 0, "copyoutstr() with valid parameters should succeed");
298 T_EXPECT_EQ_ULONG(lencopied
, strlen(copyio_test_string
) + 1, "copyoutstr() should copy string up to NULL terminator");
300 int cmp
= strncmp(data
->kern_addr
, copyio_test_string
, sizeof(copyio_test_string
));
301 T_EXPECT_EQ_INT(cmp
, 0, "copyoutstr() should correctly copy out string");
302 cmp
= memcmp(data
->kern_addr
, copyio_test_string
, sizeof(copyio_test_string
));
303 T_EXPECT_NE_INT(cmp
, 0, "copyoutstr() should stop copying at NULL terminator");
305 bzero(data
->kern_addr
, sizeof(copyio_test_string
));
307 const vm_size_t trunc_size
= strlen(copyio_test_string
) - 4;
308 err
= copyoutstr(copyio_test_string
, data
->user_addr
, trunc_size
, &lencopied
);
309 T_EXPECT_EQ_INT(err
, ENAMETOOLONG
, "truncated copyoutstr() should return ENAMETOOLONG");
310 T_EXPECT_EQ_ULONG(lencopied
, trunc_size
, "truncated copyoutstr() should copy exactly `maxlen' bytes");
311 cmp
= strncmp(data
->kern_addr
, copyio_test_string
, trunc_size
);
312 T_EXPECT_EQ_INT(cmp
, 0, "copyoutstr() should correctly copy out truncated string");
313 cmp
= memcmp(data
->kern_addr
, copyio_test_string
, sizeof(copyio_test_string
));
314 T_EXPECT_NE_INT(cmp
, 0, "copyoutstr() should stop copying at `maxlen' bytes");
316 err
= copyoutstr(copyio_test_string
, data
->unmapped_addr
, strlen(copyio_test_string
), &lencopied
);
317 T_EXPECT_EQ_INT(err
, EFAULT
, "copyoutstr() to unmapped userspace address should return EFAULT");
318 err
= copyoutstr(copyio_test_string
, data
->unmapped_addr
- 1, strlen(copyio_test_string
), &lencopied
);
319 T_EXPECT_EQ_INT(err
, EFAULT
, "copyoutstr() to partially valid userspace range should return EFAULT");
320 err
= copyoutstr(copyio_test_string
, data
->user_lastpage_addr
+ PAGE_SIZE
- 1, strlen(copyio_test_string
), &lencopied
);
321 T_EXPECT_EQ_INT(err
, EFAULT
, "copyoutstr() past end of userspace address space should return EFAULT");
323 bzero(data
->kern_addr
, sizeof(copyio_test_string
));
324 data
->thread_ptr
= &lencopied
;
326 err
= copyio_test_run_in_thread(copyoutstr_to_kernel
, data
);
327 #if defined(__arm__) || defined (__arm64__)
328 T_EXPECT_EQ_INT(err
, EFAULT
, "copyoutstr() to kernel address in kernel_task thread should return EFAULT");
330 T_EXPECT_EQ_INT(err
, 0, "copyoutstr() to kernel address in kernel_task thread should succeed");
331 T_EXPECT_EQ_ULONG(lencopied
, strlen(copyio_test_string
) + 1, "copyoutstr() to kernel address should copy string up to NULL terminator");
332 cmp
= strncmp(data
->kern_addr
, copyio_test_string
, sizeof(copyio_test_string
));
333 T_EXPECT_EQ_INT(cmp
, 0, "copyoutstr() to kernel address should correctly copy out data");
335 err
= copyoutstr_to_kernel(data
);
336 T_EXPECT_EQ_INT(err
, EFAULT
, "copyoutstr() to kernel address in other threads should return EFAULT");
338 copyio_test_protect(data
, VM_PROT_READ
);
339 err
= copyoutstr(copyio_test_string
, data
->user_addr
, strlen(copyio_test_string
), &lencopied
);
340 T_EXPECT_EQ_INT(err
, EFAULT
, "copyoutstr() to read-only address should return EFAULT");
341 copyio_test_protect(data
, VM_PROT_READ
| VM_PROT_WRITE
);
345 copyin_atomic32_from_kernel(struct copyio_test_data
*data
)
347 return copyin_atomic32((uintptr_t)data
->kern_addr
, data
->thread_ptr
);
351 copyin_atomic64_from_kernel(struct copyio_test_data
*data
)
353 return copyin_atomic64((uintptr_t)data
->kern_addr
, data
->thread_ptr
);
357 copyout_atomic32_to_kernel(struct copyio_test_data
*data
)
359 return copyout_atomic32((uint32_t)data
->thread_data
, (user_addr_t
)data
->kern_addr
);
363 copyout_atomic64_to_kernel(struct copyio_test_data
*data
)
365 return copyout_atomic64(data
->thread_data
, (user_addr_t
)data
->kern_addr
);
369 * Note: we can't test atomic copyio calls which go past the end of the
370 * userspace address space, since there's no way to provide a range
371 * that straddles the userspace address boundary while being suitably
372 * aligned for the copy.
374 #define copyin_atomic_test(data, word_t, copyin_fn, copyin_from_kernel_fn) \
376 const word_t word_out = (word_t)0x123456789ABCDEF0UL; \
377 word_t word_in = 0; \
378 memcpy(data->kern_addr, &word_out, sizeof(word_out)); \
380 int err = copyin_fn(data->user_addr, &word_in); \
381 T_EXPECT_EQ_INT(err, 0, #copyin_fn "() with valid parameters should succeed"); \
383 int cmp = memcmp(&word_in, &word_out, sizeof(word_t)); \
384 T_EXPECT_EQ_INT(cmp, 0, #copyin_fn "() should correctly copy word"); \
386 for (unsigned int offset = 1; offset < sizeof(word_t); offset++) { \
387 err = copyin_fn(data->user_addr + offset, &word_in); \
388 T_EXPECT_EQ_INT(err, EINVAL, \
389 #copyin_fn "() from unaligned userspace address should return EINVAL (offset = %u)", \
392 err = copyin_fn(data->unmapped_addr, &word_in); \
393 T_EXPECT_EQ_INT(err, EFAULT, #copyin_fn "() from unmapped userspace address should return EFAULT"); \
395 data->thread_ptr = &word_in; \
397 err = copyio_test_run_in_thread(copyin_from_kernel_fn, data); \
398 T_EXPECT_EQ_INT(err, EFAULT, \
399 #copyin_fn "() from kernel address in kernel_task threads should return EFAULT"); \
400 err = copyin_from_kernel_fn(data); \
401 T_EXPECT_EQ_INT(err, EFAULT, \
402 #copyin_fn "() from kernel address in other threads should return EFAULT"); \
404 copyio_test_protect(data, VM_PROT_WRITE); \
405 err = copyin_fn(data->user_addr, &word_in); \
406 T_EXPECT_EQ_INT(err, EFAULT, #copyin_fn "() from write-only address should return EFAULT"); \
407 copyio_test_protect(data, VM_PROT_READ | VM_PROT_WRITE); \
410 #define copyout_atomic_test(data, word_t, copyout_fn, copyout_to_kernel_fn) \
412 const word_t word_out = (word_t)0x123456789ABCDEF0UL; \
413 bzero(data->kern_addr, sizeof(word_t)); \
415 int err = copyout_fn(word_out, data->user_addr); \
416 T_EXPECT_EQ_INT(err, 0, #copyout_fn "() with valid parameters should succeed"); \
418 int cmp = memcmp(data->kern_addr, &word_out, sizeof(word_t)); \
419 T_EXPECT_EQ_INT(cmp, 0, #copyout_fn "() should correctly copy word"); \
421 for (unsigned int offset = 1; offset < sizeof(word_t); offset++) { \
422 err = copyout_fn(word_out, data->user_addr + offset); \
423 T_EXPECT_EQ_INT(err, EINVAL, \
424 #copyout_fn "() to unaligned userspace address should return EINVAL (offset = %u)", \
427 err = copyout_fn(word_out, data->unmapped_addr); \
428 T_EXPECT_EQ_INT(err, EFAULT, #copyout_fn "() to unmapped userspace address should return EFAULT"); \
429 err = copyout_fn(word_out, (uintptr_t)data->kern_addr); \
430 T_EXPECT_EQ_INT(err, EFAULT, #copyout_fn "() to kernel address should return EFAULT"); \
432 data->thread_data = word_out; \
434 err = copyio_test_run_in_thread(copyout_to_kernel_fn, data); \
435 T_EXPECT_EQ_INT(err, EFAULT, \
436 #copyout_fn "() to kernel address in kernel_task thread should return EFAULT"); \
437 err = copyout_to_kernel_fn(data); \
438 T_EXPECT_EQ_INT(err, EFAULT, #copyout_fn "() to kernel address in other threads should return EFAULT"); \
440 copyio_test_protect(data, VM_PROT_READ); \
441 err = copyout_fn(word_out, data->user_addr); \
442 T_EXPECT_EQ_INT(err, EFAULT, #copyout_fn "() to read-only address should return EFAULT"); \
443 copyio_test_protect(data, VM_PROT_READ | VM_PROT_WRITE); \
446 #define copyio_atomic_test(data, size) \
448 copyin_atomic_test((data), uint ## size ## _t, copyin_atomic ## size, \
449 copyin_atomic ## size ## _from_kernel); \
450 copyout_atomic_test((data), uint ## size ## _t, copyout_atomic ## size, \
451 copyout_atomic ## size ## _to_kernel); \
455 copyin_atomic32_wait_if_equals_from_kernel(struct copyio_test_data
*data
)
457 return copyin_atomic32_wait_if_equals((uintptr_t)data
->kern_addr
, (uint32_t)data
->thread_data
);
461 copyin_atomic32_wait_if_equals_test(struct copyio_test_data
*data
)
463 bzero(data
->kern_addr
, sizeof(uint32_t));
464 int err
= copyin_atomic32_wait_if_equals(data
->user_addr
, 0);
465 T_EXPECT_EQ_INT(err
, 0, "copyin_atomic32_wait_if_equals() should return 0 when equals");
466 err
= copyin_atomic32_wait_if_equals(data
->user_addr
, ~0U);
467 T_EXPECT_EQ_INT(err
, ESTALE
, "copyin_atomic32_wait_if_equals() should return ESTALE when not equals");
469 for (unsigned int offset
= 1; offset
< sizeof(uint32_t); offset
++) {
470 err
= copyin_atomic32_wait_if_equals(data
->user_addr
+ offset
, 0);
471 T_EXPECT_EQ_INT(err
, EINVAL
,
472 "copyin_atomic32_wait_if_equals() on unaligned userspace address should return EINVAL (offset = %u)",
475 err
= copyin_atomic32_wait_if_equals(data
->unmapped_addr
, 0);
476 T_EXPECT_EQ_INT(err
, EFAULT
, "copyin_atomic32_wait_if_equals() on unmapped userspace address should return EFAULT");
478 data
->thread_data
= 0;
480 err
= copyio_test_run_in_thread(copyin_atomic32_wait_if_equals_from_kernel
, data
);
481 T_EXPECT_EQ_INT(err
, EFAULT
, "copyin_atomic32_wait_if_equals() from kernel address in kernel_task thread should return EFAULT");
482 err
= copyin_atomic32_wait_if_equals_from_kernel(data
);
483 T_EXPECT_EQ_INT(err
, EFAULT
, "copyin_atomic32_wait_if_equals() from kernel address in other threads should return EFAULT");
485 copyio_test_protect(data
, VM_PROT_WRITE
);
486 err
= copyin_atomic32_wait_if_equals(data
->user_addr
, 0);
487 T_EXPECT_EQ_INT(err
, EFAULT
, "copyin_atomic32_wait_if_equals() on write-only address should return EFAULT");
488 copyio_test_protect(data
, VM_PROT_READ
| VM_PROT_WRITE
);
494 struct copyio_test_data data
= {};
495 mach_vm_offset_t user_addr
= 0;
496 kern_return_t ret
= KERN_SUCCESS
;
498 data
.buf1
= kalloc(copyio_test_buf_size
);
499 data
.buf2
= kalloc(copyio_test_buf_size
);
500 if (!data
.buf1
|| !data
.buf2
) {
501 T_FAIL("failed to allocate scratch buffers");
507 * This test needs to manipulate the current userspace process's
508 * address space. This is okay to do at the specific point in time
509 * when bsd_do_post() runs: current_proc() points to the init process,
510 * which has been set up to the point of having a valid vm_map, but
511 * not to the point of actually execing yet.
513 proc_t proc
= current_proc();
514 assert(proc
->p_pid
== 1);
515 data
.user_map
= get_task_map_reference(proc
->task
);
517 user_addr
= data
.user_addr
;
518 ret
= mach_vm_allocate_kernel(data
.user_map
, &user_addr
, copyio_test_buf_size
+ PAGE_SIZE
, VM_FLAGS_ANYWHERE
, VM_KERN_MEMORY_NONE
);
520 T_FAIL("mach_vm_allocate_kernel(user_addr) failed: %d", ret
);
523 data
.user_addr
= (user_addr_t
)user_addr
;
525 user_addr
= get_map_max(data
.user_map
) - PAGE_SIZE
;
526 ret
= mach_vm_allocate_kernel(data
.user_map
, &user_addr
, PAGE_SIZE
, VM_FLAGS_FIXED
, VM_KERN_MEMORY_NONE
);
528 T_FAIL("mach_vm_allocate_kernel(user_lastpage_addr) failed: %d", ret
);
529 goto err_user_lastpage_alloc
;
531 data
.user_lastpage_addr
= (user_addr_t
)user_addr
;
533 data
.unmapped_addr
= data
.user_addr
+ copyio_test_buf_size
;
534 mach_vm_deallocate(data
.user_map
, data
.unmapped_addr
, PAGE_SIZE
);
536 vm_prot_t cur_protection
, max_protection
;
537 mach_vm_offset_t kern_addr
= 0;
538 ret
= mach_vm_remap_kernel(kernel_map
, &kern_addr
, copyio_test_buf_size
, VM_PROT_READ
| VM_PROT_WRITE
, VM_FLAGS_ANYWHERE
, VM_KERN_MEMORY_NONE
,
539 data
.user_map
, data
.user_addr
, false, &cur_protection
, &max_protection
, VM_INHERIT_NONE
);
541 T_FAIL("mach_vm_remap_kernel() failed: %d", ret
);
544 data
.kern_addr
= (void *)kern_addr
;
548 copyinstr_test(&data
);
549 copyoutstr_test(&data
);
550 copyio_atomic_test(&data
, 32);
551 copyio_atomic_test(&data
, 64);
552 copyin_atomic32_wait_if_equals_test(&data
);
554 mach_vm_deallocate(kernel_map
, kern_addr
, copyio_test_buf_size
);
556 mach_vm_deallocate(data
.user_map
, data
.user_lastpage_addr
, PAGE_SIZE
);
557 err_user_lastpage_alloc
:
558 mach_vm_deallocate(data
.user_map
, data
.user_addr
, copyio_test_buf_size
);
560 vm_map_deallocate(data
.user_map
);
562 kfree(data
.buf2
, copyio_test_buf_size
);
563 kfree(data
.buf1
, copyio_test_buf_size
);