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