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
This commit is contained in:
Peter Zhu 2024-12-16 11:41:41 -05:00
parent 960f971ac8
commit 516a6cd1ad
Notes: git 2024-12-16 17:24:43 +00:00
4 changed files with 34 additions and 0 deletions

View File

@ -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();
{

6
gc.c
View File

@ -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

View File

@ -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),

View File

@ -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 }