Skip to content

Commit 378024c

Browse files
Andrew Boiecarlescufi
authored andcommitted
userspace: add z_is_in_user_syscall()
Certain types of system call validation may need to be pushed deeper in the implementation and not performed in the verification function. If such checks are only pertinent when the caller was from user mode, we need an API to detect this situation. This is implemented by having thread->syscall_frame be non-NULL only while a user system call is in progress. The template for the system call marshalling functions is changed to clear this value on exit. A test is added to prove that this works. Signed-off-by: Andrew Boie <[email protected]>
1 parent 64c8189 commit 378024c

File tree

5 files changed

+79
-1
lines changed

5 files changed

+79
-1
lines changed

include/syscall_handler.h

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,38 @@ enum _obj_init_check {
2525
_OBJ_INIT_ANY = 1
2626
};
2727

28+
/**
29+
* Return true if we are currently handling a system call from user mode
30+
*
31+
* Inside z_vrfy functions, we always know that we are handling
32+
* a system call invoked from user context.
33+
*
34+
* However, some checks that are only relevant to user mode must
35+
* instead be placed deeper within the implementation. This
36+
* API is useful to conditionally make these checks.
37+
*
38+
* For performance reasons, whenever possible, checks should be placed
39+
* in the relevant z_vrfy function since these are completely skipped
40+
* when a syscall is invoked.
41+
*
42+
* This will return true only if we are handling a syscall for a
43+
* user thread. If the system call was invoked from supervisor mode,
44+
* or we are not handling a system call, this will return false.
45+
*
46+
* @return whether the current context is handling a syscall for a user
47+
* mode thread
48+
*/
49+
static inline bool z_is_in_user_syscall(void)
50+
{
51+
/* This gets set on entry to the syscall's generasted z_mrsh
52+
* function and then cleared on exit. This code path is only
53+
* encountered when a syscall is made from user mode, system
54+
* calls from supervisor mode bypass everything directly to
55+
* the implementation function.
56+
*/
57+
return !k_is_in_isr() && _current->syscall_frame != NULL;
58+
}
59+
2860
/**
2961
* Ensure a system object is a valid object of the expected type
3062
*

kernel/thread.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -466,6 +466,7 @@ void z_setup_new_thread(struct k_thread *new_thread,
466466
z_object_init(stack);
467467
new_thread->stack_obj = stack;
468468
new_thread->mem_domain_info.mem_domain = NULL;
469+
new_thread->syscall_frame = NULL;
469470

470471
/* Any given thread has access to itself */
471472
k_object_access_grant(new_thread, new_thread);

scripts/gen_syscalls.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,15 +284,19 @@ def marshall_defs(func_name, func_type, args):
284284

285285
if func_type == "void":
286286
mrsh += "\t" + "%s;\n" % vrfy_call
287+
mrsh += "\t" + "_current->syscall_frame = NULL;\n"
287288
mrsh += "\t" + "return 0;\n"
288289
else:
289290
mrsh += "\t" + "%s ret = %s;\n" % (func_type, vrfy_call)
291+
290292
if need_split(func_type):
291293
ptr = "((u64_t *)%s)" % mrsh_rval(nmrsh - 1, nmrsh)
292294
mrsh += "\t" + "Z_OOPS(Z_SYSCALL_MEMORY_WRITE(%s, 8));\n" % ptr
293295
mrsh += "\t" + "*%s = ret;\n" % ptr
296+
mrsh += "\t" + "_current->syscall_frame = NULL;\n"
294297
mrsh += "\t" + "return 0;\n"
295298
else:
299+
mrsh += "\t" + "_current->syscall_frame = NULL;\n"
296300
mrsh += "\t" + "return (uintptr_t) ret;\n"
297301

298302
mrsh += "}\n"

tests/kernel/mem_protect/syscalls/src/main.c

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,44 @@ void test_syscall_torture(void)
337337
printk("\n");
338338
}
339339

340+
bool z_impl_syscall_context(void)
341+
{
342+
return z_is_in_user_syscall();
343+
}
344+
345+
static inline bool z_vrfy_syscall_context(void)
346+
{
347+
return z_impl_syscall_context();
348+
}
349+
#include <syscalls/syscall_context_mrsh.c>
350+
351+
void test_syscall_context_user(void *p1, void *p2, void *p3)
352+
{
353+
ARG_UNUSED(p1);
354+
ARG_UNUSED(p2);
355+
ARG_UNUSED(p3);
356+
357+
zassert_true(syscall_context(),
358+
"not reported in user syscall");
359+
}
360+
361+
/* Show that z_is_in_syscall() works properly */
362+
void test_syscall_context(void)
363+
{
364+
/* We're a regular supervisor thread. */
365+
zassert_false(z_is_in_user_syscall(),
366+
"reported in user syscall when in supv. thread ctx");
367+
368+
/* Make a system call from supervisor mode. The check in the
369+
* implementation function should return false.
370+
*/
371+
zassert_false(syscall_context(),
372+
"reported in user syscall when called from supervisor");
373+
374+
/* Remainder of the test in user mode */
375+
k_thread_user_mode_enter(test_syscall_context_user, NULL, NULL, NULL);
376+
}
377+
340378
K_MEM_POOL_DEFINE(test_pool, BUF_SIZE, BUF_SIZE, 4 * NR_THREADS, 4);
341379

342380
void test_main(void)
@@ -352,7 +390,8 @@ void test_main(void)
352390
ztest_user_unit_test(test_user_string_copy),
353391
ztest_user_unit_test(test_user_string_alloc_copy),
354392
ztest_user_unit_test(test_arg64),
355-
ztest_unit_test(test_syscall_torture)
393+
ztest_unit_test(test_syscall_torture),
394+
ztest_unit_test(test_syscall_context)
356395
);
357396
ztest_run_test_suite(syscalls);
358397
}

tests/kernel/mem_protect/syscalls/src/test_syscalls.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ __syscall int syscall_arg64(u64_t arg);
2121
__syscall u64_t syscall_arg64_big(u32_t arg1, u32_t arg2, u64_t arg3,
2222
u32_t arg4, u32_t arg5, u64_t arg6);
2323

24+
__syscall bool syscall_context(void);
25+
2426
#include <syscalls/test_syscalls.h>
2527

2628
#endif /* _TEST_SYSCALLS_H_ */

0 commit comments

Comments
 (0)