summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Zhu <[email protected]>2024-12-16 11:41:41 -0500
committerPeter Zhu <[email protected]>2024-12-16 12:24:24 -0500
commit516a6cd1ad620b880651c1333bd856a9d7dec3c4 (patch)
treec27c7b4f538b0a751f47498ad380c16c2eaec1f7
parent960f971ac86e0d171fbc3df12bda8c6c81d3ae29 (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.c4
-rw-r--r--gc.c6
-rw-r--r--internal/gc.h1
-rw-r--r--test/objspace/test_objspace.rb23
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();
{
diff --git a/gc.c b/gc.c
index f4322417b6..c5277ee2c8 100644
--- a/gc.c
+++ b/gc.c
@@ -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 }