Skip to content

Commit 14db4ee

Browse files
Andrew Boienashif
authored andcommitted
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 <[email protected]>
1 parent 00a8818 commit 14db4ee

File tree

4 files changed

+211
-1
lines changed

4 files changed

+211
-1
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,4 @@
11
CONFIG_ZTEST=y
2+
CONFIG_INIT_STACKS=y
3+
CONFIG_APPLICATION_DEFINED_SYSCALL=y
4+
CONFIG_THREAD_USERSPACE_LOCAL_DATA=y

tests/kernel/mem_protect/userspace/src/main.c

Lines changed: 192 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@
1414
#include <stdlib.h>
1515
#include <app_memory/app_memdomain.h>
1616
#include <misc/util.h>
17+
#include <misc/stack.h>
18+
#include <syscall_handler.h>
19+
#include "test_syscall.h"
1720

1821
#if defined(CONFIG_ARC)
1922
#include <arch/arc/v2/mpu/arc_core_mpu.h>
@@ -895,6 +898,193 @@ static void domain_remove_part_context_switch(void)
895898
spawn_user();
896899
}
897900

901+
/*
902+
* Stack testing
903+
*/
904+
905+
#define NUM_STACKS 3
906+
#define STEST_STACKSIZE 1024
907+
K_THREAD_STACK_DEFINE(stest_stack, STEST_STACKSIZE);
908+
K_THREAD_STACK_ARRAY_DEFINE(stest_stack_array, NUM_STACKS, STEST_STACKSIZE);
909+
910+
struct foo {
911+
int bar;
912+
K_THREAD_STACK_MEMBER(stack, STEST_STACKSIZE);
913+
int baz;
914+
};
915+
916+
struct foo stest_member_stack;
917+
918+
void z_impl_stack_info_get(u32_t *start_addr, u32_t *size)
919+
{
920+
*start_addr = k_current_get()->stack_info.start;
921+
*size = k_current_get()->stack_info.size;
922+
}
923+
924+
Z_SYSCALL_HANDLER(stack_info_get, start_addr, size)
925+
{
926+
Z_OOPS(Z_SYSCALL_MEMORY_WRITE(start_addr, sizeof(u32_t)));
927+
Z_OOPS(Z_SYSCALL_MEMORY_WRITE(size, sizeof(u32_t)));
928+
929+
z_impl_stack_info_get((u32_t *)start_addr, (u32_t *)size);
930+
931+
return 0;
932+
}
933+
934+
int z_impl_check_perms(void *addr, size_t size, int write)
935+
{
936+
return z_arch_buffer_validate(addr, size, write);
937+
}
938+
939+
Z_SYSCALL_HANDLER(check_perms, addr, size, write)
940+
{
941+
return z_impl_check_perms((void *)addr, size, write);
942+
}
943+
944+
void stack_buffer_scenarios(k_thread_stack_t *stack_obj, size_t obj_size)
945+
{
946+
size_t stack_size;
947+
u8_t val;
948+
char *stack_start, *stack_ptr, *stack_end, *obj_start, *obj_end;
949+
volatile char *pos;
950+
951+
expect_fault = false;
952+
953+
954+
/* Dump interesting information */
955+
956+
stack_info_get((u32_t *)&stack_start, (u32_t *)&stack_size);
957+
printk(" - Thread reports buffer %p size %zu\n", stack_start,
958+
stack_size);
959+
960+
stack_end = stack_start + stack_size;
961+
obj_end = (char *)stack_obj + obj_size;
962+
obj_start = (char *)stack_obj;
963+
964+
/* Assert that the created stack object, with the reserved data
965+
* removed, can hold a thread buffer of STEST_STACKSIZE
966+
*/
967+
zassert_true(STEST_STACKSIZE <= (obj_size - K_THREAD_STACK_RESERVED),
968+
"bad stack size in object");
969+
970+
/* Check that the stack info in the thread marks a region
971+
* completely contained within the stack object
972+
*/
973+
zassert_true(stack_end <= obj_end,
974+
"stack size in thread struct out of bounds (overflow)");
975+
zassert_true(stack_start >= obj_start,
976+
"stack size in thread struct out of bounds (underflow)");
977+
978+
/* Check that the entire stack buffer is read/writable */
979+
printk(" - check read/write to stack buffer\n");
980+
981+
/* Address of this stack variable is guaranteed to part of
982+
* the active stack, and close to the actual stack pointer.
983+
* Some CPUs have hardware stack overflow detection which
984+
* faults on memory access within the stack buffer but below
985+
* the stack pointer.
986+
*
987+
* First test does direct read & write starting at the estimated
988+
* stack pointer up to the highest addresses in the buffer
989+
*/
990+
stack_ptr = &val;
991+
for (pos = stack_ptr; pos < stack_end; pos++) {
992+
/* pos is volatile so this doesn't get optimized out */
993+
val = *pos;
994+
*pos = val;
995+
}
996+
997+
if (z_arch_is_user_context()) {
998+
/* If we're in user mode, check every byte in the stack buffer
999+
* to ensure that the thread has permissions on it.
1000+
*/
1001+
for (pos = stack_start; pos < stack_end; pos++) {
1002+
zassert_false(check_perms((void *)pos, 1, 1),
1003+
"bad MPU/MMU permission on stack buffer at address %p",
1004+
pos);
1005+
}
1006+
1007+
/* Bounds check the user accessible area, it shouldn't extend
1008+
* before or after the stack. Because of memory protection HW
1009+
* alignment constraints, we test the end of the stack object
1010+
* and not the buffer.
1011+
*/
1012+
zassert_true(check_perms(obj_start - 1, 1, 0),
1013+
"user mode access to memory before start of stack object");
1014+
zassert_true(check_perms(obj_end, 1, 0),
1015+
"user mode access past end of stack object");
1016+
}
1017+
1018+
1019+
/* This API is being removed just whine about it for now */
1020+
if (K_THREAD_STACK_BUFFER(stack_obj) != stack_start) {
1021+
printk("WARNING: K_THREAD_STACK_BUFFER() reports %p\n",
1022+
K_THREAD_STACK_BUFFER(stack_obj));
1023+
}
1024+
1025+
if (z_arch_is_user_context()) {
1026+
zassert_true(stack_size <= obj_size - K_THREAD_STACK_RESERVED,
1027+
"bad stack size in thread struct");
1028+
}
1029+
1030+
1031+
k_sem_give(&uthread_end_sem);
1032+
}
1033+
1034+
void stest_thread_entry(void *p1, void *p2, void *p3)
1035+
{
1036+
bool drop = (bool)p3;
1037+
1038+
if (drop) {
1039+
k_thread_user_mode_enter(stest_thread_entry, p1, p2,
1040+
(void *)false);
1041+
} else {
1042+
stack_buffer_scenarios((k_thread_stack_t *)p1, (size_t)p2);
1043+
}
1044+
}
1045+
1046+
void stest_thread_launch(void *stack_obj, size_t obj_size, u32_t flags,
1047+
bool drop)
1048+
{
1049+
k_thread_create(&uthread_thread, stack_obj, STEST_STACKSIZE,
1050+
stest_thread_entry, stack_obj, (void *)obj_size,
1051+
(void *)drop,
1052+
-1, flags, K_NO_WAIT);
1053+
k_sem_take(&uthread_end_sem, K_FOREVER);
1054+
1055+
stack_analyze("test_thread", (char *)uthread_thread.stack_info.start,
1056+
uthread_thread.stack_info.size);
1057+
}
1058+
1059+
void scenario_entry(void *stack_obj, size_t obj_size)
1060+
{
1061+
printk("Stack object %p[%zu]\n", stack_obj, obj_size);
1062+
printk(" - Testing supervisor mode\n");
1063+
stest_thread_launch(stack_obj, obj_size, 0, false);
1064+
printk(" - Testing user mode (direct launch)\n");
1065+
stest_thread_launch(stack_obj, obj_size, K_USER | K_INHERIT_PERMS,
1066+
false);
1067+
printk(" - Testing user mode (drop)\n");
1068+
stest_thread_launch(stack_obj, obj_size, K_INHERIT_PERMS,
1069+
true);
1070+
}
1071+
1072+
void test_stack_buffer(void)
1073+
{
1074+
printk("Reserved space: %u\n", K_THREAD_STACK_RESERVED);
1075+
printk("Provided stack size: %u\n", STEST_STACKSIZE);
1076+
scenario_entry(stest_stack, sizeof(stest_stack));
1077+
1078+
for (int i = 0; i < NUM_STACKS; i++) {
1079+
scenario_entry(stest_stack_array[i],
1080+
sizeof(stest_stack_array[i]));
1081+
}
1082+
1083+
scenario_entry(&stest_member_stack.stack,
1084+
sizeof(stest_member_stack.stack));
1085+
1086+
}
1087+
8981088
void test_main(void)
8991089
{
9001090
struct k_mem_partition *parts[] = {&part0, &part1,
@@ -944,7 +1134,8 @@ void test_main(void)
9441134
ztest_unit_test(domain_add_thread_context_switch),
9451135
ztest_unit_test(domain_add_part_context_switch),
9461136
ztest_unit_test(domain_remove_part_context_switch),
947-
ztest_unit_test(domain_remove_thread_context_switch)
1137+
ztest_unit_test(domain_remove_thread_context_switch),
1138+
ztest_unit_test(test_stack_buffer)
9481139
);
9491140
ztest_run_test_suite(userspace);
9501141
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/*
2+
* Copyright (c) 2019 Intel Corporation
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#ifndef USERSPACE_TEST_SYSCALL_H
8+
#define USERSPACE_TEST_SYSCALL_H
9+
10+
__syscall void stack_info_get(u32_t *start_addr, u32_t *size);
11+
__syscall int check_perms(void *addr, size_t size, int write);
12+
13+
#include <syscalls/test_syscall.h>
14+
15+
#endif

tests/kernel/mem_protect/userspace/testcase.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@ tests:
22
kernel.memory_protection.userspace:
33
filter: CONFIG_ARCH_HAS_USERSPACE
44
tags: kernel security userspace ignore_faults
5+
min_ram: 32

0 commit comments

Comments
 (0)