Issue #22291 has been reported by jhawthorn (John Hawthorn). ---------------------------------------- Bug #22291: Instance variables should be forbidden on Ractor-shareable objects https://bugs.ruby-lang.org/issues/22291 * Author: jhawthorn (John Hawthorn) * Status: Open * Backport: 3.3: UNKNOWN, 3.4: UNKNOWN, 4.0: UNKNOWN ---------------------------------------- Usually Ractor-shareable objects are frozen, but we have a number of objects which are shareable even though they aren't frozen (and likely will have more in the future from C extensions). * Classes/Modules (ignored in this issue, they have special ivar handling) * `Ractor` * `Ractor::Port` * Isolated procs/lambdas from `Ractor.shareable_proc` * `ENV` These are set shareable without the usual checks that all referenced objects are also shareable. This causes a potential issue, because these objects could have unshareable instance variables, breaking the Ractor invariant (potentially causing race conditions and segmentation faults). Currently, we attempt to avoid this by having a check on ivars read that forbids reading these from a non-main Ractor ``` R = Ractor.new {} R.instance_variable_set(:@iv, +"mutable") Ractor.new { R.instance_variable_get(:@iv) }.value #=> Ractor::IsolationError: can not access instance variables of shareable objects from non-main Ractors ``` This is detected by checking on ivar read for objects which are shareable but **not** frozen. However this isn't sound because most of these unshareable objects can be frozen, turning the check off. ```ruby R = Ractor.new {} R.instance_variable_set(:@iv, +"mutable") R.freeze Ractor.new { R.instance_variable_get(:@iv) }.value # => no error, created Ractor got access to an object it shouldn't ``` (weird quirk: `ENV#freeze` raises, but you can `Kernel.instance_method(:freeze).bind_call(ENV)` so it's still an issue) I propose that we fix this by forbidding instance variable writes to objects which are frozen. This removes the need to check on read and maintains the Ractor invariant. ``` R = Ractor.new {} R.instance_variable_set(:@iv, 123) #=> Ractor::IsolationError: can not set instance variables of shareable Ractor objects Ractor.new { R.instance_variable_get(:@foo) # => always nil, because ivars are forbidden } ``` These objects behave essentially as though they're frozen, but only the IVs are frozen. This is an important issue to solve both for correctness, and because I want us to decide the semantics so that they can be implemented in ZJIT. Currently ZJIT won't compile ivar reads on multi-ractor mode because of this issue. (I will link a patch implementing this shortly) -- https://bugs.ruby-lang.org/