[ruby-core:111979] [Ruby master Bug#19369] Small corner-case issue that breaks Ractor isolation: change cloned object from another thread

Issue #19369 has been reported by luke-gru (Luke Gruber). ---------------------------------------- Bug #19369: Small corner-case issue that breaks Ractor isolation: change cloned object from another thread https://bugs.ruby-lang.org/issues/19369 * Author: luke-gru (Luke Gruber) * Status: Open * Priority: Normal * Backport: 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN, 3.2: UNKNOWN ---------------------------------------- I was looking into how objects are traversed for deep cloning and I came up with a way to break it. I don't think it'll ever happen in real life so it's not really an issue, just an interesting case. Run with warnings disabled. ```ruby obj = Object.new p "unshareable obj:", obj UNSHAREABLE = obj GO = false SET = false class Object attr_accessor :unshareable def initialize_clone(orig) puts "Clone called for #{orig.inspect}, self = #{self.inspect}" _self = self if orig == UNSHAREABLE t = Thread.new do puts "In thread" Thread.pass until GO puts "Setting unshareable!" # this must be done in separate thread to bypass object traversal deep-cloning _self.unshareable = UNSHAREABLE Object.const_set(:SET, true) end end super(orig) end end r = Ractor.new(obj) do |o| puts "from r#{Ractor.current.object_id} obj #{o.inspect}" GO = true loop until SET p "from ractor, got unshareable:", o.unshareable end r.take ``` -- https://bugs.ruby-lang.org/

Issue #19369 has been updated by luke-gru (Luke Gruber). If you wanted to fix this one way would be to disable thread creation while deep-cloning in `Ractor.new` or `Ractor#send`. Another idea would be to not call `initialize_clone` during deep-cloning for `Ractor.new` or `Ractor#send`, but that would be a negative change IMO. ---------------------------------------- Bug #19369: Small corner-case issue that breaks Ractor isolation: change cloned object from another thread https://bugs.ruby-lang.org/issues/19369#change-101416 * Author: luke-gru (Luke Gruber) * Status: Open * Priority: Normal * Backport: 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN, 3.2: UNKNOWN ---------------------------------------- I was looking into how objects are traversed for deep cloning and I came up with a way to break it. I don't think it'll ever happen in real life so it's not really an issue, just an interesting case. Run with warnings disabled. ```ruby obj = Object.new p "unshareable obj:", obj UNSHAREABLE = obj GO = false SET = false class Object attr_accessor :unshareable def initialize_clone(orig) puts "Clone called for #{orig.inspect}, self = #{self.inspect}" _self = self if orig == UNSHAREABLE t = Thread.new do puts "In thread" Thread.pass until GO puts "Setting unshareable!" # this must be done in separate thread to bypass object traversal deep-cloning _self.unshareable = UNSHAREABLE Object.const_set(:SET, true) end end super(orig) end end r = Ractor.new(obj) do |o| puts "from r#{Ractor.current.object_id} obj #{o.inspect}" GO = true loop until SET p "from ractor, got unshareable:", o.unshareable end r.take ``` -- https://bugs.ruby-lang.org/

Issue #19369 has been updated by luke-gru (Luke Gruber). I made a patch for this here: https://github.com/luke-gru/ruby/commit/ad760be949c2a35aac71ac0ff8dea4ec3169.... I'll send PR if it's an acceptable solution. ---------------------------------------- Bug #19369: Small corner-case issue that breaks Ractor isolation: change cloned object from another thread https://bugs.ruby-lang.org/issues/19369#change-101449 * Author: luke-gru (Luke Gruber) * Status: Open * Priority: Normal * Backport: 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN, 3.2: UNKNOWN ---------------------------------------- I was looking into how objects are traversed for deep cloning and I came up with a way to break it. I don't think it'll ever happen in real life so it's not really an issue, just an interesting case. Run with warnings disabled. ```ruby obj = Object.new p "unshareable obj:", obj UNSHAREABLE = obj GO = false SET = false class Object attr_accessor :unshareable def initialize_clone(orig) puts "Clone called for #{orig.inspect}, self = #{self.inspect}" _self = self if orig == UNSHAREABLE t = Thread.new do puts "In thread" Thread.pass until GO puts "Setting unshareable!" # this must be done in separate thread to bypass object traversal deep-cloning _self.unshareable = UNSHAREABLE Object.const_set(:SET, true) end end super(orig) end end r = Ractor.new(obj) do |o| puts "from r#{Ractor.current.object_id} obj #{o.inspect}" GO = true loop until SET p "from ractor, got unshareable:", o.unshareable end r.take ``` -- https://bugs.ruby-lang.org/

Issue #19369 has been updated by hsbt (Hiroshi SHIBATA). Status changed from Open to Assigned Assignee set to ko1 (Koichi Sasada) ---------------------------------------- Bug #19369: Small corner-case issue that breaks Ractor isolation: change cloned object from another thread https://bugs.ruby-lang.org/issues/19369#change-101450 * Author: luke-gru (Luke Gruber) * Status: Assigned * Priority: Normal * Assignee: ko1 (Koichi Sasada) * Backport: 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN, 3.2: UNKNOWN ---------------------------------------- I was looking into how objects are traversed for deep cloning and I came up with a way to break it. I don't think it'll ever happen in real life so it's not really an issue, just an interesting case. Run with warnings disabled. ```ruby obj = Object.new p "unshareable obj:", obj UNSHAREABLE = obj GO = false SET = false class Object attr_accessor :unshareable def initialize_clone(orig) puts "Clone called for #{orig.inspect}, self = #{self.inspect}" _self = self if orig == UNSHAREABLE t = Thread.new do puts "In thread" Thread.pass until GO puts "Setting unshareable!" # this must be done in separate thread to bypass object traversal deep-cloning _self.unshareable = UNSHAREABLE Object.const_set(:SET, true) end end super(orig) end end r = Ractor.new(obj) do |o| puts "from r#{Ractor.current.object_id} obj #{o.inspect}" GO = true loop until SET p "from ractor, got unshareable:", o.unshareable end r.take ``` -- https://bugs.ruby-lang.org/
participants (2)
-
hsbt (Hiroshi SHIBATA)
-
luke-gru (Luke Gruber)