Fix crash when GC runs during finalizers at shutdown

We need to remove from the finalizer_table after running all the
finalizers because GC could trigger during the finalizer which could
reclaim the finalizer table array.

The following code crashes:

    1_000_000.times do
      o = Object.new
      ObjectSpace.define_finalizer(o, proc { })
    end
This commit is contained in:
Peter Zhu 2024-08-14 11:31:17 -04:00
parent 85f99b3828
commit 0610f1b083
Notes: git 2024-08-14 17:50:13 +00:00
2 changed files with 16 additions and 2 deletions

View file

@ -3251,12 +3251,12 @@ rb_gc_impl_shutdown_call_finalizer(void *objspace_ptr)
while (list) { while (list) {
struct force_finalize_list *curr = list; struct force_finalize_list *curr = list;
rb_gc_run_obj_finalizer(rb_gc_impl_object_id(objspace, curr->obj), RARRAY_LEN(curr->table), get_final, (void *)curr->table);
st_data_t obj = (st_data_t)curr->obj; st_data_t obj = (st_data_t)curr->obj;
st_delete(finalizer_table, &obj, 0); st_delete(finalizer_table, &obj, 0);
FL_UNSET(curr->obj, FL_FINALIZE); FL_UNSET(curr->obj, FL_FINALIZE);
rb_gc_run_obj_finalizer(rb_gc_impl_object_id(objspace, curr->obj), RARRAY_LEN(curr->table), get_final, (void *)curr->table);
list = curr->next; list = curr->next;
xfree(curr); xfree(curr);
} }

View file

@ -101,6 +101,20 @@ End
ObjectSpace.define_finalizer(a) { p :ok } ObjectSpace.define_finalizer(a) { p :ok }
!b !b
END END
assert_in_out_err(["-e", <<~RUBY], "", %w(:ok :ok), [])
a = Object.new
ObjectSpace.define_finalizer(a) { p :ok }
1_000_000.times do
o = Object.new
ObjectSpace.define_finalizer(o) { }
end
b = Object.new
ObjectSpace.define_finalizer(b) { p :ok }
RUBY
assert_raise(ArgumentError) { ObjectSpace.define_finalizer([], Object.new) } assert_raise(ArgumentError) { ObjectSpace.define_finalizer([], Object.new) }
code = proc do |priv| code = proc do |priv|