Skip to content

Commit

Permalink
userspace: add z_is_in_user_syscall()
Browse files Browse the repository at this point in the history
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]>
  • Loading branch information
Andrew Boie authored and carlescufi committed Jun 3, 2020
1 parent 64c8189 commit 378024c
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 1 deletion.
32 changes: 32 additions & 0 deletions include/syscall_handler.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
*
Expand Down
1 change: 1 addition & 0 deletions kernel/thread.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
4 changes: 4 additions & 0 deletions scripts/gen_syscalls.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
41 changes: 40 additions & 1 deletion tests/kernel/mem_protect/syscalls/src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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);
}
2 changes: 2 additions & 0 deletions tests/kernel/mem_protect/syscalls/src/test_syscalls.h
Original file line number Diff line number Diff line change
Expand Up @@ -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_ */

0 comments on commit 378024c

Please sign in to comment.