tests: thread_stack: enforce identities
There are predictable relationships between the actual size of a stack object, the return value of K_*_STACK_SIZEOF() macros, and the original size passed in when the stack was declared. Signed-off-by: Andrew Boie <andrew.p.boie@intel.com>
This commit is contained in:
parent
5bd9afe484
commit
8067127a6f
|
@ -60,10 +60,38 @@ static inline int z_vrfy_check_perms(void *addr, size_t size, int write)
|
|||
#include <syscalls/check_perms_mrsh.c>
|
||||
#endif /* CONFIG_USERSPACE */
|
||||
|
||||
void stack_buffer_scenarios(k_thread_stack_t *stack_obj, size_t obj_size,
|
||||
bool is_user_stack)
|
||||
/* Global data structure with object information, used by
|
||||
* stack_buffer_scenarios
|
||||
*/
|
||||
ZTEST_BMEM struct scenario_data {
|
||||
k_thread_stack_t *stack;
|
||||
|
||||
/* If this was declared with K_THREAD_STACK_DEFINE and not
|
||||
* K_KERNEL_STACK_DEFINE
|
||||
*/
|
||||
bool is_user;
|
||||
|
||||
/* Stack size stored in kernel object metadata if a user stack */
|
||||
size_t metadata_size;
|
||||
|
||||
/* Return value of sizeof(stack) */
|
||||
size_t object_size;
|
||||
|
||||
/* Return value of K_{THREAD|KERNEL}_STACK_SIZEOF(stack) */
|
||||
size_t reported_size;
|
||||
|
||||
/* Original size argument passed to K_{THREAD|KERNEL}_STACK_DECLARE */
|
||||
size_t declared_size;
|
||||
|
||||
/* Whether this stack is part of an array of thread stacks */
|
||||
bool is_array;
|
||||
} scenario_data;
|
||||
|
||||
void stack_buffer_scenarios(void)
|
||||
{
|
||||
size_t stack_size, unused, carveout, reserved, alignment;
|
||||
k_thread_stack_t *stack_obj = scenario_data.stack;
|
||||
size_t obj_size = scenario_data.object_size;
|
||||
size_t stack_size, unused, carveout, reserved, alignment, adjusted;
|
||||
uint8_t val;
|
||||
char *stack_start, *stack_ptr, *stack_end, *obj_start, *obj_end;
|
||||
char *stack_buf;
|
||||
|
@ -71,6 +99,7 @@ void stack_buffer_scenarios(k_thread_stack_t *stack_obj, size_t obj_size,
|
|||
int ret, expected;
|
||||
uintptr_t base = (uintptr_t)stack_obj;
|
||||
bool is_usermode;
|
||||
long int end_space;
|
||||
|
||||
#ifdef CONFIG_USERSPACE
|
||||
is_usermode = arch_is_user_context();
|
||||
|
@ -83,7 +112,7 @@ void stack_buffer_scenarios(k_thread_stack_t *stack_obj, size_t obj_size,
|
|||
stack_size);
|
||||
|
||||
#ifdef CONFIG_USERSPACE
|
||||
if (is_user_stack) {
|
||||
if (scenario_data.is_user) {
|
||||
reserved = K_THREAD_STACK_RESERVED;
|
||||
stack_buf = Z_THREAD_STACK_BUFFER(stack_obj);
|
||||
alignment = Z_THREAD_STACK_OBJ_ALIGN(stack_size);
|
||||
|
@ -99,6 +128,8 @@ void stack_buffer_scenarios(k_thread_stack_t *stack_obj, size_t obj_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
|
||||
*/
|
||||
|
@ -168,8 +199,75 @@ void stack_buffer_scenarios(k_thread_stack_t *stack_obj, size_t obj_size,
|
|||
printk(" - Carved-out space in buffer: %zu\n", carveout);
|
||||
zassert_true(carveout < stack_size,
|
||||
"Suspicious carve-out space reported");
|
||||
/* Generally 0 unless this is a stack array */
|
||||
printk(" - Unused object space: %zu\n", obj_end - stack_end);
|
||||
/* 0 unless this is a stack array */
|
||||
end_space = obj_end - stack_end;
|
||||
printk(" - Unused objects space: %ld\n", end_space);
|
||||
|
||||
/* For all stacks, when k_thread_create() is called with a stack object,
|
||||
* it is equivalent to pass either the original requested stack size, or
|
||||
* the return value of K_*_STACK_SIZEOF() for that stack object.
|
||||
*
|
||||
* When the stack is actually instantiated, both expand to fill any space
|
||||
* rounded up, except rounding space for array members.
|
||||
*/
|
||||
if (!scenario_data.is_array) {
|
||||
/* These should be exactly the same. We have an equivalence relation:
|
||||
* For some stack declared with:
|
||||
*
|
||||
* K_THREAD_STACK_DEFINE(my_stack, X);
|
||||
* Z_THREAD_STACK_SIZE_ADJUST(X) - K_THREAD_STACK_RESERVED ==
|
||||
* K_THREAD_STACK_SIZEOF(my_stack)
|
||||
*
|
||||
* K_KERNEL_STACK_DEFINE(my_kern_stack, Y):
|
||||
* Z_KERNEL_STACK_SIZE_ADJUST(Y) - K_KERNEL_STACK_RESERVED ==
|
||||
* K_KERNEL_STACK_SIZEOF(my_stack)
|
||||
*/
|
||||
#ifdef CONFIG_USERSPACE
|
||||
/* Not defined if user mode disabled, all stacks are kernel stacks */
|
||||
if (scenario_data.is_user) {
|
||||
adjusted = Z_THREAD_STACK_SIZE_ADJUST(scenario_data.declared_size);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
adjusted = Z_KERNEL_STACK_SIZE_ADJUST(scenario_data.declared_size);
|
||||
}
|
||||
adjusted -= reserved;
|
||||
|
||||
zassert_equal(end_space, 0, "unexpected unused space\n");
|
||||
} else {
|
||||
/* For arrays there may be unused space per-object. This is because
|
||||
* every single array member must be aligned to the value returned
|
||||
* by Z_{KERNEL|THREAD}_STACK_OBJ_ALIGN.
|
||||
*
|
||||
* If we define:
|
||||
*
|
||||
* K_{THREAD|KERNEL}_STACK_ARRAY_DEFINE(my_stack_array, num_stacks, X);
|
||||
*
|
||||
* We do not auto-expand usable space to cover this unused area. Doing
|
||||
* this would require some way for the kernel to know that a stack object
|
||||
* pointer passed in is an array member, which is currently not possible.
|
||||
*
|
||||
* The equivalence here is computable with:
|
||||
* K_THREAD_STACK_SIZEOF(my_stack_array[0]) ==
|
||||
* K_THREAD_STACK_LEN(X) - K_THREAD_STACK_RESERVED;
|
||||
*/
|
||||
|
||||
if (scenario_data.is_user) {
|
||||
adjusted = K_THREAD_STACK_LEN(scenario_data.declared_size);
|
||||
} else {
|
||||
adjusted = Z_KERNEL_STACK_LEN(scenario_data.declared_size);
|
||||
}
|
||||
adjusted -= reserved;
|
||||
|
||||
/* At least make sure it's not negative, that means stack_info isn't set
|
||||
* right
|
||||
*/
|
||||
zassert_true(end_space >= 0, "bad stack bounds in stack_info");
|
||||
}
|
||||
|
||||
zassert_true(adjusted == scenario_data.reported_size,
|
||||
"size mismatch: adjusted %zu vs. reported %zu",
|
||||
adjusted, scenario_data.reported_size);
|
||||
|
||||
ret = k_thread_stack_space_get(k_current_get(), &unused);
|
||||
if (!is_usermode && IS_ENABLED(CONFIG_NO_UNUSED_STACK_INSPECTION)) {
|
||||
|
@ -184,13 +282,6 @@ void stack_buffer_scenarios(k_thread_stack_t *stack_obj, size_t obj_size,
|
|||
}
|
||||
}
|
||||
|
||||
ZTEST_BMEM struct scenario_data {
|
||||
k_thread_stack_t *stack;
|
||||
bool is_user;
|
||||
size_t metadata_size;
|
||||
size_t object_size;
|
||||
} scenario_data;
|
||||
|
||||
void stest_thread_entry(void *p1, void *p2, void *p3)
|
||||
{
|
||||
bool drop = (bool)p1;
|
||||
|
@ -199,9 +290,7 @@ void stest_thread_entry(void *p1, void *p2, void *p3)
|
|||
k_thread_user_mode_enter(stest_thread_entry, (void *)false,
|
||||
p2, p3);
|
||||
} else {
|
||||
stack_buffer_scenarios(scenario_data.stack,
|
||||
scenario_data.object_size,
|
||||
scenario_data.is_user);
|
||||
stack_buffer_scenarios();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -221,7 +310,8 @@ void stest_thread_launch(uint32_t flags, bool drop)
|
|||
printk("target thread unused stack space: %zu\n", unused);
|
||||
}
|
||||
|
||||
void scenario_entry(void *stack_obj, size_t obj_size)
|
||||
void scenario_entry(void *stack_obj, size_t obj_size, size_t reported_size,
|
||||
size_t declared_size, bool is_array)
|
||||
{
|
||||
bool is_user;
|
||||
size_t metadata_size;
|
||||
|
@ -250,6 +340,9 @@ void scenario_entry(void *stack_obj, size_t obj_size)
|
|||
scenario_data.object_size = obj_size;
|
||||
scenario_data.is_user = is_user;
|
||||
scenario_data.metadata_size = metadata_size;
|
||||
scenario_data.reported_size = reported_size;
|
||||
scenario_data.declared_size = declared_size;
|
||||
scenario_data.is_array = is_array;
|
||||
|
||||
printk("Stack object %p[%zu]\n", stack_obj, obj_size);
|
||||
printk(" - Testing supervisor mode\n");
|
||||
|
@ -291,26 +384,34 @@ void test_stack_buffer(void)
|
|||
printk("Provided stack size: %u\n", STEST_STACKSIZE);
|
||||
|
||||
printk("\ntesting user_stack\n");
|
||||
scenario_entry(user_stack, sizeof(user_stack));
|
||||
scenario_entry(user_stack, sizeof(user_stack), K_THREAD_STACK_SIZEOF(user_stack),
|
||||
STEST_STACKSIZE, false);
|
||||
|
||||
for (int i = 0; i < NUM_STACKS; i++) {
|
||||
printk("\ntesting user_stack_array[%d]\n", i);
|
||||
scenario_entry(user_stack_array[i],
|
||||
sizeof(user_stack_array[i]));
|
||||
sizeof(user_stack_array[i]),
|
||||
K_THREAD_STACK_SIZEOF(user_stack_array[i]),
|
||||
STEST_STACKSIZE, true);
|
||||
}
|
||||
|
||||
printk("\ntesting kern_stack\n");
|
||||
scenario_entry(kern_stack, sizeof(kern_stack));
|
||||
scenario_entry(kern_stack, sizeof(kern_stack), K_KERNEL_STACK_SIZEOF(kern_stack),
|
||||
STEST_STACKSIZE, false);
|
||||
|
||||
for (int i = 0; i < NUM_STACKS; i++) {
|
||||
printk("\ntesting kern_stack_array[%d]\n", i);
|
||||
scenario_entry(kern_stack_array[i],
|
||||
sizeof(kern_stack_array[i]));
|
||||
sizeof(kern_stack_array[i]),
|
||||
K_KERNEL_STACK_SIZEOF(kern_stack_array[i]),
|
||||
STEST_STACKSIZE, true);
|
||||
}
|
||||
|
||||
printk("\ntesting stest_member_stack\n");
|
||||
scenario_entry(&stest_member_stack.stack,
|
||||
sizeof(stest_member_stack.stack));
|
||||
sizeof(stest_member_stack.stack),
|
||||
K_KERNEL_STACK_SIZEOF(stest_member_stack.stack),
|
||||
STEST_STACKSIZE, false);
|
||||
}
|
||||
|
||||
void test_main(void)
|
||||
|
|
Loading…
Reference in a new issue