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