objspace: Hide identhash containing internal objs

Inside ObjectSpace.reachable_objects_from we keep an internal identhash
in order to de-duplicate reachable objects when wrapping them as
InternalObject. Previously this hash was not hidden, making it possible
to leak references to those internal objects to Ruby if using
ObjectSpace.each_object.

This commit solves this by hiding the hash. To simplify collection of
values, we instead now just use the hash as a set of visited objects,
and collect an Array (not hidden) of values to be returned.
This commit is contained in:
John Hawthorn 2022-02-09 12:14:51 -08:00
parent a271acf822
commit 05b1944c53
Notes: git 2022-02-10 10:33:06 +09:00
2 changed files with 31 additions and 15 deletions

View File

@ -712,7 +712,7 @@ iow_internal_object_id(VALUE self)
struct rof_data {
VALUE refs;
VALUE internals;
VALUE values;
};
static void
@ -723,11 +723,15 @@ reachable_object_from_i(VALUE obj, void *data_ptr)
VALUE val = obj;
if (rb_objspace_markable_object_p(obj)) {
if (NIL_P(rb_hash_lookup(data->refs, key))) {
rb_hash_aset(data->refs, key, Qtrue);
if (rb_objspace_internal_object_p(obj)) {
val = iow_newobj(obj);
rb_ary_push(data->internals, val);
}
rb_hash_aset(data->refs, key, val);
rb_ary_push(data->values, val);
}
}
}
@ -791,12 +795,12 @@ reachable_objects_from(VALUE self, VALUE obj)
obj = (VALUE)DATA_PTR(obj);
}
data.refs = rb_ident_hash_new();
data.internals = rb_ary_new();
data.refs = rb_obj_hide(rb_ident_hash_new());
data.values = rb_ary_new();
rb_objspace_reachable_objects_from(obj, reachable_object_from_i, &data);
return rb_funcall(data.refs, rb_intern("values"), 0);
return data.values;
}
else {
return Qnil;

View File

@ -140,6 +140,18 @@ class TestObjSpace < Test::Unit::TestCase
end;
end
def test_reachable_objects_during_iteration
opts = %w[--disable-gem --disable=frozen-string-literal -robjspace]
assert_separately opts, "#{<<-"begin;"}\n#{<<-'end;'}"
begin;
ObjectSpace.each_object{|o|
o.inspect
ObjectSpace.reachable_objects_from(Class)
}
end;
end
def test_reachable_objects_from_root
root_objects = ObjectSpace.reachable_objects_from_root