Skip to content

scheduler: block task wakeups during spawn#2279

Open
petermm wants to merge 1 commit intoatomvm:release-0.7from
petermm:fix-scheduler-spawning-guard
Open

scheduler: block task wakeups during spawn#2279
petermm wants to merge 1 commit intoatomvm:release-0.7from
petermm:fix-scheduler-spawning-guard

Conversation

@petermm
Copy link
Copy Markdown
Contributor

@petermm petermm commented Apr 21, 2026

While reviewing #2277 - LLM searched for similar bugs, and found:

It seems somewhat hypothetical, but maybe @pguyot can weigh in.

scheduler_make_ready_from_task checks for the Killed flag but does not check for the Spawning flag, unlike scheduler_make_ready which checks for both.

Relevant files:

  • src/libAtomVM/scheduler.c#L350-L407
350: static void scheduler_make_ready(Context *ctx)
351: {
352:     GlobalContext *global = ctx->global;
353:     SMP_SPINLOCK_LOCK(&global->processes_spinlock);
354:     if (context_get_flags(ctx, Killed | Spawning)) {
355:         SMP_SPINLOCK_UNLOCK(&global->processes_spinlock);
356:         return;
357:     }
...
390: }
391: 
392: #ifdef AVM_TASK_DRIVER_ENABLED
393: static void scheduler_make_ready_from_task(Context *ctx)
394: {
395:     GlobalContext *global = ctx->global;
396:     if (context_get_flags(ctx, Killed)) {
397:         return;
398:     }
399:     list_remove(&ctx->processes_list_head);
400:     // Move to ready queue (from waiting or running)
401:     // The process may be running (it would be signaled), so mark it
402:     // as ready
403:     context_update_flags(ctx, ~NoFlags, Ready);
404:     list_append(&global->ready_processes, &ctx->processes_list_head);
405:     sys_signal(global);
406: }
407: #endif

The oracle confirms Finding 1 is a real bug, not just cosmetic. Key conclusions:

  1. Missing Spawning is exploitable — a remote peer can learn a pid (via dist_spawn_reply) before scheduler_init_ready runs, so a task-driven message can hit the context while still Spawning.

  2. No spinlock needed inside the function — the caller already holds it; scheduler.h documents this contract. Adding it would deadlock.

  3. No TOCTOU concern — the flag check runs inside the same critical section that mutates the list.

  4. Correct minimal fix — just add Spawning to the guard:

 static void scheduler_make_ready_from_task(Context *ctx)
 {
     GlobalContext *global = ctx->global;
-    if (context_get_flags(ctx, Killed)) {
+    if (context_get_flags(ctx, Killed | Spawning)) {
         return;
     }

Match scheduler_make_ready_from_task with scheduler_make_ready by checking the Spawning flag as well as Killed before moving a process onto the ready queue.

Without this guard, a task-driven message can wake a newly spawned process after its pid is visible but before scheduler_init_ready clears Spawning, which can schedule a not-yet-initialized context.

Keep the locking model unchanged: the task-side path still relies on the caller already holding processes_spinlock.

These changes are made under both the "Apache 2.0" and the "GNU Lesser General
Public License 2.1 or later" license terms (dual license).

SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later

Match scheduler_make_ready_from_task with scheduler_make_ready by checking the Spawning flag as well as Killed before moving a process onto the ready queue.

Without this guard, a task-driven message can wake a newly spawned process after its pid is visible but before scheduler_init_ready clears Spawning, which can schedule a not-yet-initialized context.

Keep the locking model unchanged: the task-side path still relies on the caller already holding processes_spinlock.

Signed-off-by: Peter M <petermm@gmail.com>
@pguyot
Copy link
Copy Markdown
Collaborator

pguyot commented Apr 21, 2026

I don't think it could happen. This path is only used by task drivers, I can't see how dist_spawn_reply can eventually call scheduler_make_ready_from_task. It would call scheduler_make_ready. But for symmetry we can nevertheless merge this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants