From 0610f1b083738bb0a84cede42fda048fa116404e Mon Sep 17 00:00:00 2001 From: Peter Zhu Date: Wed, 14 Aug 2024 11:31:17 -0400 Subject: [PATCH] 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 --- gc/default.c | 4 ++-- test/ruby/test_objectspace.rb | 14 ++++++++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/gc/default.c b/gc/default.c index 1ecffe9b1d..5c0c2b1386 100644 --- a/gc/default.c +++ b/gc/default.c @@ -3251,12 +3251,12 @@ rb_gc_impl_shutdown_call_finalizer(void *objspace_ptr) while (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_delete(finalizer_table, &obj, 0); 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; xfree(curr); } diff --git a/test/ruby/test_objectspace.rb b/test/ruby/test_objectspace.rb index 1c97bd517e..d3ce6e6ed5 100644 --- a/test/ruby/test_objectspace.rb +++ b/test/ruby/test_objectspace.rb @@ -101,6 +101,20 @@ End ObjectSpace.define_finalizer(a) { p :ok } !b 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) } code = proc do |priv|