1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00

core/thread: fix thread_measure_stack_free()

`thread_measure_stack_free()` previously assumed that reading past the
stack is safe. When the stack was indeed part of a thread, the
`thread_t` structure is put after the stack, increasing the odds of
this assumption to hold. However, `thread_measure_stack_free()` could
also be used on the ISR stack, which may be allocated at the end of
SRAM.

A second parameter had to be added to indicate the stack size, so that
reading past the stack can now be prevented.

This also makes valgrind happy on `native`/`native64`.
This commit is contained in:
Marian Buschsieweke 2024-05-31 10:59:14 +02:00
parent 835eaee630
commit e93b5e4b98
No known key found for this signature in database
GPG Key ID: 77AA882EC78084E6
6 changed files with 40 additions and 15 deletions

View File

@ -450,15 +450,20 @@ static inline const char *thread_getname(kernel_pid_t pid)
#endif #endif
/** /**
* @brief Measures the stack usage of a stack * @brief Measures the stack usage of a stack
* @internal Should not be used externally
* *
* Only works if the thread was created with the flag THREAD_CREATE_STACKTEST. * Only works if the stack is filled with canaries
* (`*((uintptr_t *)ptr) == (uintptr_t)ptr` for naturally aligned `ptr` within
* the stack).
* *
* @param[in] stack the stack you want to measure. Try `thread_get_stackstart(thread_get_active())` * @param[in] stack the stack you want to measure. Try
* `thread_get_stackstart(thread_get_active())`
* @param[in] size size of @p stack in bytes
* *
* @return the amount of unused space of the thread's stack * @return the amount of unused space of the thread's stack
*/ */
uintptr_t thread_measure_stack_free(const char *stack); uintptr_t measure_stack_free_internal(const char *stack, size_t size);
/** /**
* @brief Get the number of bytes used on the ISR stack * @brief Get the number of bytes used on the ISR stack
@ -621,6 +626,24 @@ static inline const char *thread_get_name(const thread_t *thread)
#endif #endif
} }
/**
* @brief Measures the stack usage of a stack
*
* @pre Only works if the thread was created with the flag
* `THREAD_CREATE_STACKTEST`.
*
* @param[in] thread The thread to measure the stack of
*
* @return the amount of unused space of the thread's stack
*/
static inline uintptr_t thread_measure_stack_free(const thread_t *thread)
{
/* explicitly casting void pointers is bad code style, but needed for C++
* compatibility */
return measure_stack_free_internal((const char *)thread_get_stackstart(thread),
thread_get_stacksize(thread));
}
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@ -171,15 +171,18 @@ void thread_add_to_list(list_node_t *list, thread_t *thread)
list->next = new_node; list->next = new_node;
} }
uintptr_t thread_measure_stack_free(const char *stack) uintptr_t measure_stack_free_internal(const char *stack, size_t size)
{ {
/* Alignment of stack has been fixed (if needed) by thread_create(), so /* Alignment of stack has been fixed (if needed) by thread_create(), so
* we can silence -Wcast-align here */ * we can silence -Wcast-align here */
uintptr_t *stackp = (uintptr_t *)(uintptr_t)stack; uintptr_t *stackp = (uintptr_t *)(uintptr_t)stack;
uintptr_t end = (uintptr_t)stack + size;
/* better be safe than sorry: align end of stack just in case */
end &= (sizeof(uintptr_t) - 1);
/* assume that the comparison fails before or after end of stack */
/* assume that the stack grows "downwards" */ /* assume that the stack grows "downwards" */
while (*stackp == (uintptr_t)stackp) { while (((uintptr_t)stackp < end) && (*stackp == (uintptr_t)stackp)) {
stackp++; stackp++;
} }

View File

@ -197,7 +197,7 @@ UBaseType_t uxTaskGetStackHighWaterMark(TaskHandle_t xTask)
thread_t *thread = thread_get((xTask == NULL) ? (uint32_t)thread_getpid() thread_t *thread = thread_get((xTask == NULL) ? (uint32_t)thread_getpid()
: (uint32_t)xTask); : (uint32_t)xTask);
assert(thread != NULL); assert(thread != NULL);
return thread_measure_stack_free(thread->stack_start); return thread_measure_stack_free(thread);
} }
void *pvTaskGetThreadLocalStoragePointer(TaskHandle_t xTaskToQuery, void *pvTaskGetThreadLocalStoragePointer(TaskHandle_t xTaskToQuery,

View File

@ -92,10 +92,9 @@ void thread_isr_stack_init(void)
int thread_isr_stack_usage(void) int thread_isr_stack_usage(void)
{ {
/* cppcheck-suppress comparePointers size_t stack_size = (uintptr_t)&port_IntStackTop - (uintptr_t)&port_IntStack;
* (reason: comes from ESP-SDK, so should be valid) */ return stack_size -
return &port_IntStackTop - &port_IntStack - measure_stack_free_internal((char *)&port_IntStack, stack_size);
thread_measure_stack_free((char*)&port_IntStack);
} }
void *thread_isr_stack_pointer(void) void *thread_isr_stack_pointer(void)

View File

@ -98,7 +98,7 @@ void ps(void)
#ifdef DEVELHELP #ifdef DEVELHELP
int stacksz = thread_get_stacksize(p); /* get stack size */ int stacksz = thread_get_stacksize(p); /* get stack size */
overall_stacksz += stacksz; overall_stacksz += stacksz;
int stack_free = thread_measure_stack_free(thread_get_stackstart(p)); int stack_free = thread_measure_stack_free(p);
stacksz -= stack_free; stacksz -= stack_free;
overall_used += stacksz; overall_used += stacksz;
#endif #endif

View File

@ -34,7 +34,7 @@
void print_stack_usage_metric(const char *name, void *stack, unsigned max_size) void print_stack_usage_metric(const char *name, void *stack, unsigned max_size)
{ {
unsigned free = thread_measure_stack_free(stack); unsigned free = measure_stack_free_internal(stack, max_size);
if ((LOG_LEVEL >= LOG_INFO) && if ((LOG_LEVEL >= LOG_INFO) &&
(thread_get_stacksize(thread_get_active()) >= MIN_SIZE)) { (thread_get_stacksize(thread_get_active()) >= MIN_SIZE)) {