Project

General

Profile

Bug #12430

Segfault in irb when improperly using coerce in combination with method_missing

Added by sandal (Gregory Brown) over 3 years ago. Updated 5 months ago.

Status:
Closed
Priority:
Normal
Assignee:
-
Target version:
-
ruby -v:
ruby 2.3.1p112
[ruby-core:75724]

Description

Don't ask how or why I wrote this code, but suppose you have a class like this:

class BrokenNumber
  def initialize(num)
    @num = num
  end

  def method_missing(m, *a, &b)
    @num.send(m, *a, &b)
  end

  def coerce(other)
    [self, self]
  end
end

Then you require this code in IRB, and you create a new instance (x) and you then call 2+x.

The first call will raise a SystemStackError. The second will cause a segmentation fault.

>> require "./broken"
=> true
>> x = BrokenNumber.new(2)
=> #<BrokenNumber:0x007f8b5a84c190 @num=2>
>> 2 + x
SystemStackError: stack level too deep
    from /Users/gtb/broken.rb:7:in `+'
    from /Users/gtb/broken.rb:7:in `method_missing'
    from /Users/gtb/broken.rb:7:in `+'
    from /Users/gtb/broken.rb:7:in `method_missing'
    from /Users/gtb/broken.rb:7:in `+'
    from /Users/gtb/broken.rb:7:in `method_missing'
    from /Users/gtb/broken.rb:7:in `+'
    from /Users/gtb/broken.rb:7:in `method_missing'
    from /Users/gtb/broken.rb:7:in `+'
    from /Users/gtb/broken.rb:7:in `method_missing'
    from /Users/gtb/broken.rb:7:in `+'
    from /Users/gtb/broken.rb:7:in `method_missing'
    from /Users/gtb/broken.rb:7:in `+'
    from /Users/gtb/broken.rb:7:in `method_missing'
    from /Users/gtb/broken.rb:7:in `+'
    from /Users/gtb/broken.rb:7:in `method_missing'
... 7559 levels...
    from /Users/gtb/broken.rb:7:in `method_missing'
    from /Users/gtb/broken.rb:7:in `+'
    from /Users/gtb/broken.rb:7:in `method_missing'
    from /Users/gtb/broken.rb:7:in `+'
    from /Users/gtb/broken.rb:7:in `method_missing'
    from /Users/gtb/broken.rb:7:in `+'
    from /Users/gtb/broken.rb:7:in `method_missing'
    from /Users/gtb/broken.rb:7:in `+'
    from /Users/gtb/broken.rb:7:in `method_missing'
    from /Users/gtb/broken.rb:7:in `+'
    from /Users/gtb/broken.rb:7:in `method_missing'
    from /Users/gtb/broken.rb:7:in `+'
    from /Users/gtb/broken.rb:7:in `method_missing'
    from (irb):3:in `+'
    from (irb):3
    from /Users/gtb/.rubies/ruby-2.3.1/bin/irb:11:in `<main>'
>> 2 + x
Segmentation fault: 11
MacBook-Pro:~ gtb$ ruby -v
ruby 2.3.1p112 (2016-04-26 revision 54768) [x86_64-darwin15]

I've asked multiple people to confirm this and they've reproduced the error. I have no clue how to debug it, but thought it may be worth mentioning... even though it's a strange corner case and invalid code.

History

Updated by sandal (Gregory Brown) over 3 years ago

  • ruby -v set to ruby 2.3.1p112

Updated by jeremyevans0 (Jeremy Evans) 5 months ago

  • Status changed from Open to Closed

I don't think this is a bug, the SystemStackError is expected. In some cases, Ruby doesn't catch the stack overflow and you end up with a segfault.

The reason for the behavior:

x = BrokenNumber.new(2)

# original expression
2 + x

# Integer#+ calls (via rb_num_coerce_bin->do_coerce):
x.coerce(2)

# x.coerce(2) returns:
[x, x]

# Integer#+ calls (via rb_num_coerce_bin):
x + x

# BrokenNumber#+ not defined, so calls:
x.method_missing(:+, x)

# BrokenNumber#method_missing calls:
2 + x # the original expression, so a loop

Also available in: Atom PDF