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 <andrew.p.boie@intel.com>
This commit is contained in:
Andrew Boie 2020-05-28 11:48:54 -07:00 committed by Carles Cufí
commit 378024c510
5 changed files with 79 additions and 1 deletions

View file

@ -25,6 +25,38 @@ enum _obj_init_check {
_OBJ_INIT_ANY = 1
};
/**
* Return true if we are currently handling a system call from user mode
*
* Inside z_vrfy functions, we always know that we are handling
* a system call invoked from user context.
*
* However, some checks that are only relevant to user mode must
* instead be placed deeper within the implementation. This
* API is useful to conditionally make these checks.
*
* For performance reasons, whenever possible, checks should be placed
* in the relevant z_vrfy function since these are completely skipped
* when a syscall is invoked.
*
* This will return true only if we are handling a syscall for a
* user thread. If the system call was invoked from supervisor mode,
* or we are not handling a system call, this will return false.
*
* @return whether the current context is handling a syscall for a user
* mode thread
*/
static inline bool z_is_in_user_syscall(void)
{
/* This gets set on entry to the syscall's generasted z_mrsh
* function and then cleared on exit. This code path is only
* encountered when a syscall is made from user mode, system
* calls from supervisor mode bypass everything directly to
* the implementation function.
*/
return !k_is_in_isr() && _current->syscall_frame != NULL;
}
/**
* Ensure a system object is a valid object of the expected type
*

View file

@ -466,6 +466,7 @@ void z_setup_new_thread(struct k_thread *new_thread,
z_object_init(stack);
new_thread->stack_obj = stack;
new_thread->mem_domain_info.mem_domain = NULL;
new_thread->syscall_frame = NULL;
/* Any given thread has access to itself */
k_object_access_grant(new_thread, new_thread);

View file

@ -284,15 +284,19 @@ def marshall_defs(func_name, func_type, args):
if func_type == "void":
mrsh += "\t" + "%s;\n" % vrfy_call
mrsh += "\t" + "_current->syscall_frame = NULL;\n"
mrsh += "\t" + "return 0;\n"
else:
mrsh += "\t" + "%s ret = %s;\n" % (func_type, vrfy_call)
if need_split(func_type):
ptr = "((u64_t *)%s)" % mrsh_rval(nmrsh - 1, nmrsh)
mrsh += "\t" + "Z_OOPS(Z_SYSCALL_MEMORY_WRITE(%s, 8));\n" % ptr
mrsh += "\t" + "*%s = ret;\n" % ptr
mrsh += "\t" + "_current->syscall_frame = NULL;\n"
mrsh += "\t" + "return 0;\n"
else:
mrsh += "\t" + "_current->syscall_frame = NULL;\n"
mrsh += "\t" + "return (uintptr_t) ret;\n"
mrsh += "}\n"

View file

@ -337,6 +337,44 @@ void test_syscall_torture(void)
printk("\n");
}
bool z_impl_syscall_context(void)
{
return z_is_in_user_syscall();
}
static inline bool z_vrfy_syscall_context(void)
{
return z_impl_syscall_context();
}
#include <syscalls/syscall_context_mrsh.c>
void test_syscall_context_user(void *p1, void *p2, void *p3)
{
ARG_UNUSED(p1);
ARG_UNUSED(p2);
ARG_UNUSED(p3);
zassert_true(syscall_context(),
"not reported in user syscall");
}
/* Show that z_is_in_syscall() works properly */
void test_syscall_context(void)
{
/* We're a regular supervisor thread. */
zassert_false(z_is_in_user_syscall(),
"reported in user syscall when in supv. thread ctx");
/* Make a system call from supervisor mode. The check in the
* implementation function should return false.
*/
zassert_false(syscall_context(),
"reported in user syscall when called from supervisor");
/* Remainder of the test in user mode */
k_thread_user_mode_enter(test_syscall_context_user, NULL, NULL, NULL);
}
K_MEM_POOL_DEFINE(test_pool, BUF_SIZE, BUF_SIZE, 4 * NR_THREADS, 4);
void test_main(void)
@ -352,7 +390,8 @@ void test_main(void)
ztest_user_unit_test(test_user_string_copy),
ztest_user_unit_test(test_user_string_alloc_copy),
ztest_user_unit_test(test_arg64),
ztest_unit_test(test_syscall_torture)
ztest_unit_test(test_syscall_torture),
ztest_unit_test(test_syscall_context)
);
ztest_run_test_suite(syscalls);
}

View file

@ -21,6 +21,8 @@ __syscall int syscall_arg64(u64_t arg);
__syscall u64_t syscall_arg64_big(u32_t arg1, u32_t arg2, u64_t arg3,
u32_t arg4, u32_t arg5, u64_t arg6);
__syscall bool syscall_context(void);
#include <syscalls/test_syscalls.h>
#endif /* _TEST_SYSCALLS_H_ */