1 /*
2  * Copyright (c) 2017, 2020 Intel Corporation
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/kernel.h>
8 #include <zephyr/internal/syscall_handler.h>
9 #include <zephyr/ztest.h>
10 #include <zephyr/linker/linker-defs.h>
11 #include "test_syscalls.h"
12 #include <mmu.h>
13 
14 #define BUF_SIZE	32
15 
16 #if defined(CONFIG_BOARD_FVP_BASE_REVC_2XAEMV8A)
17 #define SLEEP_MS_LONG	30000
18 #else
19 #define SLEEP_MS_LONG	15000
20 #endif
21 
22 #if defined(CONFIG_BOARD_NUCLEO_F429ZI) || defined(CONFIG_BOARD_NUCLEO_F207ZG) \
23 	|| defined(CONFIG_BOARD_NUCLEO_L073RZ) \
24 	|| defined(CONFIG_BOARD_RONOTH_LODEV)
25 #define FAULTY_ADDRESS 0x0FFFFFFF
26 #elif defined(CONFIG_BOARD_QEMU_CORTEX_R5)
27 #define FAULTY_ADDRESS 0xBFFFFFFF
28 #elif CONFIG_MMU
29 /* Just past the zephyr image mapping should be a non-present page */
30 #define FAULTY_ADDRESS K_MEM_VM_FREE_START
31 #else
32 #define FAULTY_ADDRESS 0xFFFFFFF0
33 #endif
34 
35 #if !defined(CONFIG_TIMESLICING) || (CONFIG_TIMESLICE_SIZE == 0)
36 #define YIELD_KERNEL		z_impl_k_yield()
37 #define YIELD_USER		k_yield()
38 #else
39 #define YIELD_KERNEL
40 #define YIELD_USER
41 #endif
42 
43 #define NR_THREADS	(arch_num_cpus() * 4)
44 #define MAX_NR_THREADS	(CONFIG_MP_MAX_NUM_CPUS * 4)
45 #define STACK_SZ	(1024 + CONFIG_TEST_EXTRA_STACK_SIZE)
46 
47 struct k_thread stress_threads[MAX_NR_THREADS];
48 K_THREAD_STACK_ARRAY_DEFINE(stress_stacks, MAX_NR_THREADS, STACK_SZ);
49 
50 char kernel_string[BUF_SIZE];
51 char kernel_buf[MAX_NR_THREADS][BUF_SIZE];
52 ZTEST_BMEM char user_string[BUF_SIZE];
53 
k_sys_fatal_error_handler(unsigned int reason,const struct arch_esf * pEsf)54 void k_sys_fatal_error_handler(unsigned int reason, const struct arch_esf *pEsf)
55 {
56 	printk("Caught system error -- reason %d\n", reason);
57 	printk("Unexpected fault during test\n");
58 	TC_END_REPORT(TC_FAIL);
59 	k_fatal_halt(reason);
60 }
61 
z_impl_string_nlen(char * src,size_t maxlen,int * err)62 size_t z_impl_string_nlen(char *src, size_t maxlen, int *err)
63 {
64 	YIELD_KERNEL;
65 
66 	return k_usermode_string_nlen(src, maxlen, err);
67 }
68 
z_vrfy_string_nlen(char * src,size_t maxlen,int * err)69 static inline size_t z_vrfy_string_nlen(char *src, size_t maxlen, int *err)
70 {
71 	int err_copy;
72 	size_t ret;
73 
74 	ret = z_impl_string_nlen((char *)src, maxlen, &err_copy);
75 
76 	YIELD_KERNEL;
77 
78 	if (!err_copy && K_SYSCALL_MEMORY_READ(src, ret + 1)) {
79 		err_copy = -1;
80 	}
81 
82 	YIELD_KERNEL;
83 
84 	K_OOPS(k_usermode_to_copy((int *)err, &err_copy, sizeof(err_copy)));
85 
86 	return ret;
87 }
88 #include <zephyr/syscalls/string_nlen_mrsh.c>
89 
z_impl_string_alloc_copy(char * src)90 int z_impl_string_alloc_copy(char *src)
91 {
92 	YIELD_KERNEL;
93 
94 	if (!strcmp(src, kernel_string)) {
95 		return 0;
96 	} else {
97 		return -2;
98 	}
99 }
100 
z_vrfy_string_alloc_copy(char * src)101 static inline int z_vrfy_string_alloc_copy(char *src)
102 {
103 	char *src_copy;
104 	int ret;
105 
106 	YIELD_KERNEL;
107 
108 	src_copy = k_usermode_string_alloc_copy((char *)src, BUF_SIZE);
109 	if (!src_copy) {
110 		return -1;
111 	}
112 
113 	YIELD_KERNEL;
114 
115 	ret = z_impl_string_alloc_copy(src_copy);
116 
117 	YIELD_KERNEL;
118 
119 	k_free(src_copy);
120 
121 	return ret;
122 }
123 #include <zephyr/syscalls/string_alloc_copy_mrsh.c>
124 
z_impl_string_copy(char * src,int id)125 int z_impl_string_copy(char *src, int id)
126 {
127 	ARG_UNUSED(id);
128 
129 	YIELD_KERNEL;
130 
131 	if (!strcmp(src, kernel_string)) {
132 		return 0;
133 	} else {
134 		return ESRCH;
135 	}
136 }
137 
z_vrfy_string_copy(char * src,int id)138 static inline int z_vrfy_string_copy(char *src, int id)
139 {
140 	YIELD_KERNEL;
141 
142 	int ret = k_usermode_string_copy(kernel_buf[id], (char *)src, BUF_SIZE);
143 
144 	YIELD_KERNEL;
145 
146 	if (ret) {
147 		return ret;
148 	}
149 
150 	return z_impl_string_copy(kernel_buf[id], id);
151 }
152 #include <zephyr/syscalls/string_copy_mrsh.c>
153 
154 /* Not actually used, but will copy wrong string if called by mistake instead
155  * of the handler
156  */
z_impl_to_copy(char * dest)157 int z_impl_to_copy(char *dest)
158 {
159 	YIELD_KERNEL;
160 
161 	memcpy(dest, kernel_string, BUF_SIZE);
162 	return 0;
163 }
164 
z_vrfy_to_copy(char * dest)165 static inline int z_vrfy_to_copy(char *dest)
166 {
167 	YIELD_KERNEL;
168 
169 	return k_usermode_to_copy((char *)dest, user_string, BUF_SIZE);
170 }
171 #include <zephyr/syscalls/to_copy_mrsh.c>
172 
z_impl_syscall_arg64(uint64_t arg)173 int z_impl_syscall_arg64(uint64_t arg)
174 {
175 	YIELD_USER;
176 
177 	/* "Hash" (heh) the return to avoid accidental false positives
178 	 * due to using common/predictable values.
179 	 */
180 	return (int)(arg + 0x8c32a9eda4ca2621ULL + (size_t)&kernel_string);
181 }
182 
z_vrfy_syscall_arg64(uint64_t arg)183 static inline int z_vrfy_syscall_arg64(uint64_t arg)
184 {
185 	return z_impl_syscall_arg64(arg);
186 }
187 #include <zephyr/syscalls/syscall_arg64_mrsh.c>
188 
189 /* Bigger 64 bit arg syscall to exercise marshalling 7+ words of
190  * arguments (this one happens to need 9), and to test generation of
191  * 64 bit return values.
192  */
z_impl_syscall_arg64_big(uint32_t arg1,uint32_t arg2,uint64_t arg3,uint32_t arg4,uint32_t arg5,uint64_t arg6)193 uint64_t z_impl_syscall_arg64_big(uint32_t arg1, uint32_t arg2,
194 			       uint64_t arg3, uint32_t arg4,
195 			       uint32_t arg5, uint64_t arg6)
196 {
197 	uint64_t args[] = { arg1, arg2, arg3, arg4, arg5, arg6 };
198 	uint64_t ret = 0xae751a24ef464cc0ULL;
199 
200 	YIELD_USER;
201 
202 	for (int i = 0; i < ARRAY_SIZE(args); i++) {
203 		ret += args[i];
204 		ret = (ret << 11) | (ret >> 53);
205 	}
206 
207 	return ret;
208 }
209 
z_vrfy_syscall_arg64_big(uint32_t arg1,uint32_t arg2,uint64_t arg3,uint32_t arg4,uint32_t arg5,uint64_t arg6)210 static inline uint64_t z_vrfy_syscall_arg64_big(uint32_t arg1, uint32_t arg2,
211 					     uint64_t arg3, uint32_t arg4,
212 					     uint32_t arg5, uint64_t arg6)
213 {
214 	return z_impl_syscall_arg64_big(arg1, arg2, arg3, arg4, arg5, arg6);
215 }
216 #include <zephyr/syscalls/syscall_arg64_big_mrsh.c>
217 
z_impl_more_args(uint32_t arg1,uint32_t arg2,uint32_t arg3,uint32_t arg4,uint32_t arg5,uint32_t arg6,uint32_t arg7)218 uint32_t z_impl_more_args(uint32_t arg1, uint32_t arg2, uint32_t arg3,
219 			  uint32_t arg4, uint32_t arg5, uint32_t arg6,
220 			  uint32_t arg7)
221 {
222 	uint32_t ret = 0x4ef464cc;
223 	uint32_t args[] = { arg1, arg2, arg3, arg4, arg5, arg6, arg7 };
224 
225 	YIELD_USER;
226 
227 	for (int i = 0; i < ARRAY_SIZE(args); i++) {
228 		ret += args[i];
229 		ret = (ret << 11) | (ret >> 5);
230 	}
231 
232 	return ret;
233 }
234 
z_vrfy_more_args(uint32_t arg1,uint32_t arg2,uint32_t arg3,uint32_t arg4,uint32_t arg5,uint32_t arg6,uint32_t arg7)235 static inline uint32_t z_vrfy_more_args(uint32_t arg1, uint32_t arg2,
236 					uint32_t arg3, uint32_t arg4,
237 					uint32_t arg5, uint32_t arg6,
238 					uint32_t arg7)
239 {
240 	return z_impl_more_args(arg1, arg2, arg3, arg4, arg5, arg6, arg7);
241 }
242 #include <zephyr/syscalls/more_args_mrsh.c>
243 
244 /**
245  * @brief Test to demonstrate usage of k_usermode_string_nlen()
246  *
247  * @details The test will be called from user mode and kernel
248  * mode to check the behavior of k_usermode_string_nlen()
249  *
250  * @ingroup kernel_memprotect_tests
251  *
252  * @see k_usermode_string_nlen()
253  */
ZTEST_USER(syscalls,test_string_nlen)254 ZTEST_USER(syscalls, test_string_nlen)
255 {
256 	int err;
257 	size_t ret;
258 
259 	ret = string_nlen(kernel_string, BUF_SIZE, &err);
260 	if (arch_is_user_context()) {
261 		zassert_equal(err, -1,
262 			      "kernel string did not fault on user access (%d)", err);
263 	} else {
264 		zassert_equal(err, 0, "kernel string faulted in kernel mode (%d)", err);
265 		zassert_equal(ret, strlen(kernel_string),
266 			      "incorrect length returned (%d)", ret);
267 	}
268 
269 	/* Valid usage */
270 	ret = string_nlen(user_string, BUF_SIZE, &err);
271 	zassert_equal(err, 0, "user string faulted (%d)", err);
272 	zassert_equal(ret, strlen(user_string), "incorrect length returned (%d)", ret);
273 
274 	/* Skip this scenario for nsim_sem emulated board, unfortunately
275 	 * the emulator doesn't set up memory as specified in DTS and poking
276 	 * this address doesn't fault
277 	 * Also skip this scenario for em_starterkit_7d, which won't generate
278 	 * exceptions when unmapped address is accessed.
279 	 *
280 	 * In addition to the above, skip the scenario for Non-Secure Cortex-M
281 	 * builds; Zephyr running in Non-Secure mode will generate SecureFault
282 	 * if it attempts to access any address outside the image Flash or RAM
283 	 * boundaries, and the program will hang.
284 	 */
285 #if !((defined(CONFIG_BOARD_NSIM) && defined(CONFIG_SOC_NSIM_SEM)) || \
286 	defined(CONFIG_SOC_EMSK_EM7D) || \
287 	(defined(CONFIG_CPU_CORTEX_M) && \
288 		defined(CONFIG_TRUSTED_EXECUTION_NONSECURE)))
289 	/* Try to blow up the kernel */
290 	ret = string_nlen((char *)FAULTY_ADDRESS, BUF_SIZE, &err);
291 	zassert_equal(err, -1, "nonsense string address did not fault");
292 #endif
293 }
294 
295 /**
296  * @brief Test to verify syscall for string alloc copy
297  *
298  * @ingroup kernel_memprotect_tests
299  *
300  * @see k_usermode_string_alloc_copy(), strcmp()
301  */
ZTEST_USER(syscalls,test_user_string_alloc_copy)302 ZTEST_USER(syscalls, test_user_string_alloc_copy)
303 {
304 	int ret;
305 
306 	ret = string_alloc_copy("asdkajshdazskjdh");
307 	zassert_equal(ret, -2, "string_alloc_copy: 1: got %d", ret);
308 
309 	ret = string_alloc_copy(
310 	    "asdkajshdazskjdhikfsdjhfskdjfhsdkfjhskdfjhdskfjhs");
311 	zassert_equal(ret, -1, "string_alloc_copy: 2: got %d", ret);
312 
313 	ret = string_alloc_copy(kernel_string);
314 	zassert_equal(ret, -1, "string_alloc_copy: 3: got %d", ret);
315 
316 	ret = string_alloc_copy("this is a kernel string");
317 	zassert_equal(ret, 0, "string_alloc_copy: string should have matched (%d)", ret);
318 }
319 
320 /**
321  * @brief Test sys_call for string copy
322  *
323  * @ingroup kernel_memprotect_tests
324  *
325  * @see k_usermode_string_copy(), strcmp()
326  */
ZTEST_USER(syscalls,test_user_string_copy)327 ZTEST_USER(syscalls, test_user_string_copy)
328 {
329 	int ret;
330 
331 	ret = string_copy("asdkajshdazskjdh", 0);
332 	zassert_equal(ret, ESRCH, "string_copy: 1: got %d", ret);
333 
334 	ret = string_copy("asdkajshdazskjdhikfsdjhfskdjfhsdkfjhskdfjhdskfjhs", 0);
335 	zassert_equal(ret, EINVAL, "string_copy: 2: got %d", ret);
336 
337 	ret = string_copy(kernel_string, 0);
338 	zassert_equal(ret, EFAULT, "string_copy: 3: got %d", ret);
339 
340 	ret = string_copy("this is a kernel string", 0);
341 	zassert_equal(ret, 0, "string_copy: string should have matched (%d)", ret);
342 }
343 
344 /**
345  * @brief Test to demonstrate system call for copy
346  *
347  * @ingroup kernel_memprotect_tests
348  *
349  * @see memcpy(), k_usermode_to_copy()
350  */
ZTEST_USER(syscalls,test_to_copy)351 ZTEST_USER(syscalls, test_to_copy)
352 {
353 	char buf[BUF_SIZE];
354 	int ret;
355 
356 	ret = to_copy(kernel_buf[0]);
357 	zassert_equal(ret, EFAULT, "to_copy: should have faulted (%d)", ret);
358 
359 	ret = to_copy(buf);
360 	zassert_equal(ret, 0, "to_copy: copy should have been a success (%d)", ret);
361 	ret = strcmp(buf, user_string);
362 	zassert_equal(ret, 0, "to_copy: string should have matched (%d)", ret);
363 }
364 
run_test_arg64(void)365 void run_test_arg64(void)
366 {
367 	zassert_equal(syscall_arg64(54321),
368 		      z_impl_syscall_arg64(54321),
369 		      "syscall (arg64) didn't match impl");
370 
371 	zassert_equal(syscall_arg64_big(1, 2, 3, 4, 5, 6),
372 		      z_impl_syscall_arg64_big(1, 2, 3, 4, 5, 6),
373 		      "syscall (arg64_big) didn't match impl");
374 }
375 
ZTEST_USER(syscalls,test_arg64)376 ZTEST_USER(syscalls, test_arg64)
377 {
378 	run_test_arg64();
379 }
380 
ZTEST_USER(syscalls,test_more_args)381 ZTEST_USER(syscalls, test_more_args)
382 {
383 	zassert_equal(more_args(1, 2, 3, 4, 5, 6, 7),
384 		      z_impl_more_args(1, 2, 3, 4, 5, 6, 7),
385 		      "syscall (more_args) didn't match impl");
386 }
387 
syscall_switch_stress(void * arg1,void * arg2,void * arg3)388 void syscall_switch_stress(void *arg1, void *arg2, void *arg3)
389 {
390 	int count = 0;
391 	uintptr_t id = (uintptr_t)arg1;
392 	int ret, err;
393 	char buf[BUF_SIZE];
394 
395 	for (;; ) {
396 		/* Run a bunch of our test syscalls in scenarios that are
397 		 * expected to succeed in a tight loop to look
398 		 * for concurrency problems.
399 		 */
400 		ret = string_nlen(user_string, BUF_SIZE, &err);
401 		zassert_equal(err, 0, "stress: user string faulted (%d)", err);
402 		zassert_equal(ret, strlen(user_string),
403 			      "stress: incorrect length returned (%d)", ret);
404 
405 		YIELD_USER;
406 
407 		ret = string_alloc_copy("this is a kernel string");
408 		zassert_equal(ret, 0,
409 			      "stress: string_alloc_copy: string should have matched (%d)", ret);
410 
411 		YIELD_USER;
412 
413 		ret = string_copy("this is a kernel string", (int)id);
414 		zassert_equal(ret, 0, "stress: string_copy: string should have matched (%d)", ret);
415 
416 		YIELD_USER;
417 
418 		ret = to_copy(buf);
419 		zassert_equal(ret, 0,
420 			      "stress: to_copy: copy should have been a success (%d)", ret);
421 
422 		YIELD_USER;
423 
424 		ret = strcmp(buf, user_string);
425 		zassert_equal(ret, 0, "stress: strcmp: string should have matched (%d)", ret);
426 
427 		YIELD_USER;
428 
429 		run_test_arg64();
430 
431 		if (count++ == 30000) {
432 			printk("%ld", id);
433 			count = 0;
434 		}
435 
436 		YIELD_USER;
437 	}
438 }
439 
ZTEST(syscalls_extended,test_syscall_switch_stress)440 ZTEST(syscalls_extended, test_syscall_switch_stress)
441 {
442 	uintptr_t i;
443 
444 	printk("Running syscall switch stress test with %d threads on %d cpu(s)\n",
445 	       NR_THREADS, arch_num_cpus());
446 
447 	for (i = 0; i < NR_THREADS; i++) {
448 		k_thread_create(&stress_threads[i], stress_stacks[i],
449 				STACK_SZ, syscall_switch_stress,
450 				(void *)i, NULL, NULL,
451 				2, K_INHERIT_PERMS | K_USER, K_NO_WAIT);
452 	}
453 
454 	/* Let the stress threads hog the system for several seconds before
455 	 * we abort them.
456 	 * They will all be hammering the cpu(s) with system calls,
457 	 * hopefully smoking out any issues and causing a crash.
458 	 */
459 	k_sleep(K_MSEC(SLEEP_MS_LONG));
460 
461 	for (i = 0; i < NR_THREADS; i++) {
462 		k_thread_abort(&stress_threads[i]);
463 	}
464 
465 	for (i = 0; i < NR_THREADS; i++) {
466 		k_thread_join(&stress_threads[i], K_FOREVER);
467 	}
468 
469 	printk("\n");
470 }
471 
z_impl_syscall_context(void)472 bool z_impl_syscall_context(void)
473 {
474 	return k_is_in_user_syscall();
475 }
476 
z_vrfy_syscall_context(void)477 static inline bool z_vrfy_syscall_context(void)
478 {
479 	return z_impl_syscall_context();
480 }
481 #include <zephyr/syscalls/syscall_context_mrsh.c>
482 
test_syscall_context_user(void * p1,void * p2,void * p3)483 void test_syscall_context_user(void *p1, void *p2, void *p3)
484 {
485 	ARG_UNUSED(p1);
486 	ARG_UNUSED(p2);
487 	ARG_UNUSED(p3);
488 
489 	zassert_true(syscall_context(),
490 		     "not reported in user syscall");
491 }
492 
493 /* Show that z_is_in_syscall() works properly */
ZTEST(syscalls,test_syscall_context)494 ZTEST(syscalls, test_syscall_context)
495 {
496 	/* We're a regular supervisor thread. */
497 	zassert_false(k_is_in_user_syscall(),
498 		      "reported in user syscall when in supv. thread ctx");
499 
500 	/* Make a system call from supervisor mode. The check in the
501 	 * implementation function should return false.
502 	 */
503 	zassert_false(syscall_context(),
504 		      "reported in user syscall when called from supervisor");
505 
506 	/* Remainder of the test in user mode */
507 	k_thread_user_mode_enter(test_syscall_context_user, NULL, NULL, NULL);
508 }
509 
510 K_HEAP_DEFINE(test_heap, BUF_SIZE * (4 * MAX_NR_THREADS));
511 
syscalls_setup(void)512 void *syscalls_setup(void)
513 {
514 	sprintf(kernel_string, "this is a kernel string");
515 	sprintf(user_string, "this is a user string");
516 	k_thread_heap_assign(k_current_get(), &test_heap);
517 
518 	return NULL;
519 }
520 
521 ZTEST_SUITE(syscalls, NULL, syscalls_setup, NULL, NULL, NULL);
522 ZTEST_SUITE(syscalls_extended, NULL, syscalls_setup, NULL, NULL, NULL);
523