diff options
author | Peter Zhu <[email protected]> | 2024-12-16 11:41:41 -0500 |
---|---|---|
committer | Peter Zhu <[email protected]> | 2024-12-16 12:24:24 -0500 |
commit | 516a6cd1ad620b880651c1333bd856a9d7dec3c4 (patch) | |
tree | c27c7b4f538b0a751f47498ad380c16c2eaec1f7 | |
parent | 960f971ac86e0d171fbc3df12bda8c6c81d3ae29 (diff) |
Check whether object is valid in allocation_info_tracer_compact
When reference updating ObjectSpace.trace_object_allocations, we need to
check whether the object is valid or not because it does not mark the
object so the object may be dead. This can cause a segmentation fault
if the object is on a free heap page.
For example, the following script crashes:
require "objspace"
objs = []
ObjectSpace.trace_object_allocations do
1_000_000.times do
objs << Object.new
end
end
objs = nil
# Free pages that the objs were on
GC.start
# Run compaction and check that it doesn't crash
GC.compact
Notes
Notes:
Merged: https://github.com/ruby/ruby/pull/12360
-rw-r--r-- | ext/objspace/object_tracing.c | 4 | ||||
-rw-r--r-- | gc.c | 6 | ||||
-rw-r--r-- | internal/gc.h | 1 | ||||
-rw-r--r-- | test/objspace/test_objspace.rb | 23 |
4 files changed, 34 insertions, 0 deletions
diff --git a/ext/objspace/object_tracing.c b/ext/objspace/object_tracing.c index 8ea35ef7b7..a52dff736e 100644 --- a/ext/objspace/object_tracing.c +++ b/ext/objspace/object_tracing.c @@ -194,6 +194,10 @@ allocation_info_tracer_compact_update_object_table_i(st_data_t key, st_data_t va { st_table *table = (st_table *)data; + if (!rb_gc_pointer_to_heap_p(key)) { + return ST_DELETE; + } + if (key != rb_gc_location(key)) { DURING_GC_COULD_MALLOC_REGION_START(); { @@ -1724,6 +1724,12 @@ rb_objspace_garbage_object_p(VALUE obj) return rb_gc_impl_garbage_object_p(rb_gc_get_objspace(), obj); } +bool +rb_gc_pointer_to_heap_p(VALUE obj) +{ + return rb_gc_impl_pointer_to_heap_p(rb_gc_get_objspace(), (void *)obj); +} + /* * call-seq: * ObjectSpace._id2ref(object_id) -> an_object diff --git a/internal/gc.h b/internal/gc.h index 702a682ee3..6c6ae4008f 100644 --- a/internal/gc.h +++ b/internal/gc.h @@ -227,6 +227,7 @@ void rb_objspace_reachable_objects_from(VALUE obj, void (func)(VALUE, void *), v void rb_objspace_reachable_objects_from_root(void (func)(const char *category, VALUE, void *), void *data); int rb_objspace_internal_object_p(VALUE obj); int rb_objspace_garbage_object_p(VALUE obj); +bool rb_gc_pointer_to_heap_p(VALUE obj); void rb_objspace_each_objects( int (*callback)(void *start, void *end, size_t stride, void *data), diff --git a/test/objspace/test_objspace.rb b/test/objspace/test_objspace.rb index 394b2e49ad..e0dde3621c 100644 --- a/test/objspace/test_objspace.rb +++ b/test/objspace/test_objspace.rb @@ -305,6 +305,29 @@ class TestObjSpace < Test::Unit::TestCase RUBY end + def test_trace_object_allocations_compaction_freed_pages + omit "compaction is not supported on this platform" unless GC.respond_to?(:compact) + + assert_normal_exit(<<~RUBY) + require "objspace" + + objs = [] + ObjectSpace.trace_object_allocations do + 1_000_000.times do + objs << Object.new + end + end + + objs = nil + + # Free pages that the objs were on + GC.start + + # Run compaction and check that it doesn't crash + GC.compact + RUBY + end + def test_dump_flags # Ensure that the fstring is promoted to old generation 4.times { GC.start } |