Commit e3fe3ebb authored by Stephen Smalley's avatar Stephen Smalley Committed by Andrew Boie
Browse files

tests/kernel/mem_protect/userspace: test access to other thread stack



Add tests of the ability to read or write the stack of another thread.
Use semaphores for explicit synchronization of the start and end of the
other thread to ensure that the attempted stack access occurs while the
thread is alive.  This ensures that the MMU/MPU has been configured at
least once to allow userspace access to the stack, and that any
removal of access upon thread termination has not yet occurred.  This
therefore should exercise changing the MMU/MPU configuration to remove
access to the other thread's stack when switching back to our
thread.

Tested on qemu_x86 (pass) and on frdm_k64f (with and without the ARM
userspace patches; with them, the tests pass; without, they fail as
expected).  Also, as with most of the other tests, if you replace
ztest_user_unit_test() with ztest_unit_test(), then the tests fail as
expected.

Signed-off-by: default avatarStephen Smalley <sds@tycho.nsa.gov>
parent 2fce4e4c
Loading
Loading
Loading
Loading
+80 −1
Original line number Diff line number Diff line
@@ -15,6 +15,10 @@

#define INFO(fmt, ...) printk(fmt, ##__VA_ARGS__)

K_SEM_DEFINE(uthread_start_sem, 0, 1);
K_SEM_DEFINE(uthread_end_sem, 0, 1);
static bool give_uthread_end_sem;

/* ARM is a special case, in that k_thread_abort() does indeed return
 * instead of calling _Swap() directly. The PendSV exception is queued
 * and immediately fires upon completing the exception path; the faulting
@@ -26,6 +30,14 @@ FUNC_NORETURN
void _SysFatalErrorHandler(unsigned int reason, const NANO_ESF *pEsf)
{
	INFO("Caught system error -- reason %d\n", reason);
	/*
	 * If there is a user thread waiting for notification to exit,
	 * give it that notification.
	 */
	if (give_uthread_end_sem) {
		give_uthread_end_sem = false;
		k_sem_give(&uthread_end_sem);
	}
	ztest_test_pass();
#ifndef CONFIG_ARM
	CODE_UNREACHABLE;
@@ -213,10 +225,75 @@ static void start_kernel_thread(void)
	zassert_unreachable("Create a kernel thread did not fault\n");
}

__kernel struct k_thread uthread_thread;
K_THREAD_STACK_DEFINE(uthread_stack, STACKSIZE);

static void uthread_body(void)
{
	/* Notify our creator that we are alive. */
	k_sem_give(&uthread_start_sem);
	/* Request notification of when we should exit. */
	give_uthread_end_sem = true;
	/* Wait until notified by the fault handler or by the creator. */
	k_sem_take(&uthread_end_sem, K_FOREVER);
}

static void read_other_stack(void)
{
	/* Try to read from another thread's stack. */
	unsigned int *ptr;

	k_thread_create(&uthread_thread, uthread_stack, STACKSIZE,
			(k_thread_entry_t)uthread_body, NULL, NULL, NULL,
			-1, K_USER | K_INHERIT_PERMS,
			K_NO_WAIT);

	/* Ensure that the other thread has begun. */
	k_sem_take(&uthread_start_sem, K_FOREVER);

	/* Try to directly read the stack of the other thread. */
	ptr = (unsigned int *)K_THREAD_STACK_BUFFER(uthread_stack);
	printk("%u\n", *ptr);

	/* Shouldn't be reached, but if so, let the other thread exit */
	if (give_uthread_end_sem) {
		give_uthread_end_sem = false;
		k_sem_give(&uthread_end_sem);
	}
	zassert_unreachable("Read from other thread stack did not fault\n");
}

static void write_other_stack(void)
{
	/* Try to write to another thread's stack. */
	unsigned int *ptr;

	k_thread_create(&uthread_thread, uthread_stack, STACKSIZE,
			(k_thread_entry_t)uthread_body, NULL, NULL, NULL,
			-1, K_USER | K_INHERIT_PERMS,
			K_NO_WAIT);

	/* Ensure that the other thread has begun. */
	k_sem_take(&uthread_start_sem, K_FOREVER);

	/* Try to directly write the stack of the other thread. */
	ptr = (unsigned int *) K_THREAD_STACK_BUFFER(uthread_stack);
	*ptr = 0;

	/* Shouldn't be reached, but if so, let the other thread exit */
	if (give_uthread_end_sem) {
		give_uthread_end_sem = false;
		k_sem_give(&uthread_end_sem);
	}
	zassert_unreachable("Write to other thread stack did not fault\n");
}

void test_main(void)
{
	k_thread_access_grant(k_current_get(),
			      &kthread_thread, &kthread_stack,
			      &uthread_thread, &uthread_stack,
			      &uthread_start_sem, &uthread_end_sem,
			      NULL);
	ztest_test_suite(test_userspace,
			 ztest_user_unit_test(is_usermode),
@@ -232,7 +309,9 @@ void test_main(void)
			 ztest_user_unit_test(write_kernel_stack),
			 ztest_user_unit_test(pass_user_object),
			 ztest_user_unit_test(pass_noperms_object),
			 ztest_user_unit_test(start_kernel_thread)
			 ztest_user_unit_test(start_kernel_thread),
			 ztest_user_unit_test(read_other_stack),
			 ztest_user_unit_test(write_other_stack)
		);
	ztest_run_test_suite(test_userspace);
}