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