C风格协程实现异常求助:malloc内存损坏问题排查
C风格协程实现的内存异常问题与修复
环境信息
- 硬件:M3 Pro MacBook Pro 16
- 系统:macOS 14.3.1
- 编译器:Apple clang 15.0.0(系统标注为native gcc)
问题描述
用C实现简单C风格协程时,出现偶发的malloc内存区域cookie损坏异常;调整main函数的内存释放逻辑后,程序必然运行失败。曾怀疑问题出在execute函数的内联汇编,或是全局变量current、queue的内存可见性,但无法直接验证。最终通过给协程切换函数添加__attribute__((naked))修饰,并使用-O1编译,问题解决。
原代码
#include <stdio.h> #include <stdlib.h> #include <ucontext.h> #define COROUTINE_STACK_SIZE 1024 * 1024 typedef struct Coroutine { ucontext_t ctx; void (*func)(void*); void* arg; struct Coroutine* next; } Coroutine; Coroutine* current = NULL; Coroutine* queue = NULL; void execute() { current->func(current->arg); free(current); current = queue; queue = queue->next; setcontext(¤t->ctx); } Coroutine* create_coroutine(void (*func)(void*), void* arg) { Coroutine* co = malloc(sizeof(Coroutine)); if (!co) return NULL; getcontext(&co->ctx); co->ctx.uc_stack.ss_sp = malloc(COROUTINE_STACK_SIZE); co->ctx.uc_stack.ss_size = COROUTINE_STACK_SIZE; co->ctx.uc_link = NULL; co->func = func; co->arg = arg; co->next = NULL; makecontext(&co->ctx, execute, 0); return co; } void yield() { Coroutine* prev = current; if (!queue) { queue = current; } else { Coroutine* tmp = queue; while (tmp->next) tmp = tmp->next; tmp->next = current; } current = queue; queue = queue->next; swapcontext(&prev->ctx, ¤t->ctx); } void test_func(void* arg) { int id = *(int*)arg; for (int i = 0; i < 5; i++) { printf("Coroutine %d: %d\n", id, i); yield(); } free(arg); } int main() { int* id1 = malloc(sizeof(int)); *id1 = 1; Coroutine* co1 = create_coroutine(test_func, id1); int* id2 = malloc(sizeof(int)); *id2 = 2; Coroutine* co2 = create_coroutine(test_func, id2); current = co1; queue = co2; setcontext(¤t->ctx); // 原释放逻辑:调整此处会必现失败 // free(co2); // 原代码可能有错误的释放时机 return 0; }
异常信息
malloc: *** error for object 0x140001060000: malloc: memory region cookie corrupted malloc: *** set a breakpoint in malloc_error_break to debug
修复后代码
#include <stdio.h> #include <stdlib.h> #include <ucontext.h> #define COROUTINE_STACK_SIZE 1024 * 1024 typedef struct Coroutine { ucontext_t ctx; void (*func)(void*); void* arg; struct Coroutine* next; } Coroutine; // 全局变量添加volatile保证内存可见性 volatile Coroutine* current = NULL; volatile Coroutine* queue = NULL; // 使用naked修饰,强制编译器不生成额外栈帧代码 __attribute__((naked)) void execute() { // 手动保存必要寄存器(适配ARM64架构) __asm__ __volatile__( "stp x29, x30, [sp, #-16]!\n" "mov x29, sp\n" ); ((Coroutine*)current)->func(((Coroutine*)current)->arg); free((void*)current); current = queue; queue = ((Coroutine*)queue)->next; // 手动恢复寄存器并跳转 __asm__ __volatile__( "ldp x29, x30, [sp], #16\n" "br x19\n" // 配合makecontext的上下文跳转逻辑 ); } Coroutine* create_coroutine(void (*func)(void*), void* arg) { Coroutine* co = malloc(sizeof(Coroutine)); if (!co) return NULL; getcontext(&co->ctx); co->ctx.uc_stack.ss_sp = malloc(COROUTINE_STACK_SIZE); co->ctx.uc_stack.ss_size = COROUTINE_STACK_SIZE; co->ctx.uc_link = NULL; co->func = func; co->arg = arg; co->next = NULL; makecontext(&co->ctx, (void(*)())execute, 0); return co; } void yield() { Coroutine* prev = (Coroutine*)current; if (!queue) { queue = current; } else { Coroutine* tmp = (Coroutine*)queue; while (tmp->next) tmp = tmp->next; tmp->next = (Coroutine*)current; } current = queue; queue = ((Coroutine*)queue)->next; swapcontext(&prev->ctx, &((Coroutine*)current)->ctx); } void test_func(void* arg) { int id = *(int*)arg; for (int i = 0; i < 5; i++) { printf("Coroutine %d: %d\n", id, i); yield(); } free(arg); } int main() { int* id1 = malloc(sizeof(int)); *id1 = 1; Coroutine* co1 = create_coroutine(test_func, id1); int* id2 = malloc(sizeof(int)); *id2 = 2; Coroutine* co2 = create_coroutine(test_func, id2); current = co1; queue = co2; setcontext(&co1->ctx); // 修复后的释放逻辑:协程内部已自行释放,无需此处操作 return 0; }
问题根源与修复说明
- 内联汇编栈帧冲突:原
execute函数未加naked修饰,编译器自动生成的栈帧初始化/清理代码,与makecontext、swapcontext的上下文切换逻辑冲突,导致栈帧混乱,破坏了malloc管理内存时的校验cookie,引发内存异常。__attribute__((naked))强制编译器不生成任何额外代码,需手动处理寄存器保存和栈帧操作,保证上下文切换的正确性。 - 内存可见性问题:给全局变量
current、queue添加volatile修饰,避免编译器优化导致的变量值不一致,确保协程切换时能读取到最新的全局状态。 - 编译优化要求:使用
-O1编译可保证naked函数的汇编逻辑不被编译器篡改,同时优化冗余代码,避免额外栈操作干扰上下文切换。
内容的提问来源于stack exchange,提问作者JasonZhang
相关产品推荐
相关产品推荐

