blob: 0e657b6b304fad502861b8506352b0ddc4d96a6a [file] [log] [blame]
/*
* Copyright (c) 2017 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "mem_protect.h"
#include <zephyr/syscall_handler.h>
/* Kernel objects */
K_THREAD_STACK_DEFINE(child_stack, KOBJECT_STACK_SIZE);
K_THREAD_STACK_DEFINE(extra_stack, KOBJECT_STACK_SIZE);
K_SEM_DEFINE(kobject_sem, SEMAPHORE_INIT_COUNT, SEMAPHORE_MAX_COUNT);
K_SEM_DEFINE(kobject_public_sem, SEMAPHORE_INIT_COUNT, SEMAPHORE_MAX_COUNT);
K_MUTEX_DEFINE(kobject_mutex);
struct k_thread child_thread;
struct k_thread extra_thread;
struct k_sem *random_sem_type;
struct k_sem kobject_sem_not_hash_table;
struct k_sem kobject_sem_no_init_no_access;
struct k_sem kobject_sem_no_init_access;
/****************************************************************************/
static void kobject_access_grant_user_part(void *p1, void *p2, void *p3)
{
set_fault_valid(true);
k_sem_take(random_sem_type, K_FOREVER);
}
/**
* @brief Test access to a invalid semaphore who's address is NULL
*
* @ingroup kernel_memprotect_tests
*
* @see k_thread_access_grant(), k_thread_user_mode_enter()
*/
void test_kobject_access_grant(void)
{
set_fault_valid(false);
z_object_init(random_sem_type);
k_thread_access_grant(k_current_get(),
&kobject_sem,
&kobject_mutex,
random_sem_type);
k_thread_user_mode_enter(kobject_access_grant_user_part,
NULL, NULL, NULL);
}
/**
* @brief Test grant access of given NULL kobject
*
* @details Call function with a NULL parameter in supervisor mode,
* nothing happened.
*
* @ingroup kernel_memprotect_tests
*
* @see k_thread_access_grant()
*/
void test_kobject_access_grant_error(void)
{
k_object_access_grant(NULL, k_current_get());
}
/**
* @brief Test grant access of given NULL thread in usermode
*
* @details Call function with NULL parameter, an expected fault
* happened.
*
* @ingroup kernel_memprotect_tests
*
* @see k_thread_access_grant()
*/
void test_kobject_access_grant_error_user(void)
{
struct k_queue *q;
/*
* avoid using K_OBJ_PIPE, K_OBJ_MSGQ, or K_OBJ_STACK because the
* k_object_alloc() returns an uninitialized kernel object and these
* objects are types that can have additional memory allocations that
* need to be freed. This becomes a problem on the fault handler clean
* up because when it is freeing this uninitialized object the random
* data in the object can cause the clean up to try to free random
* data resulting in a secondary fault that fails the test.
*/
q = k_object_alloc(K_OBJ_QUEUE);
k_object_access_grant(q, k_current_get());
set_fault_valid(true);
/* a K_ERR_KERNEL_OOPS expected */
k_object_access_grant(q, NULL);
}
/**
* @brief Test grant access of given NULL kobject in usermode
*
* @details Call function with a NULL parameter, an expected fault
* happened.
*
* @see k_thread_access_grant()
*
* @ingroup kernel_memprotect_tests
*/
void test_kobject_access_grant_error_user_null(void)
{
set_fault_valid(true);
/* a K_ERR_KERNEL_OOPS expected */
k_object_access_grant(NULL, k_current_get());
}
/**
* @brief Test grant access to all the kobject for thread
*
* @details Call function with a NULL parameter, an expected fault
* happened.
*
* @see k_thread_access_all_grant()
*
* @ingroup kernel_memprotect_tests
*/
void test_kobject_access_all_grant_error(void)
{
set_fault_valid(true);
/* a K_ERR_KERNEL_OOPS expected */
k_object_access_all_grant(NULL);
}
/****************************************************************************/
static void syscall_invalid_kobject_user_part(void *p1, void *p2, void *p3)
{
k_sem_give(&kobject_sem);
/* should cause a fault */
set_fault_valid(true);
/* should cause fault. typecasting to override compiler warning */
k_sem_take((struct k_sem *)&kobject_mutex, K_FOREVER);
}
/**
* @brief Test syscall can take a different type of kobject
*
* @details Test syscall can take a different type of kobject and syscall will
* generate fatal error if check fails.
*
* @ingroup kernel_memprotect_tests
*
* @see k_thread_access_grant()
*/
void test_syscall_invalid_kobject(void)
{
set_fault_valid(false);
k_thread_access_grant(k_current_get(),
&kobject_sem,
&kobject_mutex);
k_thread_user_mode_enter(syscall_invalid_kobject_user_part,
NULL, NULL, NULL);
}
/****************************************************************************/
static void thread_without_kobject_permission_user_part(void *p1, void *p2,
void *p3)
{
/* should cause a fault */
set_fault_valid(true);
k_sem_give(&kobject_sem);
}
/**
* @brief Test user thread can access a k_object without grant
*
* @details The kernel will fail system call on kernel object that tracks thread
* permissions, on thread that don't have permission granted on the object.
*
* @ingroup kernel_memprotect_tests
*
* @see k_thread_access_grant(), k_thread_user_mode_enter()
*/
void test_thread_without_kobject_permission(void)
{
set_fault_valid(false);
k_thread_access_grant(k_current_get(),
&kobject_mutex);
k_thread_user_mode_enter(thread_without_kobject_permission_user_part,
NULL, NULL, NULL);
}
/****************************************************************************/
static void kobject_revoke_access_user_part(void *p1, void *p2, void *p3)
{
/* should cause a fault */
if ((uintptr_t)p1 == 1U) {
set_fault_valid(false);
} else {
set_fault_valid(true);
}
k_sem_give(&kobject_sem);
}
/**
* @brief Test access revoke
*
* @ingroup kernel_memprotect_tests
*
* @see k_thread_access_grant(), k_object_access_revoke()
*/
void test_kobject_revoke_access(void)
{
set_fault_valid(false);
k_thread_access_grant(k_current_get(),
&kobject_sem);
k_thread_create(&child_thread,
child_stack,
KOBJECT_STACK_SIZE,
kobject_revoke_access_user_part,
(void *)1, NULL, NULL,
0, K_INHERIT_PERMS | K_USER, K_NO_WAIT);
k_thread_join(&child_thread, K_FOREVER);
k_object_access_revoke(&kobject_sem, k_current_get());
k_thread_create(&child_thread,
child_stack,
KOBJECT_STACK_SIZE,
kobject_revoke_access_user_part,
(void *)2, NULL, NULL,
0, K_INHERIT_PERMS | K_USER, K_NO_WAIT);
k_thread_join(&child_thread, K_FOREVER);
}
/****************************************************************************/
/* grant access to all user threads that follow */
static void kobject_grant_access_child_entry(void *p1, void *p2, void *p3)
{
k_sem_give(&kobject_sem);
k_object_access_grant(&kobject_sem, &extra_thread);
}
static void kobject_grant_access_extra_entry(void *p1, void *p2, void *p3)
{
k_sem_take(&kobject_sem, K_FOREVER);
}
/**
* @brief Test access revoke
*
* @ingroup kernel_memprotect_tests
*
* @see k_thread_access_grant(), k_object_access_revoke()
*/
void test_kobject_grant_access_kobj(void)
{
set_fault_valid(false);
k_thread_access_grant(k_current_get(),
&kobject_sem, &extra_thread);
k_thread_create(&child_thread,
child_stack,
KOBJECT_STACK_SIZE,
kobject_grant_access_child_entry,
NULL, NULL, NULL,
0, K_INHERIT_PERMS | K_USER, K_NO_WAIT);
k_thread_join(&child_thread, K_FOREVER);
k_thread_create(&extra_thread,
extra_stack,
KOBJECT_STACK_SIZE,
kobject_grant_access_extra_entry,
NULL, NULL, NULL,
0, K_INHERIT_PERMS | K_USER, K_NO_WAIT);
k_thread_join(&extra_thread, K_FOREVER);
}
/****************************************************************************/
static void grant_access_kobj_invalid_child(void *p1, void *p2, void *p3)
{
k_sem_give(&kobject_sem);
set_fault_valid(true);
k_object_access_grant(&kobject_sem, &extra_thread);
zassert_unreachable("k_object validation failure");
}
/**
* @brief Test access grant between threads
*
* @details Test access grant to thread B from thread A which doesn't have
* required permissions.
*
* @ingroup kernel_memprotect_tests
*
* @see k_thread_access_grant()
*/
void test_kobject_grant_access_kobj_invalid(void)
{
set_fault_valid(false);
k_thread_access_grant(&child_thread, &kobject_sem);
k_thread_create(&child_thread,
child_stack,
KOBJECT_STACK_SIZE,
grant_access_kobj_invalid_child,
NULL, NULL, NULL,
0, K_USER, K_NO_WAIT);
k_thread_join(&child_thread, K_FOREVER);
}
/****************************************************************************/
static void release_from_user_child(void *p1, void *p2, void *p3)
{
k_sem_give(&kobject_sem);
k_object_release(&kobject_sem);
set_fault_valid(true);
k_sem_give(&kobject_sem);
}
/**
* @brief Test revoke permission of a k_object from userspace
*
* @ingroup kernel_memprotect_tests
*
* @see k_thread_access_grant(), k_object_release()
*/
void test_kobject_release_from_user(void)
{
set_fault_valid(false);
k_thread_access_grant(k_current_get(),
&kobject_sem);
k_thread_create(&child_thread,
child_stack,
KOBJECT_STACK_SIZE,
release_from_user_child,
NULL, NULL, NULL,
0, K_INHERIT_PERMS | K_USER, K_NO_WAIT);
k_thread_join(&child_thread, K_FOREVER);
}
/**
* @brief Test release and access grant an invalid kobject
*
* @details Validate release and access grant an invalid kernel object.
*
* @see k_object_release(), k_object_access_all_grant()
*
* @ingroup kernel_memprotect_tests
*/
void test_kobject_invalid(void)
{
int dummy = 0;
k_object_access_all_grant(&dummy);
k_object_release(&dummy);
}
/****************************************************************************/
static void access_all_grant_child_give(void *p1, void *p2, void *p3)
{
k_sem_give(&kobject_public_sem);
}
static void access_all_grant_child_take(void *p1, void *p2, void *p3)
{
k_sem_take(&kobject_public_sem, K_FOREVER);
}
/**
* @brief Test supervisor thread grants kernel objects all access public status
*
* @details System makes kernel object kobject_public_sem public to all threads
* Test the access to that kernel object by creating two new user threads.
*
* @see k_object_access_all_grant()
*
* @ingroup kernel_memprotect_tests
*/
void test_kobject_access_all_grant(void)
{
set_fault_valid(false);
k_object_access_all_grant(&kobject_public_sem);
k_thread_create(&child_thread,
child_stack,
KOBJECT_STACK_SIZE,
access_all_grant_child_give,
NULL, NULL, NULL,
0, K_USER, K_NO_WAIT);
k_thread_join(&child_thread, K_FOREVER);
k_thread_create(&child_thread,
child_stack,
KOBJECT_STACK_SIZE,
access_all_grant_child_take,
NULL, NULL, NULL,
0, K_USER, K_NO_WAIT);
k_thread_join(&child_thread, K_FOREVER);
}
/****************************************************************************/
static void residual_permissions_child_success(void *p1, void *p2, void *p3)
{
k_sem_give(&kobject_sem);
}
static void residual_permissions_child_fail(void *p1, void *p2, void *p3)
{
set_fault_valid(true);
k_sem_take(&kobject_sem, K_FOREVER);
zassert_unreachable("Failed to clear permission on a deleted thread");
}
/**
* @brief Test access permission of a terminated thread
*
* @details If a deleted thread with some permissions is
* recreated with the same tid, check if it still has the
* permissions.
*
* @ingroup kernel_memprotect_tests
*
* @see k_thread_access_grant()
*/
void test_thread_has_residual_permissions(void)
{
set_fault_valid(false);
k_thread_access_grant(k_current_get(),
&kobject_sem);
k_thread_create(&child_thread,
child_stack,
KOBJECT_STACK_SIZE,
residual_permissions_child_success,
NULL, NULL, NULL,
0, K_INHERIT_PERMS | K_USER, K_NO_WAIT);
k_thread_join(&child_thread, K_FOREVER);
k_thread_create(&child_thread,
child_stack,
KOBJECT_STACK_SIZE,
residual_permissions_child_fail,
NULL, NULL, NULL,
0, K_USER, K_NO_WAIT);
k_thread_join(&child_thread, K_FOREVER);
}
/****************************************************************************/
/**
* @brief Test grant access to a valid kobject but invalid thread id
*
* @ingroup kernel_memprotect_tests
*
* @see k_object_access_grant(), k_object_access_revoke(),
* z_object_find()
*/
void test_kobject_access_grant_to_invalid_thread(void)
{
static struct k_thread uninit_thread;
set_fault_valid(false);
k_object_access_grant(&kobject_sem, &uninit_thread);
k_object_access_revoke(&kobject_sem, &uninit_thread);
zassert_not_equal(Z_SYSCALL_OBJ(&uninit_thread, K_OBJ_THREAD), 0,
"Access granted/revoked to invalid thread k_object");
}
/****************************************************************************/
/**
* @brief Object validation checks
*
* @details Test syscall on a kobject which is not present in the hash table.
*
* @ingroup kernel_memprotect_tests
*/
void test_kobject_access_invalid_kobject(void)
{
set_fault_valid(true);
k_sem_take(&kobject_sem_not_hash_table, K_SECONDS(1));
zassert_unreachable("k_object validation failure.");
}
/****************************************************************************/
/**
* @brief Object validation checks without init access
*
* @details Test syscall on a kobject which is not initialized
* and has no access
*
* @ingroup kernel_memprotect_tests
*/
void test_access_kobject_without_init_access(void)
{
set_fault_valid(true);
k_sem_take(&kobject_sem_no_init_no_access, K_SECONDS(1));
zassert_unreachable("k_object validation failure");
}
/****************************************************************************/
/* object validation checks */
static void without_init_with_access_child(void *p1, void *p2, void *p3)
{
set_fault_valid(true);
k_sem_take(&kobject_sem_no_init_access, K_SECONDS(1));
zassert_unreachable("_SYSCALL_OBJ implementation failure.");
}
/**
* @brief Test syscall on a kobject which is not initialized and has access
*
* @ingroup kernel_memprotect_tests
*
* @see k_thread_access_grant()
*/
void test_access_kobject_without_init_with_access(void)
{
set_fault_valid(false);
k_thread_access_grant(k_current_get(),
&kobject_sem_no_init_access);
k_thread_create(&child_thread,
child_stack,
KOBJECT_STACK_SIZE,
without_init_with_access_child,
NULL, NULL, NULL,
0, K_INHERIT_PERMS | K_USER, K_NO_WAIT);
k_thread_join(&child_thread, K_FOREVER);
}
/****************************************************************************/
/* object validation checks */
static void reinitialize_thread_kobj_extra(void *p1, void *p2, void *p3)
{
zassert_unreachable("_SYSCALL_OBJ implementation failure.");
}
static void reinitialize_thread_kobj_child(void *p1, void *p2, void *p3)
{
set_fault_valid(true);
k_thread_create(&extra_thread,
extra_stack,
KOBJECT_STACK_SIZE,
reinitialize_thread_kobj_extra,
NULL, NULL, NULL,
0, K_USER, K_NO_WAIT);
zassert_unreachable("_SYSCALL_OBJ implementation failure.");
}
/**
* @brief Test to reinitialize the k_thread object
*
* @ingroup kernel_memprotect_tests
*/
void test_kobject_reinitialize_thread_kobj(void)
{
set_fault_valid(false);
k_thread_create(&child_thread,
child_stack,
KOBJECT_STACK_SIZE,
reinitialize_thread_kobj_child,
NULL, NULL, NULL,
0, K_INHERIT_PERMS | K_USER, K_NO_WAIT);
k_thread_join(&child_thread, K_FOREVER);
}
/****************************************************************************/
/* object validation checks */
static void new_thread_from_user_extra(void *p1, void *p2, void *p3)
{
k_thread_abort(&extra_thread);
}
static void new_thread_from_user_child(void *p1, void *p2, void *p3)
{
set_fault_valid(false);
k_thread_create(&extra_thread,
extra_stack,
KOBJECT_STACK_SIZE,
new_thread_from_user_extra,
NULL, NULL, NULL,
0, K_USER, K_NO_WAIT);
k_thread_join(&child_thread, K_FOREVER);
}
/**
* @brief Test thread create from a user thread and check permissions
*
* @details
* - Test user thread can create new thread.
* - Verify that given thread and thread stack permissions to the user thread,
* allow to create new user thread.
* - Verify that new created user thread have access to its own thread object
* by aborting itself.
*
* @ingroup kernel_memprotect_tests
*/
void test_create_new_thread_from_user(void)
{
set_fault_valid(false);
k_thread_access_grant(&child_thread,
&extra_thread,
&extra_stack);
k_thread_create(&child_thread,
child_stack,
KOBJECT_STACK_SIZE,
new_thread_from_user_child,
NULL, NULL, NULL,
0, K_INHERIT_PERMS | K_USER, K_NO_WAIT);
k_thread_join(&child_thread, K_FOREVER);
}
/* Additional functions for test below
* User thread create with in-use stack objects
*/
static void new_thrd_from_user_with_in_use_stack(void *p1, void *p2, void *p3)
{
zassert_unreachable("New user thread init with in-use stack obj");
}
static void new_user_thrd_child_with_in_use_stack(void *p1, void *p2, void *p3)
{
set_fault_valid(true);
k_thread_create(&extra_thread,
child_stack,
KOBJECT_STACK_SIZE,
new_thrd_from_user_with_in_use_stack,
NULL, NULL, NULL,
0, K_USER, K_NO_WAIT);
k_thread_join(&child_thread, K_FOREVER);
}
/**
* @brief Test create new user thread from a user thread with in-use stack obj
*
* @details The kernel must prevent new user threads to use initialized (in-use)
* stack objects. In that case extra_thread is going to be create with in-use
* stack object child_stack. That will generate error, showing that kernel
* memory protection is working correctly.
*
* @ingroup kernel_memprotect_tests
*/
void test_new_user_thread_with_in_use_stack_obj(void)
{
set_fault_valid(false);
k_thread_access_grant(&child_thread,
&extra_thread,
&extra_stack,
&child_stack);
k_thread_create(&child_thread,
child_stack,
KOBJECT_STACK_SIZE,
new_user_thrd_child_with_in_use_stack,
NULL, NULL, NULL,
0, K_INHERIT_PERMS | K_USER, K_NO_WAIT);
k_thread_join(&child_thread, K_FOREVER);
}
static void from_user_no_access_stack_extra_entry(void *p1, void *p2, void *p3)
{
zassert_unreachable("k_object validation failure in k thread create");
}
static void from_user_no_access_stack_child_entry(void *p1, void *p2, void *p3)
{
set_fault_valid(true);
k_thread_create(&extra_thread,
extra_stack,
KOBJECT_STACK_SIZE,
from_user_no_access_stack_extra_entry,
NULL, NULL, NULL,
0, K_USER, K_NO_WAIT);
}
/**
* @brief Test creates new thread from usermode without stack access
*
* @details Create a new thread from user and the user doesn't have access
* to the stack region of new thread.
* _handler_k_thread_create validation.
*
* @ingroup kernel_memprotect_tests
*/
void test_create_new_thread_from_user_no_access_stack(void)
{
set_fault_valid(false);
k_thread_access_grant(&child_thread,
&extra_thread);
k_thread_create(&child_thread,
child_stack,
KOBJECT_STACK_SIZE,
from_user_no_access_stack_child_entry,
NULL, NULL, NULL,
0, K_USER, K_NO_WAIT);
k_thread_join(&child_thread, K_FOREVER);
}
/****************************************************************************/
/* object validation checks */
#ifndef CONFIG_MPU_REQUIRES_POWER_OF_TWO_ALIGNMENT
static void from_user_invalid_stacksize_extra(void *p1, void *p2, void *p3)
{
zassert_unreachable("k_object validation failure in k thread create");
}
static void from_user_invalid_stacksize_child(void *p1, void *p2, void *p3)
{
set_fault_valid(true);
k_thread_create(&extra_thread,
extra_stack,
-1,
from_user_invalid_stacksize_extra,
NULL, NULL, NULL,
0, K_USER, K_NO_WAIT);
zassert_unreachable("k_object validation failure in k thread create");
}
/**
* @brief Test to validate user thread spawning with stack overflow
*
* @details Create a new thread from user and use a huge stack
* size which overflows. This is _handler_k_thread_create validation.
*
* @ingroup kernel_memprotect_tests
*/
void test_create_new_thread_from_user_invalid_stacksize(void)
{
set_fault_valid(false);
k_thread_access_grant(&child_thread,
&extra_thread,
&child_stack);
k_thread_create(&child_thread,
child_stack,
KOBJECT_STACK_SIZE,
from_user_invalid_stacksize_child,
NULL, NULL, NULL,
0, K_USER, K_NO_WAIT);
k_thread_join(&child_thread, K_FOREVER);
}
#else
void test_create_new_thread_from_user_invalid_stacksize(void)
{
ztest_test_skip();
}
#endif
/****************************************************************************/
/* object validation checks */
#ifndef CONFIG_MPU_REQUIRES_POWER_OF_TWO_ALIGNMENT
static void user_huge_stacksize_extra(void *p1, void *p2, void *p3)
{
zassert_unreachable("k_object validation failure in k thread create");
}
static void user_huge_stacksize_child(void *p1, void *p2, void *p3)
{
set_fault_valid(true);
k_thread_create(&extra_thread,
extra_stack,
K_THREAD_STACK_SIZEOF(extra_stack) + 1,
user_huge_stacksize_extra,
NULL, NULL, NULL,
0, K_USER, K_NO_WAIT);
zassert_unreachable("k_object validation failure in k thread create");
}
/**
* @brief Test to check stack overflow from user thread
*
* @details Create a new thread from user and use a stack
* bigger than allowed size. This is_handler_k_thread_create
* validation.
*
* @ingroup kernel_memprotect_tests
*/
void test_create_new_thread_from_user_huge_stacksize(void)
{
set_fault_valid(false);
k_thread_access_grant(&child_thread,
&extra_thread,
&extra_stack);
k_thread_create(&child_thread,
child_stack,
KOBJECT_STACK_SIZE,
user_huge_stacksize_child,
NULL, NULL, NULL,
0, K_USER, K_NO_WAIT);
k_thread_join(&child_thread, K_FOREVER);
}
#else
void test_create_new_thread_from_user_huge_stacksize(void)
{
ztest_test_skip();
}
#endif
/****************************************************************************/
/* object validation checks */
static void supervisor_from_user_extra(void *p1, void *p2, void *p3)
{
zassert_unreachable("k_object validation failure in k thread create");
}
static void supervisor_from_user_child(void *p1, void *p2, void *p3)
{
set_fault_valid(true);
k_thread_create(&extra_thread,
extra_stack,
KOBJECT_STACK_SIZE,
supervisor_from_user_extra,
NULL, NULL, NULL,
0, 0, K_NO_WAIT);
zassert_unreachable("k_object validation failure in k thread create");
}
/**
* @brief Test to create a new supervisor thread from user
*
* @details The system kernel must prevent user threads from creating supervisor
* threads.
*
* @ingroup kernel_memprotect_tests
*/
void test_create_new_supervisor_thread_from_user(void)
{
set_fault_valid(false);
k_thread_access_grant(&child_thread,
&extra_thread,
&extra_stack);
k_thread_create(&child_thread,
child_stack,
KOBJECT_STACK_SIZE,
supervisor_from_user_child,
NULL, NULL, NULL,
0, K_USER, K_NO_WAIT);
k_thread_join(&child_thread, K_FOREVER);
}
/****************************************************************************/
/* object validation checks */
static void essential_thread_from_user_extra(void *p1, void *p2, void *p3)
{
zassert_unreachable("k_object validation failure in k thread create");
}
static void essential_thread_from_user_child(void *p1, void *p2, void *p3)
{
set_fault_valid(true);
k_thread_create(&extra_thread,
extra_stack,
KOBJECT_STACK_SIZE,
essential_thread_from_user_extra,
NULL, NULL, NULL,
0, K_USER | K_ESSENTIAL, K_NO_WAIT);
zassert_unreachable("k_object validation failure in k thread create");
}
/**
* @brief Create a new essential thread from user.
*
* @ingroup kernel_memprotect_tests
*/
void test_create_new_essential_thread_from_user(void)
{
set_fault_valid(false);
k_thread_access_grant(&child_thread,
&extra_thread,
&extra_stack);
k_thread_create(&child_thread,
child_stack,
KOBJECT_STACK_SIZE,
essential_thread_from_user_child,
NULL, NULL, NULL,
0, K_USER, K_NO_WAIT);
k_thread_join(&child_thread, K_FOREVER);
}
/****************************************************************************/
/* object validation checks */
static void higher_prio_from_user_extra(void *p1, void *p2, void *p3)
{
zassert_unreachable("k_object validation failure in k thread create");
}
static void higher_prio_from_user_child(void *p1, void *p2, void *p3)
{
set_fault_valid(true);
k_thread_create(&extra_thread,
extra_stack,
KOBJECT_STACK_SIZE,
higher_prio_from_user_extra,
NULL, NULL, NULL,
-1, K_USER, K_NO_WAIT);
zassert_unreachable("k_object validation failure in k thread create");
}
/**
* @brief Thread creation with priority is higher than current thread
*
* @details _handler_k_thread_create validation.
*
* @ingroup kernel_memprotect_tests
*/
void test_create_new_higher_prio_thread_from_user(void)
{
set_fault_valid(false);
k_thread_access_grant(&child_thread,
&extra_thread,
&extra_stack);
k_thread_create(&child_thread,
child_stack,
KOBJECT_STACK_SIZE,
higher_prio_from_user_child,
NULL, NULL, NULL,
0, K_USER, K_NO_WAIT);
k_thread_join(&child_thread, K_FOREVER);
}
/****************************************************************************/
/* object validation checks */
static void invalid_prio_from_user_extra(void *p1, void *p2, void *p3)
{
zassert_unreachable("k_object validation failure in k thread create");
}
static void invalid_prio_from_user_child(void *p1, void *p2, void *p3)
{
set_fault_valid(true);
k_thread_create(&extra_thread,
extra_stack,
KOBJECT_STACK_SIZE,
invalid_prio_from_user_extra,
NULL, NULL, NULL,
6000, K_USER, K_NO_WAIT);
zassert_unreachable("k_object validation failure in k thread create");
}
/**
* @brief Create a new thread whose priority is invalid.
*
* @details _handler_k_thread_create validation.
*
* @ingroup kernel_memprotect_tests
*/
void test_create_new_invalid_prio_thread_from_user(void)
{
set_fault_valid(false);
k_thread_access_grant(&child_thread,
&extra_thread,
&extra_stack);
k_thread_create(&child_thread,
child_stack,
KOBJECT_STACK_SIZE,
invalid_prio_from_user_child,
NULL, NULL, NULL,
0, K_USER, K_NO_WAIT);
k_thread_join(&child_thread, K_FOREVER);
}
/* Function to init thread's stack objects */
static void thread_stack_init_objects(void *p1, void *p2, void *p3)
{
int ret;
struct z_object *ko;
/* check that thread is initialized when running */
ko = z_object_find(&child_thread);
ret = z_object_validate(ko, K_OBJ_ANY, _OBJ_INIT_TRUE);
zassert_equal(ret, _OBJ_INIT_TRUE, NULL);
/* check that stack is initialized when running */
ko = z_object_find(child_stack);
ret = z_object_validate(ko, K_OBJ_ANY, _OBJ_INIT_TRUE);
zassert_equal(ret, _OBJ_INIT_TRUE, NULL);
}
/**
* @brief Test when thread exits, kernel marks stack objects uninitialized
*
* @details When thread exits, the kernel upon thread exit, should mark
* the exiting thread and thread stack object as uninitialized
*
* @ingroup kernel_memprotect_tests
*/
void test_mark_thread_exit_uninitialized(void)
{
set_fault_valid(false);
int ret;
struct z_object *ko;
k_thread_access_grant(&child_thread,
&child_stack);
k_thread_create(&child_thread,
child_stack,
KOBJECT_STACK_SIZE,
thread_stack_init_objects,
NULL, NULL, NULL,
0, K_INHERIT_PERMS, K_NO_WAIT);
k_thread_join(&child_thread, K_FOREVER);
/* check thread is uninitialized after its exit */
ko = z_object_find(&child_thread);
ret = z_object_validate(ko, K_OBJ_ANY, _OBJ_INIT_FALSE);
zassert_equal(ret, _OBJ_INIT_FALSE, NULL);
/* check stack is uninitialized after thread exit */
ko = z_object_find(child_stack);
ret = z_object_validate(ko, K_OBJ_ANY, _OBJ_INIT_FALSE);
zassert_equal(ret, _OBJ_INIT_FALSE, NULL);
}
/****************************************************************************/
/* object validation checks */
static void tThread_object_free_error(void *p1, void *p2, void *p3)
{
/* a K_ERR_CPU_EXCEPTION expected */
set_fault_valid(true);
k_object_free(NULL);
}
/**
* @brief Test free an invalid kernel object
*
* @details Spawn a thread free a NULL, an expected fault happened.
*
* @see k_object_free()
*
* @ingroup kernel_memprotect_tests
*/
void test_kobject_free_error(void)
{
uint32_t perm = K_INHERIT_PERMS;
if (k_is_user_context()) {
perm = perm | K_USER;
}
k_tid_t tid = k_thread_create(&child_thread, child_stack,
K_THREAD_STACK_SIZEOF(child_stack),
(k_thread_entry_t)&tThread_object_free_error,
(void *)&tid, NULL, NULL,
K_PRIO_PREEMPT(1), perm, K_NO_WAIT);
k_thread_join(tid, K_FOREVER);
}
/**
* @brief Test alloc an invalid kernel object
*
* @details Allocate invalid kernel objects, then no allocation
* will be returned.
*
* @ingroup kernel_memprotect_tests
*
* @see k_object_alloc()
*/
void test_kobject_init_error(void)
{
/* invalid kernel object allocation */
zassert_is_null(k_object_alloc(K_OBJ_ANY-1),
"expected got NULL kobject");
zassert_is_null(k_object_alloc(K_OBJ_LAST),
"expected got NULL kobject");
/* futex not support */
zassert_is_null(k_object_alloc(K_OBJ_FUTEX),
"expected got NULL kobject");
}
/**
* @brief Test kernel object until out of memory
*
* @details Create a dynamic kernel object repeatedly until run out
* of all heap memory, an expected out of memory error generated.
*
* @see k_object_alloc()
*
* @ingroup kernel_memprotect_tests
*/
void test_kobj_create_out_of_memory(void)
{
int ttype;
int max_obj = 0;
void *create_obj[MAX_OBJ] = {0};
for (ttype = K_OBJ_MEM_SLAB; ttype < K_OBJ_CONDVAR ; ttype++) {
for (int i = 0; i < MAX_OBJ; i++) {
create_obj[i] = k_object_alloc(ttype);
max_obj = i;
if (create_obj[i] == NULL) {
break;
}
}
zassert_is_null(create_obj[max_obj],
"excepted alloc failure");
printk("==max_obj(%d)\n", max_obj);
for (int i = 0; i < max_obj; i++) {
k_object_free((void *)create_obj[i]);
}
}
}
#ifdef CONFIG_DYNAMIC_OBJECTS
extern uint8_t _thread_idx_map[CONFIG_MAX_THREAD_BYTES];
#define MAX_THREAD_BITS (CONFIG_MAX_THREAD_BYTES * 8)
#endif
/* @brief Test alloc thread object until out of idex
*
* @details Allocate thread object until it out of index, no more
* thread can be allocated and report an error.
*
* @see k_object_alloc()
*
* @ingroup kernel_memprotect_tests
*/
void test_thread_alloc_out_of_idx(void)
{
#ifdef CONFIG_DYNAMIC_OBJECTS
struct k_thread *thread[MAX_THREAD_BITS];
struct k_thread *fail_thread;
int cur_max = 0;
for (int i = 0; i < MAX_THREAD_BITS; i++) {
thread[i] = k_object_alloc(K_OBJ_THREAD);
if (!thread[i]) {
cur_max = i;
break;
}
}
/** TESTPOINT: all the idx bits set to 1 */
for (int i = 0; i < CONFIG_MAX_THREAD_BYTES; i++) {
int idx = find_lsb_set(_thread_idx_map[i]);
zassert_true(idx == 0,
"idx shall all set to 1 when all used");
}
fail_thread = k_object_alloc(K_OBJ_THREAD);
/** TESTPOINT: thread alloc failed due to out of idx */
zassert_is_null(fail_thread,
"mo more kobj[%d](0x%lx) shall be allocated"
, cur_max, (uintptr_t)thread[cur_max]);
for (int i = 0; i < cur_max; i++) {
if (thread[i]) {
k_object_free(thread[i]);
}
}
#else
ztest_test_skip();
#endif
}
/**
* @brief Test kernel object allocation
*
* @details Allocate all kinds of kernel object and do permission
* operation functions.
*
* @see k_object_alloc()
*
* @ingroup kernel_memprotect_tests
*/
void test_alloc_kobjects(void)
{
struct k_thread *t;
struct k_msgq *m;
struct k_stack *s;
struct k_pipe *p;
struct k_queue *q;
struct k_mem_slab *mslab;
struct k_poll_signal *polls;
struct k_timer *timer;
struct k_mutex *mutex;
struct k_condvar *condvar;
void *ko;
/* allocate kernel object */
t = k_object_alloc(K_OBJ_THREAD);
zassert_not_null(t, "alloc obj (0x%lx)\n", (uintptr_t)t);
p = k_object_alloc(K_OBJ_PIPE);
zassert_not_null(p, "alloc obj (0x%lx)\n", (uintptr_t)p);
k_pipe_init(p, NULL, 0);
s = k_object_alloc(K_OBJ_STACK);
zassert_not_null(s, "alloc obj (0x%lx)\n", (uintptr_t)s);
k_stack_init(s, NULL, 0);
m = k_object_alloc(K_OBJ_MSGQ);
zassert_not_null(m, "alloc obj (0x%lx)\n", (uintptr_t)m);
k_msgq_init(m, NULL, 0, 0);
q = k_object_alloc(K_OBJ_QUEUE);
zassert_not_null(q, "alloc obj (0x%lx)\n", (uintptr_t)q);
/* release operations */
k_object_release((void *)t);
k_object_release((void *)p);
k_object_release((void *)s);
k_object_release((void *)m);
k_object_release((void *)q);
mslab = k_object_alloc(K_OBJ_MEM_SLAB);
zassert_not_null(mslab, "alloc obj (0x%lx)\n", (uintptr_t)mslab);
polls = k_object_alloc(K_OBJ_POLL_SIGNAL);
zassert_not_null(polls, "alloc obj (0x%lx)\n", (uintptr_t)polls);
timer = k_object_alloc(K_OBJ_TIMER);
zassert_not_null(timer, "alloc obj (0x%lx)\n", (uintptr_t)timer);
mutex = k_object_alloc(K_OBJ_MUTEX);
zassert_not_null(mutex, "alloc obj (0x%lx)\n", (uintptr_t)mutex);
condvar = k_object_alloc(K_OBJ_CONDVAR);
zassert_not_null(condvar, "alloc obj (0x%lx)\n", (uintptr_t)condvar);
k_object_release((void *)mslab);
k_object_release((void *)polls);
k_object_release((void *)timer);
k_object_release((void *)mutex);
/* no real object will be allocated */
ko = k_object_alloc(K_OBJ_ANY);
zassert_is_null(ko, "alloc obj (0x%lx)\n", (uintptr_t)ko);
ko = k_object_alloc(K_OBJ_LAST);
zassert_is_null(ko, "alloc obj (0x%lx)\n", (uintptr_t)ko);
/* alloc possible device driver */
ko = k_object_alloc(K_OBJ_LAST-1);
zassert_not_null(ko, "alloc obj (0x%lx)\n", (uintptr_t)ko);
k_object_release((void *)ko);
}
/* static kobject for permission testing */
struct k_mem_slab ms;
struct k_msgq mq;
struct k_mutex mutex;
struct k_pipe p;
struct k_queue q;
struct k_poll_signal ps;
struct k_sem sem;
struct k_stack s;
struct k_thread t;
struct k_timer timer;
struct z_thread_stack_element zs;
struct k_futex f;
struct k_condvar c;
static void entry_error_perm(void *p1, void *p2, void *p3)
{
set_fault_valid(true);
k_object_access_grant(p2, k_current_get());
}
/**
* @brief Test grant access failed in user mode
*
* @details Before grant access of static kobject to user thread, any
* grant access to this thread, will trigger an expected thread
* permission error.
*
* @see k_thread_access_grant()
*
* @ingroup kernel_memprotect_tests
*/
void test_kobject_perm_error(void)
{
void *kobj[16];
kobj[0] = &ms;
kobj[1] = &mq;
kobj[2] = &mutex;
kobj[3] = &p;
kobj[4] = &q;
kobj[5] = &ps;
kobj[6] = &sem;
kobj[7] = &s;
kobj[8] = &t;
kobj[9] = &timer;
kobj[10] = &zs;
kobj[11] = &f;
kobj[12] = &c;
for (int i = 0; i < 12 ; i++) {
k_tid_t tid = k_thread_create(&child_thread, child_stack,
K_THREAD_STACK_SIZEOF(child_stack),
(k_thread_entry_t)entry_error_perm,
(void *)&tid, kobj[i], NULL,
1, K_USER, K_NO_WAIT);
k_thread_join(tid, K_FOREVER);
}
}
extern const char *otype_to_str(enum k_objects otype);
/**
* @brief Test get all kernel object list
*
* @details Get all of the kernel object in kobject list.
*
* @ingroup kernel_memprotect_tests
*/
void test_all_kobjects_str(void)
{
enum k_objects otype = K_OBJ_ANY;
const char *c;
int cmp;
do {
c = otype_to_str(otype);
cmp = strcmp(c, "?");
if (otype != K_OBJ_LAST) {
zassert_true(cmp != 0,
"otype %d unexpectedly maps to last entry \"?\"", otype);
} else {
zassert_true(cmp == 0,
"otype %d does not map to last entry \"?\"", otype);
}
otype++;
} while (otype <= K_OBJ_LAST);
}