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