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