From f8883178c58c38b2d2909f9e4d27bd00e1e7e6f0 Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Thu, 16 Oct 2025 10:45:10 +0900 Subject: [PATCH] mruby-task: prevent segfault when sleep is called from C function; fix #6642 when sleep() was called from within a C function (such as module.new's block evaluation), the task scheduler would segfault while attempting to resume the task. this occurred because C functions don't execute bytecode and thus their callinfo has no valid program counter (pc). when the task tried to resume execution, mrb_vm_exec() received a null pc, causing a segmentation fault. the fix adds two safeguards in task.c: 1. C function boundary detection: before suspending a task for sleep, check if we're inside a C function by examining the cci (c call info) field. if cci > 0, fall back to blocking sleep via HAL instead of attempting cooperative context switch. this preserves sleep functionality without raising exceptions, though it blocks other tasks during the sleep period. 2. proc fallback in execute_task(): use the task's stored proc if the current callinfo's proc is null, ensuring mrb_vm_exec() always receives a valid proc pointer. this approach prioritizes functionality over strict cooperative multitasking semantics - tasks can still sleep inside C functions, but the sleep becomes blocking. the alternative would be raising an exception like fiber does, but that would break existing code unexpectedly. Co-authored-by: Claude --- mrbgems/mruby-task/src/task.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/mrbgems/mruby-task/src/task.c b/mrbgems/mruby-task/src/task.c index d78f35705..bfc789394 100644 --- a/mrbgems/mruby-task/src/task.c +++ b/mrbgems/mruby-task/src/task.c @@ -336,7 +336,8 @@ execute_task(mrb_state *mrb, mrb_task *t) t->c.status = MRB_FIBER_RUNNING; /* Save proc and PC to locals before calling mrb_vm_exec */ - const struct RProc *proc = t->c.ci->proc; + /* Use current callinfo's proc if available, otherwise use task's stored proc */ + const struct RProc *proc = t->c.ci->proc ? t->c.ci->proc : mrb_proc_ptr(t->proc); const mrb_code *pc = t->c.ci->pc; /* Set vmexec flag to prevent fiber_terminate from being called */ @@ -491,6 +492,17 @@ sleep_us_impl(mrb_state *mrb, mrb_int usec) return; } + /* Check for C function boundary - cannot do cooperative context switch */ + mrb_callinfo *ci; + for (ci = mrb->c->ci; ci >= mrb->c->cibase; ci--) { + if (ci->cci > 0) { + /* Inside C function - fall back to blocking sleep without context switch */ + mrb_hal_task_sleep_us(mrb, usec); + switching_ = FALSE; + return; + } + } + /* In task context - get current running task */ t = MRB2TASK(mrb);