mirror of
https://github.com/ruby/ruby.git
synced 2026-10-28 08:25:20 +08:00
Prohibit copying a class/module created by another Ractor
Module#initialize_copy of a class/module that another Ractor owns used to check that its constants and fields are all shareable, and copy it if so. That check made the copying Ractor walk (and flag) the owner's objects, and the fields were collected into a buffer and checked one by one, so a later value sat only in that buffer, which the owner's GC cannot see. Raise Ractor::IsolationError instead, whatever the class holds. The metaclass is checked as well, since its tables are copied too. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
ac823ef8b8
commit
a4131efb5a
2 files changed
+19
-107
Notes:
git
2026-10-23 19:14:25 +00:00
No files matched your search
@@ -1640,63 +1640,25 @@ assert_equal "can not access non-shareable objects in constant Object::STR of a
|
||||
end
|
||||
RUBY
|
||||
|
||||
# Copying a class/module created by another Ractor raises if its constants or
|
||||
# fields refer to unshareable objects
|
||||
assert_equal 'can not copy a class/module created by another Ractor because constant CONST refers to an unshareable object', <<~'RUBY', frozen_string_literal: false
|
||||
class C
|
||||
CONST = 'str'
|
||||
end
|
||||
|
||||
r = Ractor.new { C.dup }
|
||||
|
||||
begin
|
||||
r.join
|
||||
rescue Ractor::RemoteError => e
|
||||
e.cause.message
|
||||
end
|
||||
RUBY
|
||||
|
||||
assert_equal 'can not copy a class/module created by another Ractor because variable @iv refers to an unshareable object', <<~'RUBY', frozen_string_literal: false
|
||||
class C
|
||||
@iv = 'str'
|
||||
end
|
||||
|
||||
r = Ractor.new { C.dup }
|
||||
|
||||
begin
|
||||
r.join
|
||||
rescue Ractor::RemoteError => e
|
||||
e.cause.message
|
||||
end
|
||||
RUBY
|
||||
|
||||
# ... the metaclass is checked too, because the copy carries it over
|
||||
assert_equal 'can not copy a class/module created by another Ractor because variable @secret refers to an unshareable object', <<~'RUBY', frozen_string_literal: false
|
||||
class C; end
|
||||
C.singleton_class.instance_variable_set(:@secret, 'str')
|
||||
|
||||
r = Ractor.new { C.dup }
|
||||
|
||||
begin
|
||||
r.join
|
||||
rescue Ractor::RemoteError => e
|
||||
e.cause.message
|
||||
end
|
||||
RUBY
|
||||
|
||||
# ... and the copy of a class which refers to nothing unshareable belongs to the
|
||||
# copying Ractor, which is a way to modify a foreign class without mutating it
|
||||
assert_equal 'copy orig', %q{
|
||||
# Copying a class/module created by another Ractor is not allowed: the copy would
|
||||
# carry the owner's field values
|
||||
assert_equal 'can not copy a class/module created by another Ractor', %q{
|
||||
class C
|
||||
CONST = 1
|
||||
def m = 'orig'
|
||||
end
|
||||
|
||||
Ractor.new do
|
||||
k = C.dup
|
||||
k.class_eval { def m = 'copy' }
|
||||
"#{k.new.m} #{C.new.m}"
|
||||
end.value
|
||||
r = Ractor.new { C.dup }
|
||||
|
||||
begin
|
||||
r.join
|
||||
rescue Ractor::RemoteError => e
|
||||
e.cause.message
|
||||
end
|
||||
}
|
||||
|
||||
assert_equal 'Ractor::IsolationError', %q{
|
||||
module M; end
|
||||
Ractor.new { M.clone rescue $!.class.to_s }.value
|
||||
}
|
||||
|
||||
# Setting constants of classes created by other Ractors is not allowed
|
||||
|
||||
@@ -1004,30 +1004,6 @@ rb_module_check_initializable(VALUE mod)
|
||||
}
|
||||
}
|
||||
|
||||
static enum rb_id_table_iterator_result
|
||||
init_copy_check_const_i(ID id, VALUE v, void *data)
|
||||
{
|
||||
const rb_const_entry_t *ce = (const rb_const_entry_t *)v;
|
||||
if (!UNDEF_P(ce->value) && !RB_SPECIAL_CONST_P(ce->value) && !RB_OBJ_SHAREABLE_P(ce->value)) {
|
||||
// checked after the walk, as rb_ractor_shareable_p may allocate or raise
|
||||
rb_ary_push((VALUE)data, ID2SYM(id));
|
||||
}
|
||||
return ID_TABLE_CONTINUE;
|
||||
}
|
||||
|
||||
static int
|
||||
init_copy_check_field_i(ID id, VALUE val, st_data_t arg)
|
||||
{
|
||||
if ((rb_is_instance_id(id) || rb_is_class_id(id)) && !rb_ractor_shareable_p(val)) {
|
||||
rb_raise(rb_eRactorIsolationError,
|
||||
"can not copy a class/module created by another Ractor because "
|
||||
"variable %"PRIsVALUE" refers to an unshareable object", rb_id2str(id));
|
||||
}
|
||||
return ST_CONTINUE;
|
||||
}
|
||||
|
||||
// The copy belongs to the copying Ractor, so it must not carry over unshareable
|
||||
// objects owned by the source's Ractor.
|
||||
static enum rb_id_table_iterator_result
|
||||
move_check_const_i(ID id, VALUE v, void *data)
|
||||
{
|
||||
@@ -1064,39 +1040,13 @@ rb_class_check_singleton_movable(VALUE klass)
|
||||
} while ((klass = class_own_metaclass(klass)) != 0);
|
||||
}
|
||||
|
||||
static void
|
||||
init_copy_check_tables(VALUE klass)
|
||||
{
|
||||
if (RCLASS_CONST_TBL(klass)) {
|
||||
const VALUE ids = rb_ary_hidden_new(0);
|
||||
RB_VM_LOCKING() { // see rb_const_lookup_copy
|
||||
rb_id_table_foreach(RCLASS_CONST_TBL(klass), init_copy_check_const_i, (void *)ids);
|
||||
}
|
||||
for (long i = 0; i < RARRAY_LEN(ids); i++) {
|
||||
const ID id = SYM2ID(RARRAY_AREF(ids, i));
|
||||
rb_const_entry_t entry;
|
||||
if (rb_const_lookup_copy(klass, id, &entry) && !UNDEF_P(entry.value) &&
|
||||
!rb_ractor_shareable_p(entry.value)) {
|
||||
rb_raise(rb_eRactorIsolationError,
|
||||
"can not copy a class/module created by another Ractor because "
|
||||
"constant %"PRIsVALUE" refers to an unshareable object", rb_id2str(id));
|
||||
}
|
||||
}
|
||||
}
|
||||
rb_ivar_foreach_buffered(klass, init_copy_check_field_i, 0);
|
||||
}
|
||||
|
||||
// the copy would carry the owner's field values, which only the owner may read
|
||||
static void
|
||||
init_copy_owner_check(VALUE orig)
|
||||
{
|
||||
if (!rb_class_owned_p(orig)) {
|
||||
init_copy_check_tables(orig);
|
||||
|
||||
// rb_singleton_class_clone_and_attach copies the metaclass's tables too
|
||||
VALUE meta = METACLASS_OF(orig);
|
||||
if (RCLASS_SINGLETON_P(meta)) {
|
||||
init_copy_check_tables(meta);
|
||||
}
|
||||
VALUE meta = METACLASS_OF(orig);
|
||||
if (!rb_class_owned_p(orig) || (RCLASS_SINGLETON_P(meta) && !rb_class_owned_p(meta))) {
|
||||
rb_raise(rb_eRactorIsolationError, "can not copy a class/module created by another Ractor");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user