Commit 3a500dfd authored by Enjia Mai's avatar Enjia Mai Committed by Carles Cufi
Browse files

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: default avatarEnjia Mai <enjiax.mai@intel.com>
parent 1a868208
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -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
+377 −5
Original line number Diff line number Diff line
@@ -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);
	}
}
+17 −1
Original line number Diff line number Diff line
@@ -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);
+22 −0
Original line number Diff line number Diff line
@@ -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_