Skip to content

Commit

Permalink
FREE_AT_EXIT: Don't free main stack post-fork
Browse files Browse the repository at this point in the history
When a forked process was started in a thread, this would result in a
double-free during the child process exit.

    RUBY_FREE_AT_EXIT=1 ./miniruby -e 'Thread.new { fork { } }.join; Process.waitpid'

This is because the main thread in the forked process was not the
initial VM thread, and the new thread's stack was freed as part of
objectspace iteration.

This change also allows rb_threadptr_root_fiber_release to run without
EC being available.
  • Loading branch information
jhawthorn committed Dec 23, 2023
1 parent 339978e commit f1b7424
Show file tree
Hide file tree
Showing 2 changed files with 4 additions and 3 deletions.
4 changes: 2 additions & 2 deletions cont.c
Expand Up @@ -2587,12 +2587,12 @@ rb_threadptr_root_fiber_release(rb_thread_t *th)
/* ignore. A root fiber object will free th->ec */
}
else {
rb_execution_context_t *ec = GET_EC();
rb_execution_context_t *ec = rb_current_execution_context(false);

VM_ASSERT(th->ec->fiber_ptr->cont.type == FIBER_CONTEXT);
VM_ASSERT(th->ec->fiber_ptr->cont.self == 0);

if (th->ec == ec) {
if (ec && th->ec == ec) {
rb_ractor_set_current_ec(th->ractor, NULL);
}
fiber_free(th->ec->fiber_ptr);
Expand Down
3 changes: 2 additions & 1 deletion vm.c
Expand Up @@ -3056,7 +3056,8 @@ ruby_vm_destruct(rb_vm_t *vm)
rb_objspace_free_objects(objspace);
rb_free_generic_iv_tbl_();
rb_free_default_rand_key();
if (th) {
if (th && vm->fork_gen == 0) {
/* If we have forked, main_thread may not be the initial thread */
xfree(stack);
ruby_mimfree(th);
}
Expand Down

0 comments on commit f1b7424

Please sign in to comment.