tests: mem_protect: add error test case of userspace

Add some error test cases for userspace of memory protection module.
This increase the code coverage of testing.

Signed-off-by: Enjia Mai <enjiax.mai@intel.com>
This commit is contained in:
Enjia Mai 2021-03-19 12:13:27 +08:00 committed by Carles Cufí
commit 3a500dfdfc
4 changed files with 417 additions and 7 deletions

View file

@ -2,5 +2,5 @@ CONFIG_STACK_CANARIES=n
CONFIG_ZTEST=y
CONFIG_ZTEST_STACKSIZE=2048
CONFIG_MAX_THREAD_BYTES=4
CONFIG_USERSPACE=y
CONFIG_TEST_USERSPACE=y
CONFIG_APPLICATION_DEFINED_SYSCALL=y

View file

@ -52,6 +52,78 @@ void test_kobject_access_grant(void)
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_msgq *m;
m = k_object_alloc(K_OBJ_MSGQ);
k_object_access_grant(m, k_current_get());
set_fault_valid(true);
/* a K_ERR_KERNEL_OOPS expected */
k_object_access_grant(m, 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)
{
@ -177,14 +249,11 @@ static void kobject_grant_access_extra_entry(void *p1, void *p2, void *p3)
}
/**
* @brief Test grant access
*
* @details Will grant access to another thread for the
* semaphore it holds.
* @brief Test access revoke
*
* @ingroup kernel_memprotect_tests
*
* @see k_thread_access_grant()
* @see k_thread_access_grant(), k_object_access_revoke()
*/
void test_kobject_grant_access_kobj(void)
{
@ -282,6 +351,22 @@ void test_kobject_release_from_user(void)
k_thread_join(&child_thread, K_FOREVER);
}
/* @brief Test release kernel a invaild kobject
*
* @details Validate release kernel objects with NULL parameter.
*
* @see k_object_release()
*
* @ingroup kernel_memprotect_tests
*/
void test_kobject_release_null(void)
{
int dummy;
k_object_release(&dummy);
}
/****************************************************************************/
static void access_all_grant_child_give(void *p1, void *p2, void *p3)
{
@ -325,6 +410,7 @@ void test_kobject_access_all_grant(void)
k_thread_join(&child_thread, K_FOREVER);
}
/****************************************************************************/
static void residual_permissions_child_success(void *p1, void *p2, void *p3)
@ -994,3 +1080,289 @@ void test_mark_thread_exit_uninitialized(void)
ret = z_object_validate(ko, K_OBJ_ANY, _OBJ_INIT_FALSE);
zassert_equal(ret, _OBJ_INIT_FALSE, NULL);
}
/****************************************************************************/
/* object validatoin 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 (_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 alloction
* will be returned.
*
* @ingroup kernel_memprotect_tests
*
* @see k_object_alloc()
*/
void test_kobject_init_error(void)
{
/* invalid kernel object alloction */
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(thread[cur_max],
"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;
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);
s = k_object_alloc(K_OBJ_STACK);
zassert_not_null(s, "alloc obj (0x%lx)\n", (uintptr_t)s);
m = k_object_alloc(K_OBJ_MSGQ);
zassert_not_null(m, "alloc obj (0x%lx)\n", (uintptr_t)m);
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);
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);
}
}

View file

@ -14,11 +14,16 @@
#include <stdlib.h>
#include "mem_protect.h"
K_HEAP_DEFINE(test_mem_heap, TEST_HEAP_SIZE);
K_THREAD_STACK_DEFINE(test_stack, KOBJECT_STACK_SIZE);
void test_main(void)
{
k_thread_priority_set(k_current_get(), -1);
k_thread_heap_assign(k_current_get(), &test_mem_heap);
ztest_test_suite(memory_protection_test_suite,
/* inherit.c */
ztest_unit_test(test_permission_inheritance),
@ -43,12 +48,17 @@ void test_main(void)
/* kobject.c */
ztest_unit_test(test_kobject_access_grant),
ztest_unit_test(test_kobject_access_grant_error),
ztest_user_unit_test(test_kobject_access_grant_error_user_null),
ztest_user_unit_test(test_kobject_access_grant_error_user),
ztest_user_unit_test(test_kobject_access_all_grant_error),
ztest_unit_test(test_syscall_invalid_kobject),
ztest_unit_test(test_thread_without_kobject_permission),
ztest_unit_test(test_kobject_revoke_access),
ztest_unit_test(test_kobject_grant_access_kobj),
ztest_unit_test(test_kobject_grant_access_kobj_invalid),
ztest_unit_test(test_kobject_release_from_user),
ztest_unit_test(test_kobject_release_null),
ztest_unit_test(test_kobject_access_all_grant),
ztest_unit_test(test_thread_has_residual_permissions),
ztest_unit_test(test_kobject_access_grant_to_invalid_thread),
@ -65,7 +75,13 @@ void test_main(void)
ztest_unit_test(test_create_new_essential_thread_from_user),
ztest_unit_test(test_create_new_higher_prio_thread_from_user),
ztest_unit_test(test_create_new_invalid_prio_thread_from_user),
ztest_unit_test(test_mark_thread_exit_uninitialized)
ztest_unit_test(test_mark_thread_exit_uninitialized),
ztest_user_unit_test(test_kobject_init_error),
ztest_unit_test(test_alloc_kobjects),
ztest_unit_test(test_thread_alloc_out_of_idx),
ztest_unit_test(test_kobj_create_out_of_memory),
ztest_unit_test(test_kobject_perm_error),
ztest_unit_test(test_kobject_free_error)
);
ztest_run_test_suite(memory_protection_test_suite);

View file

@ -53,6 +53,18 @@ extern void test_create_new_higher_prio_thread_from_user(void);
extern void test_create_new_invalid_prio_thread_from_user(void);
extern void test_mark_thread_exit_uninitialized(void);
extern void test_mem_part_overlap(void);
extern void test_kobject_access_grant_error(void);
extern void test_kobject_access_grant_error_user(void);
extern void test_kobject_access_grant_error_user_null(void);
extern void test_kobject_access_all_grant_error(void);
extern void test_kobject_release_null(void);
extern void test_kobject_free_error(void);
extern void test_kobject_init_error(void);
extern void test_kobj_create_out_of_memory(void);
extern void test_thread_alloc_out_of_idx(void);
extern void test_alloc_kobjects(void);
extern void test_kobject_perm_error(void);
/* Flag needed to figure out if the fault was expected or not. */
extern volatile bool valid_fault;
@ -115,6 +127,16 @@ static inline void set_fault_valid(bool valid)
/* for kobject.c */
#define KOBJECT_STACK_SIZE (512 + CONFIG_TEST_EXTRA_STACKSIZE)
#if defined(CONFIG_X86_64)
#define TEST_HEAP_SIZE (2 << CONFIG_MAX_THREAD_BYTES) * 1024
#define MAX_OBJ 512
#else
#define TEST_HEAP_SIZE (2 << CONFIG_MAX_THREAD_BYTES) * 256
#define MAX_OBJ 256
#endif
#ifndef _TEST_SYSCALLS_H_
#define _TEST_SYSCALLS_H_