Project

General

Profile

Actions

Bug #21208

open

`Ractor#send(move: true)` allow moving objects that are on the stack, and used by C code.

Added by byroot (Jean Boussier) 1 day ago. Updated about 13 hours ago.

Status:
Open
Assignee:
Target version:
-
[ruby-core:121490]

Description

The following script causes a crash:

rac = Ractor.new do
  Ractor.receive
end

hash = Hash[*50.times]

hash.merge!(12 => 0, 14 => 0) do |key, old_val, new_val|
  if key == 12
    rac.send(hash, move: true)
  end
  new_val
end

p rac.take

Contrary to previous crashes related to send(move: true), I'm afraid this one is with the design of the feature itself, not just the implementation.

If we allow objects to be moved, any C code that calls rb_yield() may cause memory corruption if one of the objects it uses is moved.

I personally can't see any realistic solution to this.

Updated by byroot (Jean Boussier) 1 day ago

The following script causes a crash

Actually, that was only because I was on an older branch, it no longer crash on master, and even behave somewhat correctly by sheer luck.

The general issue remain, send(obj, move: true) essentially zero-out the object slot, and none of the C methods that call rb_yield expect that, and I don't think we can realistically handle that everywhere in Ruby itself, and certainly not in C extensions.

Updated by luke-gru (Luke Gruber) about 13 hours ago ยท Edited

Hmm... this is tricky, good find! This probably won't occur often, I'm having a hard time coming up with a situation where it would happen with some benign looking code.

It's fun thinking about solutions to these kinds of things, so as thought experiment let's say we don't free the src object or zero out its slot and let the GC do it later. So for example an array would be moved by copying its storage into the new moved object and then keeping the original array around but niling out the contents or clearing it, keeping it a T_ARRAY so things like RARRAY_LEN still work on it (so Array#each still works if we're already in a call to it) but it appears as if it's a MovedObject to users that try to call methods on it. Then during GC, the GC could notice it's still a T_ARRAY and take care of the heap storage. Maybe we wouldn't even have to change the class to MovedObject, because that could complicate things if some code assumes that T_ARRAYs always have a class that is a descendant of Array for example. It could be a flag on the object that gets checked during every method call, and raises an error there instead of being its own real class.

Actions

Also available in: Atom PDF

Like0
Like0Like0