xref: /xnu-11215.1.10/bsd/tests/copyio_tests.c (revision 8d741a5de7ff4191bf97d57b9f54c2f6d4a15585)
1 /*
2  * Copyright (c) 2019 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 <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_xnu.h>
37 #include <vm/vm_map.h>
38 #include <vm/vm_protos.h>
39 #include <sys/errno.h>
40 #include <sys/proc.h>
41 #include <sys/proc_internal.h>
42 #include <sys/vm.h>
43 #include <tests/ktest.h>
44 
45 kern_return_t copyio_test(void);
46 
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'};
49 
50 struct copyio_test_data {
51 	/* VM map of the current userspace process. */
52 	vm_map_t user_map;
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'. */
60 	void *kern_addr;
61 
62 	/* Scratch buffers of size `copyio_test_buf_size'. */
63 	char *buf1, *buf2;
64 	/* Scratch data to pass to helper threads */
65 	union {
66 		void *thread_ptr;
67 		uint64_t thread_data;
68 	};
69 };
70 
71 typedef int (*copyio_thread_fn_t)(struct copyio_test_data *);
72 
73 struct copyio_test_thread_data {
74 	copyio_thread_fn_t fn;
75 	struct copyio_test_data *data;
76 	int ret;
77 	semaphore_t done;
78 };
79 
80 static void
copyio_thread_call_fn(void * arg,wait_result_t __unused res)81 copyio_thread_call_fn(void *arg, wait_result_t __unused res)
82 {
83 	struct copyio_test_thread_data *tdata = arg;
84 	tdata->ret = tdata->fn(tdata->data);
85 	semaphore_signal(tdata->done);
86 }
87 
88 static int
copyio_test_run_in_thread(copyio_thread_fn_t fn,struct copyio_test_data * data)89 copyio_test_run_in_thread(copyio_thread_fn_t fn, struct copyio_test_data *data)
90 {
91 	struct copyio_test_thread_data tdata = {
92 		.fn = fn,
93 		.data = data,
94 	};
95 	thread_t thread;
96 
97 	semaphore_create(current_task(), &tdata.done, SYNC_POLICY_FIFO, 0);
98 	kernel_thread_start(copyio_thread_call_fn, &tdata, &thread);
99 
100 	semaphore_wait(tdata.done);
101 
102 	thread_deallocate(thread);
103 	semaphore_destroy(current_task(), tdata.done);
104 
105 	return tdata.ret;
106 }
107 
108 static void
copyio_test_protect(struct copyio_test_data * data,vm_prot_t prot)109 copyio_test_protect(struct copyio_test_data *data, vm_prot_t prot)
110 {
111 	__assert_only kern_return_t ret = mach_vm_protect(data->user_map, data->user_addr, copyio_test_buf_size, false, prot);
112 	assert(ret == KERN_SUCCESS);
113 }
114 
115 static int
copyin_from_kernel(struct copyio_test_data * data)116 copyin_from_kernel(struct copyio_test_data *data)
117 {
118 	char *in_buf = data->buf2;
119 	return copyin((uintptr_t)data->kern_addr, in_buf, copyio_test_buf_size);
120 }
121 
122 static void
copyin_test(struct copyio_test_data * data)123 copyin_test(struct copyio_test_data *data)
124 {
125 	char *out_buf = data->buf1;
126 	char *in_buf = data->buf2;
127 
128 	for (size_t i = 0; i < copyio_test_buf_size; i++) {
129 		out_buf[i] = (char)i;
130 	}
131 	memcpy(data->kern_addr, out_buf, copyio_test_buf_size);
132 
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");
137 
138 	err = copyin(data->unmapped_addr, NULL, 0);
139 	T_EXPECT_EQ_INT(err, 0, "copyin() with 0 size should always succeed");
140 
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");
147 
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");
155 
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);
160 }
161 
162 static int
copyout_to_kernel(struct copyio_test_data * data)163 copyout_to_kernel(struct copyio_test_data *data)
164 {
165 	char *out_buf = data->buf1;
166 	return copyout(out_buf, (uintptr_t)data->kern_addr, copyio_test_buf_size);
167 }
168 
169 static void
copyout_test(struct copyio_test_data * data)170 copyout_test(struct copyio_test_data *data)
171 {
172 	char *out_buf = data->buf1;
173 
174 	bzero(data->kern_addr, copyio_test_buf_size);
175 
176 	for (size_t i = 0; i < copyio_test_buf_size; i++) {
177 		out_buf[i] = ~(char)i;
178 	}
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");
181 
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");
184 
185 	err = copyout(NULL, data->unmapped_addr, 0);
186 	T_EXPECT_EQ_INT(err, 0, "copyout() with 0 size should always succeed");
187 
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");
194 
195 	bzero(data->kern_addr, copyio_test_buf_size);
196 
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");
203 
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);
208 }
209 
210 static int
copyinstr_from_kernel(struct copyio_test_data * data)211 copyinstr_from_kernel(struct copyio_test_data *data)
212 {
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);
216 }
217 
218 static void
copyinstr_test(struct copyio_test_data * data)219 copyinstr_test(struct copyio_test_data *data)
220 {
221 	char *in_buf = data->buf1;
222 
223 	memcpy(data->kern_addr, copyio_test_string, sizeof(copyio_test_string));
224 
225 	bzero(in_buf, copyio_test_buf_size);
226 	size_t lencopied;
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");
230 
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");
235 
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");
245 
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");
250 
251 	bzero(in_buf, copyio_test_buf_size);
252 	data->thread_ptr = &lencopied;
253 
254 	err = copyio_test_run_in_thread(copyinstr_from_kernel, data);
255 #if defined (__arm64__)
256 	T_EXPECT_EQ_INT(err, EFAULT, "copyinstr() from kernel address in kernel_task thread should return EFAULT");
257 #else
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");
264 #endif
265 	err = copyinstr_from_kernel(data);
266 	T_EXPECT_EQ_INT(err, EFAULT, "copyinstr() from kernel address in other threads should return EFAULT");
267 
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);
272 
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);
277 
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");
281 }
282 
283 static int
copyoutstr_to_kernel(struct copyio_test_data * data)284 copyoutstr_to_kernel(struct copyio_test_data *data)
285 {
286 	size_t *lencopied = data->thread_ptr;
287 	return copyoutstr(copyio_test_string, (user_addr_t)data->kern_addr, sizeof(copyio_test_string), lencopied);
288 }
289 
290 static void
copyoutstr_test(struct copyio_test_data * data)291 copyoutstr_test(struct copyio_test_data *data)
292 {
293 	bzero(data->kern_addr, sizeof(copyio_test_string));
294 
295 	size_t lencopied;
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");
299 
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");
304 
305 	bzero(data->kern_addr, sizeof(copyio_test_string));
306 
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");
315 
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");
322 
323 	bzero(data->kern_addr, sizeof(copyio_test_string));
324 	data->thread_ptr = &lencopied;
325 
326 	err = copyio_test_run_in_thread(copyoutstr_to_kernel, data);
327 #if defined (__arm64__)
328 	T_EXPECT_EQ_INT(err, EFAULT, "copyoutstr() to kernel address in kernel_task thread should return EFAULT");
329 #else
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");
334 #endif
335 	err = copyoutstr_to_kernel(data);
336 	T_EXPECT_EQ_INT(err, EFAULT, "copyoutstr() to kernel address in other threads should return EFAULT");
337 
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);
342 }
343 
344 static int
copyin_atomic32_from_kernel(struct copyio_test_data * data)345 copyin_atomic32_from_kernel(struct copyio_test_data *data)
346 {
347 	return copyin_atomic32((uintptr_t)data->kern_addr, data->thread_ptr);
348 }
349 
350 static int
copyin_atomic64_from_kernel(struct copyio_test_data * data)351 copyin_atomic64_from_kernel(struct copyio_test_data *data)
352 {
353 	return copyin_atomic64((uintptr_t)data->kern_addr, data->thread_ptr);
354 }
355 
356 static int
copyout_atomic32_to_kernel(struct copyio_test_data * data)357 copyout_atomic32_to_kernel(struct copyio_test_data *data)
358 {
359 	return copyout_atomic32((uint32_t)data->thread_data, (user_addr_t)data->kern_addr);
360 }
361 
362 static int
copyout_atomic64_to_kernel(struct copyio_test_data * data)363 copyout_atomic64_to_kernel(struct copyio_test_data *data)
364 {
365 	return copyout_atomic64(data->thread_data, (user_addr_t)data->kern_addr);
366 }
367 
368 /**
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.
373  */
374 #define copyin_atomic_test(data, word_t, copyin_fn, copyin_from_kernel_fn)                                              \
375 	do {                                                                                                            \
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));                                                   \
379                                                                                                                         \
380 	        int err = copyin_fn(data->user_addr, &word_in);                                                         \
381 	        T_EXPECT_EQ_INT(err, 0, #copyin_fn "() with valid parameters should succeed");                          \
382                                                                                                                         \
383 	        int cmp = memcmp(&word_in, &word_out, sizeof(word_t));                                                  \
384 	        T_EXPECT_EQ_INT(cmp, 0, #copyin_fn "() should correctly copy word");                                    \
385                                                                                                                         \
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)",        \
390 	                    offset);                                                                                    \
391 	        };                                                                                                      \
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");     \
394                                                                                                                         \
395 	        data->thread_ptr = &word_in;                                                                            \
396                                                                                                                         \
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");                         \
403                                                                                                                         \
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);                                                \
408 	} while (0)
409 
410 #define copyout_atomic_test(data, word_t, copyout_fn, copyout_to_kernel_fn)                                             \
411 	do {                                                                                                            \
412 	        const word_t word_out = (word_t)0x123456789ABCDEF0UL;                                                   \
413 	        bzero(data->kern_addr, sizeof(word_t));                                                                 \
414                                                                                                                         \
415 	        int err = copyout_fn(word_out, data->user_addr);                                                        \
416 	        T_EXPECT_EQ_INT(err, 0, #copyout_fn "() with valid parameters should succeed");                         \
417                                                                                                                         \
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");                                   \
420                                                                                                                         \
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)",         \
425 	                    offset);                                                                                    \
426 	        };                                                                                                      \
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");                  \
431                                                                                                                         \
432 	        data->thread_data = word_out;                                                                           \
433                                                                                                                         \
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"); \
439                                                                                                                         \
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);                                                \
444 	} while (0)
445 
446 #define copyio_atomic_test(data, size)                                                          \
447 	do {                                                                                    \
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);                                      \
452 	} while (0)
453 
454 static int
copyin_atomic32_wait_if_equals_from_kernel(struct copyio_test_data * data)455 copyin_atomic32_wait_if_equals_from_kernel(struct copyio_test_data *data)
456 {
457 	return copyin_atomic32_wait_if_equals((uintptr_t)data->kern_addr, (uint32_t)data->thread_data);
458 }
459 
460 static void
copyin_atomic32_wait_if_equals_test(struct copyio_test_data * data)461 copyin_atomic32_wait_if_equals_test(struct copyio_test_data *data)
462 {
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");
468 
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)",
473 		    offset);
474 	}
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");
477 
478 	data->thread_data = 0;
479 
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");
484 
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);
489 }
490 
491 kern_return_t
copyio_test(void)492 copyio_test(void)
493 {
494 	struct copyio_test_data data = {};
495 	mach_vm_offset_t user_addr = 0;
496 	kern_return_t ret = KERN_SUCCESS;
497 
498 	data.buf1 = kalloc_data(copyio_test_buf_size, Z_WAITOK);
499 	data.buf2 = kalloc_data(copyio_test_buf_size, Z_WAITOK);
500 	if (!data.buf1 || !data.buf2) {
501 		T_FAIL("failed to allocate scratch buffers");
502 		ret = KERN_NO_SPACE;
503 		goto err_kalloc;
504 	}
505 
506 	/**
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.
512 	 */
513 	proc_t proc = current_proc();
514 	assert(proc_getpid(proc) == 1);
515 	data.user_map = get_task_map_reference(proc_task(proc));
516 
517 	user_addr = data.user_addr;
518 	ret = mach_vm_allocate_kernel(data.user_map, &user_addr,
519 	    copyio_test_buf_size + PAGE_SIZE, VM_MAP_KERNEL_FLAGS_ANYWHERE());
520 	if (ret) {
521 		T_FAIL("mach_vm_allocate_kernel(user_addr) failed: %d", ret);
522 		goto err_user_alloc;
523 	}
524 	data.user_addr = (user_addr_t)user_addr;
525 
526 	user_addr = get_map_max(data.user_map) - PAGE_SIZE;
527 	ret = mach_vm_allocate_kernel(data.user_map, &user_addr, PAGE_SIZE,
528 	    VM_MAP_KERNEL_FLAGS_FIXED());
529 	if (ret) {
530 		T_FAIL("mach_vm_allocate_kernel(user_lastpage_addr) failed: %d", ret);
531 		goto err_user_lastpage_alloc;
532 	}
533 	data.user_lastpage_addr = (user_addr_t)user_addr;
534 
535 	data.unmapped_addr = data.user_addr + copyio_test_buf_size;
536 	mach_vm_deallocate(data.user_map, data.unmapped_addr, PAGE_SIZE);
537 
538 	vm_prot_t cur_protection, max_protection;
539 	mach_vm_offset_t kern_addr = 0;
540 	ret = mach_vm_remap(kernel_map, &kern_addr, copyio_test_buf_size,
541 	    VM_PROT_READ | VM_PROT_WRITE, VM_FLAGS_ANYWHERE,
542 	    data.user_map, data.user_addr, false,
543 	    &cur_protection, &max_protection, VM_INHERIT_NONE);
544 	if (ret) {
545 		T_FAIL("mach_vm_remap() failed: %d", ret);
546 		goto err_kern_remap;
547 	}
548 	data.kern_addr = (void *)kern_addr;
549 
550 	copyin_test(&data);
551 	copyout_test(&data);
552 	copyinstr_test(&data);
553 	copyoutstr_test(&data);
554 	copyio_atomic_test(&data, 32);
555 	copyio_atomic_test(&data, 64);
556 	copyin_atomic32_wait_if_equals_test(&data);
557 
558 	mach_vm_deallocate(kernel_map, kern_addr, copyio_test_buf_size);
559 err_kern_remap:
560 	mach_vm_deallocate(data.user_map, data.user_lastpage_addr, PAGE_SIZE);
561 err_user_lastpage_alloc:
562 	mach_vm_deallocate(data.user_map, data.user_addr, copyio_test_buf_size);
563 err_user_alloc:
564 	vm_map_deallocate(data.user_map);
565 err_kalloc:
566 	kfree_data(data.buf2, copyio_test_buf_size);
567 	kfree_data(data.buf1, copyio_test_buf_size);
568 	return ret;
569 }
570