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:
parent
a271acf822
commit
05b1944c53
Notes:
git
2022-02-10 10:33:06 +09:00
@ -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 (rb_objspace_internal_object_p(obj)) {
|
||||
val = iow_newobj(obj);
|
||||
rb_ary_push(data->internals, val);
|
||||
}
|
||||
rb_hash_aset(data->refs, key, val);
|
||||
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->values, val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -785,21 +789,21 @@ static VALUE
|
||||
reachable_objects_from(VALUE self, VALUE obj)
|
||||
{
|
||||
if (rb_objspace_markable_object_p(obj)) {
|
||||
struct rof_data data;
|
||||
struct rof_data data;
|
||||
|
||||
if (rb_typeddata_is_kind_of(obj, &iow_data_type)) {
|
||||
obj = (VALUE)DATA_PTR(obj);
|
||||
}
|
||||
if (rb_typeddata_is_kind_of(obj, &iow_data_type)) {
|
||||
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);
|
||||
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;
|
||||
return Qnil;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user