Bug #22280
openRuby::Box breaks $? after Kernel#system / IO.popen
Description
Under RUBY_BOX=1, reading $? after Kernel#system or IO.popen returns an uninitialized Process::Status. Process.last_status is correct.
$ RUBY_BOX=1 ruby -e 'p system("false"); p $?; p Process.last_status'
false
#<Process::Status: uninitialized>
#<Process::Status: pid 69619 exit 1>
In a user box, rb_gvar_get caches gvar reads in the box gvar_tbl, storing a clone of the getter result. $? is a readonly virtual variable backed by per-thread state (rb_last_status_get), so the cache can never be right. Worse, Process::Status is TypedData and clone does not copy the wrapped struct, so even the first read yields an uninitialized status (exit 0). $$ has the same defect. A forked child reports the parent's pid.
The regexp special variables were already fixed this way. Readonly virtual variables ($&, $`, $', $+) are marked rb_gvar_box_ready so reads bypass the cache (commit 8ad6baa0174). $? and $$ need the same marking in InitVM_process.
This breaks rake's sh ("Command failed with status (0)") and bundler's env_helpers specs under RUBY_BOX=1. Related: #22275
This is fixed by https://github.com/ruby/ruby/pull/18577.