Skip to content

Commit 81d8ed2

Browse files
committed
Fix use-after-free in process scheduling lists
When `do_spawn` fails after `context_new` has been called, `context_destroy` is invoked directly. `context_new` calls `globalcontext_init_process` which adds the context to both `processes_table` and `waiting_processes`, but `context_destroy` only removed from `processes_table`. The freed context's node remained linked in `waiting_processes`, causing a use-after-free corruption of the scheduling list. Fix by adding a spinlock-protected `list_remove` + `list_init` of `processes_list_head` in `context_destroy`. The `list_init` makes the node self-referential so that a second `list_remove` (from callers like `scheduler_terminate` that already do the removal before calling `context_destroy`) is a safe no-op. Also remove the now-redundant explicit removal in the native handler kill path of `scheduler_run`, since `context_destroy` handles it. Signed-off-by: Peter M <petermm@gmail.com>
1 parent fde1fb3 commit 81d8ed2

File tree

2 files changed

+8
-3
lines changed

2 files changed

+8
-3
lines changed

src/libAtomVM/context.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,13 @@ void context_destroy(Context *ctx)
140140
struct ListHead *processes_table_list = synclist_wrlock(&ctx->global->processes_table);
141141
UNUSED(processes_table_list);
142142

143+
// Remove from scheduling lists (waiting/ready/running).
144+
// list_remove on a self-referential (already removed + re-initialized) node is a safe no-op.
145+
SMP_SPINLOCK_LOCK(&ctx->global->processes_spinlock);
146+
list_remove(&ctx->processes_list_head);
147+
list_init(&ctx->processes_list_head);
148+
SMP_SPINLOCK_UNLOCK(&ctx->global->processes_spinlock);
149+
143150
list_remove(&ctx->processes_table_head);
144151

145152
// Ensure process is not registered

src/libAtomVM/scheduler.c

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -287,9 +287,6 @@ Context *scheduler_run(GlobalContext *global)
287287
// process signal messages and also empty outer list to inner list.
288288
scheduler_process_native_signal_messages(result);
289289
if (UNLIKELY(result->flags & Killed)) {
290-
SMP_SPINLOCK_LOCK(&global->processes_spinlock);
291-
list_remove(&result->processes_list_head);
292-
SMP_SPINLOCK_UNLOCK(&global->processes_spinlock);
293290
context_destroy(result);
294291
} else {
295292
if (mailbox_has_next(&result->mailbox)) {
@@ -429,6 +426,7 @@ void scheduler_terminate(Context *ctx)
429426
SMP_SPINLOCK_LOCK(&ctx->global->processes_spinlock);
430427
context_update_flags(ctx, ~NoFlags, Killed);
431428
list_remove(&ctx->processes_list_head);
429+
list_init(&ctx->processes_list_head);
432430
SMP_SPINLOCK_UNLOCK(&ctx->global->processes_spinlock);
433431
if (!ctx->leader) {
434432
context_destroy(ctx);

0 commit comments

Comments
 (0)