Skip to content

Commit 516a6cd

Browse files
committed
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
1 parent 960f971 commit 516a6cd

File tree

4 files changed

+34
-0
lines changed

4 files changed

+34
-0
lines changed

ext/objspace/object_tracing.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,10 @@ allocation_info_tracer_compact_update_object_table_i(st_data_t key, st_data_t va
194194
{
195195
st_table *table = (st_table *)data;
196196

197+
if (!rb_gc_pointer_to_heap_p(key)) {
198+
return ST_DELETE;
199+
}
200+
197201
if (key != rb_gc_location(key)) {
198202
DURING_GC_COULD_MALLOC_REGION_START();
199203
{

gc.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1724,6 +1724,12 @@ rb_objspace_garbage_object_p(VALUE obj)
17241724
return rb_gc_impl_garbage_object_p(rb_gc_get_objspace(), obj);
17251725
}
17261726

1727+
bool
1728+
rb_gc_pointer_to_heap_p(VALUE obj)
1729+
{
1730+
return rb_gc_impl_pointer_to_heap_p(rb_gc_get_objspace(), (void *)obj);
1731+
}
1732+
17271733
/*
17281734
* call-seq:
17291735
* ObjectSpace._id2ref(object_id) -> an_object

internal/gc.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,7 @@ void rb_objspace_reachable_objects_from(VALUE obj, void (func)(VALUE, void *), v
227227
void rb_objspace_reachable_objects_from_root(void (func)(const char *category, VALUE, void *), void *data);
228228
int rb_objspace_internal_object_p(VALUE obj);
229229
int rb_objspace_garbage_object_p(VALUE obj);
230+
bool rb_gc_pointer_to_heap_p(VALUE obj);
230231

231232
void rb_objspace_each_objects(
232233
int (*callback)(void *start, void *end, size_t stride, void *data),

test/objspace/test_objspace.rb

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,29 @@ def test_trace_object_allocations_compaction
305305
RUBY
306306
end
307307

308+
def test_trace_object_allocations_compaction_freed_pages
309+
omit "compaction is not supported on this platform" unless GC.respond_to?(:compact)
310+
311+
assert_normal_exit(<<~RUBY)
312+
require "objspace"
313+
314+
objs = []
315+
ObjectSpace.trace_object_allocations do
316+
1_000_000.times do
317+
objs << Object.new
318+
end
319+
end
320+
321+
objs = nil
322+
323+
# Free pages that the objs were on
324+
GC.start
325+
326+
# Run compaction and check that it doesn't crash
327+
GC.compact
328+
RUBY
329+
end
330+
308331
def test_dump_flags
309332
# Ensure that the fstring is promoted to old generation
310333
4.times { GC.start }

0 commit comments

Comments
 (0)