tests: userspace: check stack buffer access
The stack information stored in the thread->stack_info fields need to represent the actual writable area for its associated thread. Perform various tests to ensure that the various reported and specified values are in agreement. Signed-off-by: Andrew Boie <andrew.p.boie@intel.com>
This commit is contained in:
parent
00a8818da2
commit
14db4eedff
4 changed files with 211 additions and 1 deletions
|
@ -1 +1,4 @@
|
|||
CONFIG_ZTEST=y
|
||||
CONFIG_INIT_STACKS=y
|
||||
CONFIG_APPLICATION_DEFINED_SYSCALL=y
|
||||
CONFIG_THREAD_USERSPACE_LOCAL_DATA=y
|
||||
|
|
|
@ -14,6 +14,9 @@
|
|||
#include <stdlib.h>
|
||||
#include <app_memory/app_memdomain.h>
|
||||
#include <misc/util.h>
|
||||
#include <misc/stack.h>
|
||||
#include <syscall_handler.h>
|
||||
#include "test_syscall.h"
|
||||
|
||||
#if defined(CONFIG_ARC)
|
||||
#include <arch/arc/v2/mpu/arc_core_mpu.h>
|
||||
|
@ -895,6 +898,193 @@ static void domain_remove_part_context_switch(void)
|
|||
spawn_user();
|
||||
}
|
||||
|
||||
/*
|
||||
* Stack testing
|
||||
*/
|
||||
|
||||
#define NUM_STACKS 3
|
||||
#define STEST_STACKSIZE 1024
|
||||
K_THREAD_STACK_DEFINE(stest_stack, STEST_STACKSIZE);
|
||||
K_THREAD_STACK_ARRAY_DEFINE(stest_stack_array, NUM_STACKS, STEST_STACKSIZE);
|
||||
|
||||
struct foo {
|
||||
int bar;
|
||||
K_THREAD_STACK_MEMBER(stack, STEST_STACKSIZE);
|
||||
int baz;
|
||||
};
|
||||
|
||||
struct foo stest_member_stack;
|
||||
|
||||
void z_impl_stack_info_get(u32_t *start_addr, u32_t *size)
|
||||
{
|
||||
*start_addr = k_current_get()->stack_info.start;
|
||||
*size = k_current_get()->stack_info.size;
|
||||
}
|
||||
|
||||
Z_SYSCALL_HANDLER(stack_info_get, start_addr, size)
|
||||
{
|
||||
Z_OOPS(Z_SYSCALL_MEMORY_WRITE(start_addr, sizeof(u32_t)));
|
||||
Z_OOPS(Z_SYSCALL_MEMORY_WRITE(size, sizeof(u32_t)));
|
||||
|
||||
z_impl_stack_info_get((u32_t *)start_addr, (u32_t *)size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int z_impl_check_perms(void *addr, size_t size, int write)
|
||||
{
|
||||
return z_arch_buffer_validate(addr, size, write);
|
||||
}
|
||||
|
||||
Z_SYSCALL_HANDLER(check_perms, addr, size, write)
|
||||
{
|
||||
return z_impl_check_perms((void *)addr, size, write);
|
||||
}
|
||||
|
||||
void stack_buffer_scenarios(k_thread_stack_t *stack_obj, size_t obj_size)
|
||||
{
|
||||
size_t stack_size;
|
||||
u8_t val;
|
||||
char *stack_start, *stack_ptr, *stack_end, *obj_start, *obj_end;
|
||||
volatile char *pos;
|
||||
|
||||
expect_fault = false;
|
||||
|
||||
|
||||
/* Dump interesting information */
|
||||
|
||||
stack_info_get((u32_t *)&stack_start, (u32_t *)&stack_size);
|
||||
printk(" - Thread reports buffer %p size %zu\n", stack_start,
|
||||
stack_size);
|
||||
|
||||
stack_end = stack_start + stack_size;
|
||||
obj_end = (char *)stack_obj + obj_size;
|
||||
obj_start = (char *)stack_obj;
|
||||
|
||||
/* Assert that the created stack object, with the reserved data
|
||||
* removed, can hold a thread buffer of STEST_STACKSIZE
|
||||
*/
|
||||
zassert_true(STEST_STACKSIZE <= (obj_size - K_THREAD_STACK_RESERVED),
|
||||
"bad stack size in object");
|
||||
|
||||
/* Check that the stack info in the thread marks a region
|
||||
* completely contained within the stack object
|
||||
*/
|
||||
zassert_true(stack_end <= obj_end,
|
||||
"stack size in thread struct out of bounds (overflow)");
|
||||
zassert_true(stack_start >= obj_start,
|
||||
"stack size in thread struct out of bounds (underflow)");
|
||||
|
||||
/* Check that the entire stack buffer is read/writable */
|
||||
printk(" - check read/write to stack buffer\n");
|
||||
|
||||
/* Address of this stack variable is guaranteed to part of
|
||||
* the active stack, and close to the actual stack pointer.
|
||||
* Some CPUs have hardware stack overflow detection which
|
||||
* faults on memory access within the stack buffer but below
|
||||
* the stack pointer.
|
||||
*
|
||||
* First test does direct read & write starting at the estimated
|
||||
* stack pointer up to the highest addresses in the buffer
|
||||
*/
|
||||
stack_ptr = &val;
|
||||
for (pos = stack_ptr; pos < stack_end; pos++) {
|
||||
/* pos is volatile so this doesn't get optimized out */
|
||||
val = *pos;
|
||||
*pos = val;
|
||||
}
|
||||
|
||||
if (z_arch_is_user_context()) {
|
||||
/* If we're in user mode, check every byte in the stack buffer
|
||||
* to ensure that the thread has permissions on it.
|
||||
*/
|
||||
for (pos = stack_start; pos < stack_end; pos++) {
|
||||
zassert_false(check_perms((void *)pos, 1, 1),
|
||||
"bad MPU/MMU permission on stack buffer at address %p",
|
||||
pos);
|
||||
}
|
||||
|
||||
/* Bounds check the user accessible area, it shouldn't extend
|
||||
* before or after the stack. Because of memory protection HW
|
||||
* alignment constraints, we test the end of the stack object
|
||||
* and not the buffer.
|
||||
*/
|
||||
zassert_true(check_perms(obj_start - 1, 1, 0),
|
||||
"user mode access to memory before start of stack object");
|
||||
zassert_true(check_perms(obj_end, 1, 0),
|
||||
"user mode access past end of stack object");
|
||||
}
|
||||
|
||||
|
||||
/* This API is being removed just whine about it for now */
|
||||
if (K_THREAD_STACK_BUFFER(stack_obj) != stack_start) {
|
||||
printk("WARNING: K_THREAD_STACK_BUFFER() reports %p\n",
|
||||
K_THREAD_STACK_BUFFER(stack_obj));
|
||||
}
|
||||
|
||||
if (z_arch_is_user_context()) {
|
||||
zassert_true(stack_size <= obj_size - K_THREAD_STACK_RESERVED,
|
||||
"bad stack size in thread struct");
|
||||
}
|
||||
|
||||
|
||||
k_sem_give(&uthread_end_sem);
|
||||
}
|
||||
|
||||
void stest_thread_entry(void *p1, void *p2, void *p3)
|
||||
{
|
||||
bool drop = (bool)p3;
|
||||
|
||||
if (drop) {
|
||||
k_thread_user_mode_enter(stest_thread_entry, p1, p2,
|
||||
(void *)false);
|
||||
} else {
|
||||
stack_buffer_scenarios((k_thread_stack_t *)p1, (size_t)p2);
|
||||
}
|
||||
}
|
||||
|
||||
void stest_thread_launch(void *stack_obj, size_t obj_size, u32_t flags,
|
||||
bool drop)
|
||||
{
|
||||
k_thread_create(&uthread_thread, stack_obj, STEST_STACKSIZE,
|
||||
stest_thread_entry, stack_obj, (void *)obj_size,
|
||||
(void *)drop,
|
||||
-1, flags, K_NO_WAIT);
|
||||
k_sem_take(&uthread_end_sem, K_FOREVER);
|
||||
|
||||
stack_analyze("test_thread", (char *)uthread_thread.stack_info.start,
|
||||
uthread_thread.stack_info.size);
|
||||
}
|
||||
|
||||
void scenario_entry(void *stack_obj, size_t obj_size)
|
||||
{
|
||||
printk("Stack object %p[%zu]\n", stack_obj, obj_size);
|
||||
printk(" - Testing supervisor mode\n");
|
||||
stest_thread_launch(stack_obj, obj_size, 0, false);
|
||||
printk(" - Testing user mode (direct launch)\n");
|
||||
stest_thread_launch(stack_obj, obj_size, K_USER | K_INHERIT_PERMS,
|
||||
false);
|
||||
printk(" - Testing user mode (drop)\n");
|
||||
stest_thread_launch(stack_obj, obj_size, K_INHERIT_PERMS,
|
||||
true);
|
||||
}
|
||||
|
||||
void test_stack_buffer(void)
|
||||
{
|
||||
printk("Reserved space: %u\n", K_THREAD_STACK_RESERVED);
|
||||
printk("Provided stack size: %u\n", STEST_STACKSIZE);
|
||||
scenario_entry(stest_stack, sizeof(stest_stack));
|
||||
|
||||
for (int i = 0; i < NUM_STACKS; i++) {
|
||||
scenario_entry(stest_stack_array[i],
|
||||
sizeof(stest_stack_array[i]));
|
||||
}
|
||||
|
||||
scenario_entry(&stest_member_stack.stack,
|
||||
sizeof(stest_member_stack.stack));
|
||||
|
||||
}
|
||||
|
||||
void test_main(void)
|
||||
{
|
||||
struct k_mem_partition *parts[] = {&part0, &part1,
|
||||
|
@ -944,7 +1134,8 @@ void test_main(void)
|
|||
ztest_unit_test(domain_add_thread_context_switch),
|
||||
ztest_unit_test(domain_add_part_context_switch),
|
||||
ztest_unit_test(domain_remove_part_context_switch),
|
||||
ztest_unit_test(domain_remove_thread_context_switch)
|
||||
ztest_unit_test(domain_remove_thread_context_switch),
|
||||
ztest_unit_test(test_stack_buffer)
|
||||
);
|
||||
ztest_run_test_suite(userspace);
|
||||
}
|
||||
|
|
15
tests/kernel/mem_protect/userspace/src/test_syscall.h
Normal file
15
tests/kernel/mem_protect/userspace/src/test_syscall.h
Normal file
|
@ -0,0 +1,15 @@
|
|||
/*
|
||||
* Copyright (c) 2019 Intel Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef USERSPACE_TEST_SYSCALL_H
|
||||
#define USERSPACE_TEST_SYSCALL_H
|
||||
|
||||
__syscall void stack_info_get(u32_t *start_addr, u32_t *size);
|
||||
__syscall int check_perms(void *addr, size_t size, int write);
|
||||
|
||||
#include <syscalls/test_syscall.h>
|
||||
|
||||
#endif
|
|
@ -2,3 +2,4 @@ tests:
|
|||
kernel.memory_protection.userspace:
|
||||
filter: CONFIG_ARCH_HAS_USERSPACE
|
||||
tags: kernel security userspace ignore_faults
|
||||
min_ram: 32
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue