|
14 | 14 | #include <stdlib.h> |
15 | 15 | #include <app_memory/app_memdomain.h> |
16 | 16 | #include <misc/util.h> |
| 17 | +#include <misc/stack.h> |
| 18 | +#include <syscall_handler.h> |
| 19 | +#include "test_syscall.h" |
17 | 20 |
|
18 | 21 | #if defined(CONFIG_ARC) |
19 | 22 | #include <arch/arc/v2/mpu/arc_core_mpu.h> |
@@ -895,6 +898,193 @@ static void domain_remove_part_context_switch(void) |
895 | 898 | spawn_user(); |
896 | 899 | } |
897 | 900 |
|
| 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 | + |
898 | 1088 | void test_main(void) |
899 | 1089 | { |
900 | 1090 | struct k_mem_partition *parts[] = {&part0, &part1, |
@@ -944,7 +1134,8 @@ void test_main(void) |
944 | 1134 | ztest_unit_test(domain_add_thread_context_switch), |
945 | 1135 | ztest_unit_test(domain_add_part_context_switch), |
946 | 1136 | 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) |
948 | 1139 | ); |
949 | 1140 | ztest_run_test_suite(userspace); |
950 | 1141 | } |
0 commit comments