Skip to content

Commit e93b5e4

Browse files
committed
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`.
1 parent 835eaee commit e93b5e4

File tree

6 files changed

+40
-15
lines changed

6 files changed

+40
-15
lines changed

core/include/thread.h

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -450,15 +450,20 @@ static inline const char *thread_getname(kernel_pid_t pid)
450450
#endif
451451

452452
/**
453-
* @brief Measures the stack usage of a stack
453+
* @brief Measures the stack usage of a stack
454+
* @internal Should not be used externally
454455
*
455-
* Only works if the thread was created with the flag THREAD_CREATE_STACKTEST.
456+
* Only works if the stack is filled with canaries
457+
* (`*((uintptr_t *)ptr) == (uintptr_t)ptr` for naturally aligned `ptr` within
458+
* the stack).
456459
*
457-
* @param[in] stack the stack you want to measure. Try `thread_get_stackstart(thread_get_active())`
460+
* @param[in] stack the stack you want to measure. Try
461+
* `thread_get_stackstart(thread_get_active())`
462+
* @param[in] size size of @p stack in bytes
458463
*
459-
* @return the amount of unused space of the thread's stack
464+
* @return the amount of unused space of the thread's stack
460465
*/
461-
uintptr_t thread_measure_stack_free(const char *stack);
466+
uintptr_t measure_stack_free_internal(const char *stack, size_t size);
462467

463468
/**
464469
* @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)
621626
#endif
622627
}
623628

629+
/**
630+
* @brief Measures the stack usage of a stack
631+
*
632+
* @pre Only works if the thread was created with the flag
633+
* `THREAD_CREATE_STACKTEST`.
634+
*
635+
* @param[in] thread The thread to measure the stack of
636+
*
637+
* @return the amount of unused space of the thread's stack
638+
*/
639+
static inline uintptr_t thread_measure_stack_free(const thread_t *thread)
640+
{
641+
/* explicitly casting void pointers is bad code style, but needed for C++
642+
* compatibility */
643+
return measure_stack_free_internal((const char *)thread_get_stackstart(thread),
644+
thread_get_stacksize(thread));
645+
}
646+
624647
#ifdef __cplusplus
625648
}
626649
#endif

core/thread.c

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -171,15 +171,18 @@ void thread_add_to_list(list_node_t *list, thread_t *thread)
171171
list->next = new_node;
172172
}
173173

174-
uintptr_t thread_measure_stack_free(const char *stack)
174+
uintptr_t measure_stack_free_internal(const char *stack, size_t size)
175175
{
176176
/* Alignment of stack has been fixed (if needed) by thread_create(), so
177177
* we can silence -Wcast-align here */
178178
uintptr_t *stackp = (uintptr_t *)(uintptr_t)stack;
179+
uintptr_t end = (uintptr_t)stack + size;
180+
181+
/* better be safe than sorry: align end of stack just in case */
182+
end &= (sizeof(uintptr_t) - 1);
179183

180-
/* assume that the comparison fails before or after end of stack */
181184
/* assume that the stack grows "downwards" */
182-
while (*stackp == (uintptr_t)stackp) {
185+
while (((uintptr_t)stackp < end) && (*stackp == (uintptr_t)stackp)) {
183186
stackp++;
184187
}
185188

cpu/esp_common/freertos/task.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ UBaseType_t uxTaskGetStackHighWaterMark(TaskHandle_t xTask)
197197
thread_t *thread = thread_get((xTask == NULL) ? (uint32_t)thread_getpid()
198198
: (uint32_t)xTask);
199199
assert(thread != NULL);
200-
return thread_measure_stack_free(thread->stack_start);
200+
return thread_measure_stack_free(thread);
201201
}
202202

203203
void *pvTaskGetThreadLocalStoragePointer(TaskHandle_t xTaskToQuery,

cpu/esp_common/thread_arch.c

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -92,10 +92,9 @@ void thread_isr_stack_init(void)
9292

9393
int thread_isr_stack_usage(void)
9494
{
95-
/* cppcheck-suppress comparePointers
96-
* (reason: comes from ESP-SDK, so should be valid) */
97-
return &port_IntStackTop - &port_IntStack -
98-
thread_measure_stack_free((char*)&port_IntStack);
95+
size_t stack_size = (uintptr_t)&port_IntStackTop - (uintptr_t)&port_IntStack;
96+
return stack_size -
97+
measure_stack_free_internal((char *)&port_IntStack, stack_size);
9998
}
10099

101100
void *thread_isr_stack_pointer(void)

sys/ps/ps.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ void ps(void)
9898
#ifdef DEVELHELP
9999
int stacksz = thread_get_stacksize(p); /* get stack size */
100100
overall_stacksz += stacksz;
101-
int stack_free = thread_measure_stack_free(thread_get_stackstart(p));
101+
int stack_free = thread_measure_stack_free(p);
102102
stacksz -= stack_free;
103103
overall_used += stacksz;
104104
#endif

sys/test_utils/print_stack_usage/print_stack_usage.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434

3535
void print_stack_usage_metric(const char *name, void *stack, unsigned max_size)
3636
{
37-
unsigned free = thread_measure_stack_free(stack);
37+
unsigned free = measure_stack_free_internal(stack, max_size);
3838

3939
if ((LOG_LEVEL >= LOG_INFO) &&
4040
(thread_get_stacksize(thread_get_active()) >= MIN_SIZE)) {

0 commit comments

Comments
 (0)