Skip to content

Commit 5093626

Browse files
committed
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.
1 parent 8f0c958 commit 5093626

File tree

1 file changed

+43
-4
lines changed

1 file changed

+43
-4
lines changed

coro.c

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,9 @@ static struct {
222222
/* Stack size for each hart coroutine (1MB - increased for complex execution) */
223223
#define CORO_STACK_SIZE (1024 * 1024)
224224

225+
/* Stack canary value for overflow detection */
226+
#define STACK_CANARY_VALUE 0xDEADBEEFCAFEBABEULL
227+
225228
/* Sentinel value for current_hart when no coroutine is running */
226229
#define CORO_HART_ID_IDLE UINT32_MAX
227230

@@ -241,6 +244,26 @@ static inline void coro_clear_running_state(void)
241244
tls_running_coro = NULL;
242245
}
243246

247+
/* Get pointer to stack canary (placed at bottom of stack buffer) */
248+
static inline uint64_t *coro_get_canary_ptr(coro_t *co)
249+
{
250+
return (uint64_t *) co->stack_base;
251+
}
252+
253+
/* Check for stack overflow by verifying the canary value in stack buffer */
254+
static inline void coro_check_stack(coro_t *co)
255+
{
256+
uint64_t *canary_ptr = coro_get_canary_ptr(co);
257+
if (*canary_ptr != STACK_CANARY_VALUE) {
258+
fprintf(stderr,
259+
"FATAL: Stack overflow detected in coroutine! "
260+
"Expected canary=0x%llx, got=0x%llx at %p\n",
261+
(unsigned long long) STACK_CANARY_VALUE,
262+
(unsigned long long) *canary_ptr, (void *) canary_ptr);
263+
abort();
264+
}
265+
}
266+
244267
/* Forward declarations */
245268

246269
#ifdef CORO_USE_ASM
@@ -483,12 +506,22 @@ bool coro_create_hart(uint32_t hart_id, void (*func)(void *), void *hart)
483506
return false;
484507
}
485508

486-
/* Initialize context */
509+
/* Place canary at bottom of stack buffer (first 8 bytes)
510+
* Stack grows downward from top, so overflow will hit canary first */
511+
uint64_t *canary_ptr = coro_get_canary_ptr(co);
512+
*canary_ptr = STACK_CANARY_VALUE;
513+
514+
/* Adjust usable stack to skip canary area
515+
* Stack starts after the canary (bottom + sizeof(uint64_t)) */
516+
void *usable_stack_base = (uint8_t *) co->stack_base + sizeof(uint64_t);
517+
size_t usable_stack_size = co->stack_size - sizeof(uint64_t);
518+
519+
/* Initialize context with adjusted stack bounds */
487520
#ifdef CORO_USE_ASM
488-
make_context(co, &co->context->ctx, co->stack_base, co->stack_size);
521+
make_context(co, &co->context->ctx, usable_stack_base, usable_stack_size);
489522
#else
490-
if (make_context(co, &co->context->ctx, co->stack_base, co->stack_size) !=
491-
0) {
523+
if (make_context(co, &co->context->ctx, usable_stack_base,
524+
usable_stack_size) != 0) {
492525
free(co->stack_base);
493526
free(co->context);
494527
free(co);
@@ -520,9 +553,15 @@ void coro_resume_hart(uint32_t hart_id)
520553
return;
521554
}
522555

556+
/* Check for stack overflow before resuming */
557+
coro_check_stack(co);
558+
523559
coro_state.current_hart = hart_id;
524560
co->state = CORO_STATE_RUNNING;
525561
jump_into(co);
562+
563+
/* Check for stack overflow after returning from coroutine */
564+
coro_check_stack(co);
526565
}
527566

528567
void coro_yield(void)

0 commit comments

Comments
 (0)