git bisect shows the problem is caused by the following commit.
% git bisect bad
84e4453436c3549b4fda6014cdd5fcc9e0b80755 is the first bad commit
commit 84e4453436c3549b4fda6014cdd5fcc9e0b80755
Author: Aaron Patterson <tenderlove@ruby-lang.org>
Date: Tue Feb 7 17:46:42 2023 -0800
Use a functional red-black tree for indexing the shapes
This is an experimental commit that uses a functional red-black tree to
create an index of the ancestor shapes. It uses an Okasaki style
functional red black tree:
https://www.cs.tufts.edu/comp/150FP/archive/chris-okasaki/redblack99.pdf
This tree is advantageous because:
* It offers O(n log n) insertions and O(n log n) lookups.
* It shares memory with previous "versions" of the tree
When we insert a node in the tree, only the parts of the tree that need
to be rebalanced are newly allocated. Parts of the tree that don't need
to be rebalanced are not reallocated, so "new trees" are able to share
memory with old trees. This is in contrast to a sorted set where we
would have to duplicate the set, and also resort the set on each
insertion.
I've added a new stat to RubyVM.stat so we can understand how the red
black tree increases.
benchmark/vm_ivar_ic_miss.yml | 20 +++
rjit_c.rb | 5 +
shape.c | 309 ++++++++++++++++++++++++++++++++++++++++--
shape.h | 15 ++
vm.c | 8 +-
5 files changed, 342 insertions(+), 15 deletions(-)
create mode 100644 benchmark/vm_ivar_ic_miss.yml
It looks like it has to do with defined?() in an if expression and its catch table entries when the first part of the if expression has been eliminated and there is no then label to follow.
For example, this is a minimal reproduction:
defmy_methodivar=nil# seems to be needed to reproduce the issueiffalse&&defined?File::TMPFILEendraise"woops"endmy_method
The defined catch table entry is not setup correctly in this case.
I only tried this on ruby head, but I get a segfault.
This is a weird way to reproduce, but you can see it on https://runruby.dev/ if you comment out the Gemfile and put this in main.rb:
defmy_methodvar=niliffalse&&defined?File::TMPFILEendraise"woops"endputsRubyVM::InstructionSequence.disasm(method(:my_method))# you should see the invalid catch table entry in the disasm bytecodemy_method()# if this doesn't trigger an error, try running it multiple times.
Okay, I figured out what's happening. In compile.c, new LABELs are allocated from an arena, and this is using xmalloc, so it's not zeroed. Labels have a position field that is not set in the new_label_body() function, so it could be zeroed or not depending on many things of course. When compiling the defined after a known compile-time false value, its labels are NOT added to the anchor, and so its position is not set during iseq_set_sequence in iseq_setup, but it is saved to the iseq's catch_table_ary. Then, during iseq_set_exception_table, the iseq_catch_table_entry's start and end are set to the LABEL's position because the LABEL is inside the iseq's catch_table_ary. There is no check for garbage values, which would be negative in this case, as position is an int. The iseq_catch_table_entry takes this possibly garbage value and saves it as its start and end.
I've updated my PR and added some assertions to the code to make sure this doesn't happen elsewhere.
These issues don't appear on current ruby master because prism is the new parser/compiler and it only affects the parse.y parser/compiler.
Backport changed from 3.1: REQUIRED, 3.2: REQUIRED, 3.3: REQUIRED to 3.1: WONTFIX, 3.2: WONTFIX, 3.3: REQUIRED
While trying to backport d592ddd5e619ffe1691b8050de2ccc3e1bd6e080 to ruby_3_2, I found that it depends on 6e64d4370456190541705ec4c6cf3af6bf4ac647 (for [Bug #19862]). And I cannot reproduce the SEGV on rub-3.2.
I decided to set WONTFIX for 3.1/3.2.
Please tell us if you found it can be reproduced on 3.2.
Backporting this patch (https://github.com/ruby/ruby/pull/11554) to ruby_3_3 causes a lot of conflicts. Please file a PR to ruby_3_3 branch on GitHub for this ticket to be backported.