From 5093626a59cf9a8652f0510cd89856b4a4424546 Mon Sep 17 00:00:00 2001 From: Jim Huang Date: Thu, 30 Oct 2025 17:07:21 +0800 Subject: [PATCH] Add stack overflow detection for coroutines Implement canary-based stack overflow detection for hart coroutines to catch stack corruption before it leads to undefined behavior. The canary is placed at the bottom of the stack buffer itself (not in the coro_t structure), so actual stack overflow will corrupt it and trigger detection. The implementation provides immediate detection of stack corruption with minimal performance cost. Validated with Linux boot test and unit tests confirming canary is in the actual stack buffer. --- coro.c | 47 +++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 43 insertions(+), 4 deletions(-) diff --git a/coro.c b/coro.c index db9781a..3eada98 100644 --- a/coro.c +++ b/coro.c @@ -222,6 +222,9 @@ static struct { /* Stack size for each hart coroutine (1MB - increased for complex execution) */ #define CORO_STACK_SIZE (1024 * 1024) +/* Stack canary value for overflow detection */ +#define STACK_CANARY_VALUE 0xDEADBEEFCAFEBABEULL + /* Sentinel value for current_hart when no coroutine is running */ #define CORO_HART_ID_IDLE UINT32_MAX @@ -241,6 +244,26 @@ static inline void coro_clear_running_state(void) tls_running_coro = NULL; } +/* Get pointer to stack canary (placed at bottom of stack buffer) */ +static inline uint64_t *coro_get_canary_ptr(coro_t *co) +{ + return (uint64_t *) co->stack_base; +} + +/* Check for stack overflow by verifying the canary value in stack buffer */ +static inline void coro_check_stack(coro_t *co) +{ + uint64_t *canary_ptr = coro_get_canary_ptr(co); + if (*canary_ptr != STACK_CANARY_VALUE) { + fprintf(stderr, + "FATAL: Stack overflow detected in coroutine! " + "Expected canary=0x%llx, got=0x%llx at %p\n", + (unsigned long long) STACK_CANARY_VALUE, + (unsigned long long) *canary_ptr, (void *) canary_ptr); + abort(); + } +} + /* Forward declarations */ #ifdef CORO_USE_ASM @@ -483,12 +506,22 @@ bool coro_create_hart(uint32_t hart_id, void (*func)(void *), void *hart) return false; } - /* Initialize context */ + /* Place canary at bottom of stack buffer (first 8 bytes) + * Stack grows downward from top, so overflow will hit canary first */ + uint64_t *canary_ptr = coro_get_canary_ptr(co); + *canary_ptr = STACK_CANARY_VALUE; + + /* Adjust usable stack to skip canary area + * Stack starts after the canary (bottom + sizeof(uint64_t)) */ + void *usable_stack_base = (uint8_t *) co->stack_base + sizeof(uint64_t); + size_t usable_stack_size = co->stack_size - sizeof(uint64_t); + + /* Initialize context with adjusted stack bounds */ #ifdef CORO_USE_ASM - make_context(co, &co->context->ctx, co->stack_base, co->stack_size); + make_context(co, &co->context->ctx, usable_stack_base, usable_stack_size); #else - if (make_context(co, &co->context->ctx, co->stack_base, co->stack_size) != - 0) { + if (make_context(co, &co->context->ctx, usable_stack_base, + usable_stack_size) != 0) { free(co->stack_base); free(co->context); free(co); @@ -520,9 +553,15 @@ void coro_resume_hart(uint32_t hart_id) return; } + /* Check for stack overflow before resuming */ + coro_check_stack(co); + coro_state.current_hart = hart_id; co->state = CORO_STATE_RUNNING; jump_into(co); + + /* Check for stack overflow after returning from coroutine */ + coro_check_stack(co); } void coro_yield(void)