Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does Ruby 2.5.0 raise SystemStackError in Alpine Linux even if stack level does not too much deep?

Tags:

ruby

This is a sample code to reproduce problem.

# test.rb
n = 100000
res = {}
1.upto(n).to_a.inject(res) do |r, i|
  r[i] = {}
end

def f(x)
  x.each_value { |v| f(v) }
end

f(res)

Run this code using Docker image provided by Docker Hub.

  • Ruby 2.5.0 in Alpine Linux is UNEXPECTED (not too much deep)
  • Ruby 2.4.3 in Alpine Linux is EXPECTED
  • RUby 2.5.0 in Debian is EXPECTED

What makes difference?

supplementary explanation

(I'm sorry, my English is not good.)

This is a supplementary explanation.

I know sample code cause SystemStackError. I wrote sample code to cause SystemStackError.

I'd like to know reason of difference of "levels".

  • Ruby 2.5.0 in Alpine Linux -> "... 137 levels..."
  • RUby 2.4.3 in ALpine Linux -> "... 10067 levels..."
  • RUby 2.5.0 in Debian -> "... 9866 levels..."

Ruby 2.5.0 + Alpine Linux (ruby:2.5.0-alpine3.7)

In this case, stack level is 137.

% docker container run -v (pwd):/mnt/my --rm -it ruby:2.5.0-alpine3.7 ruby -v /mnt/my/test.rb
ruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-linux-musl]
Traceback (most recent call last):
        149: from /mnt/my/test.rb:11:in `<main>'
        148: from /mnt/my/test.rb:8:in `f'
        147: from /mnt/my/test.rb:8:in `each_value'
        146: from /mnt/my/test.rb:8:in `block in f'
        145: from /mnt/my/test.rb:8:in `f'
        144: from /mnt/my/test.rb:8:in `each_value'
        143: from /mnt/my/test.rb:8:in `block in f'
        142: from /mnt/my/test.rb:8:in `f'
         ... 137 levels...
          4: from /mnt/my/test.rb:8:in `f'
          3: from /mnt/my/test.rb:8:in `each_value'
          2: from /mnt/my/test.rb:8:in `block in f'
          1: from /mnt/my/test.rb:8:in `f'
/mnt/my/test.rb:8:in `each_value': stack level too deep (SystemStackError)

But simple case, stack level is 13092.

% docker container run --rm -it ruby:2.5.0-alpine3.7 ruby -v -e 'def f; f; end; f'
ruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-linux-musl]
Traceback (most recent call last):
        13104: from -e:1:in `<main>'
        13103: from -e:1:in `f'
        13102: from -e:1:in `f'
        13101: from -e:1:in `f'
        13100: from -e:1:in `f'
        13099: from -e:1:in `f'
        13098: from -e:1:in `f'
        13097: from -e:1:in `f'
         ... 13092 levels...
            4: from -e:1:in `f'
            3: from -e:1:in `f'
            2: from -e:1:in `f'
            1: from -e:1:in `f'
-e:1:in `f': stack level too deep (SystemStackError)

Ruby 2.4.3 + Alpine Linux (ruby:2.4.3-alpine3.7)

In this case, stack level is 10067. There is a big difference from above case.

% docker container run -v (pwd):/mnt/my --rm -it ruby:2.4.3-alpine3.7 ruby -v /mnt/my/test.rb
ruby 2.4.3p205 (2017-12-14 revision 61247) [x86_64-linux-musl]
/mnt/my/test.rb:8:in `each_value': stack level too deep (SystemStackError)
        from /mnt/my/test.rb:8:in `f'
        from /mnt/my/test.rb:8:in `block in f'
        from /mnt/my/test.rb:8:in `each_value'
        from /mnt/my/test.rb:8:in `f'
        from /mnt/my/test.rb:8:in `block in f'
        from /mnt/my/test.rb:8:in `each_value'
        from /mnt/my/test.rb:8:in `f'
        from /mnt/my/test.rb:8:in `block in f'
         ... 10067 levels...
        from /mnt/my/test.rb:8:in `block in f'
        from /mnt/my/test.rb:8:in `each_value'
        from /mnt/my/test.rb:8:in `f'
        from /mnt/my/test.rb:11:in `<main>'

I show output of diff Dockerfile.

--- 2.4/alpine3.7/Dockerfile    2017-12-28 20:34:43.000000000 +0900
+++ 2.5/alpine3.7/Dockerfile    2017-12-28 20:34:43.000000000 +0900
@@ -7,9 +7,9 @@
                echo 'update: --no-document'; \
        } >> /usr/local/etc/gemrc

-ENV RUBY_MAJOR 2.4
-ENV RUBY_VERSION 2.4.3
-ENV RUBY_DOWNLOAD_SHA256 23677d40bf3b7621ba64593c978df40b1e026d8653c74a0599f0ead78ed92b51
+ENV RUBY_MAJOR 2.5
+ENV RUBY_VERSION 2.5.0
+ENV RUBY_DOWNLOAD_SHA256 1da0afed833a0dab94075221a615c14487b05d0c407f991c8080d576d985b49b
 ENV RUBYGEMS_VERSION 2.7.4
 ENV BUNDLER_VERSION 1.16.1

It means using same Alpine Linux. Just the Ruby is different.

Ruby 2.5.0 + Debian (ruby:2.5.0-stretch)

In this case, stack level is 9866. This case uses Ruby 2.5.0, but it runs at Debian (not Alpine).

% docker container run -v (pwd):/mnt/my --rm -it ruby:2.5.0-stretch ruby -v /mnt/my/test.rb
ruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-linux]
Traceback (most recent call last):
        9878: from /mnt/my/test.rb:11:in `<main>'
        9877: from /mnt/my/test.rb:8:in `f'
        9876: from /mnt/my/test.rb:8:in `each_value'
        9875: from /mnt/my/test.rb:8:in `block in f'
        9874: from /mnt/my/test.rb:8:in `f'
        9873: from /mnt/my/test.rb:8:in `each_value'
        9872: from /mnt/my/test.rb:8:in `block in f'
        9871: from /mnt/my/test.rb:8:in `f'
         ... 9866 levels...
           4: from /mnt/my/test.rb:8:in `f'
           3: from /mnt/my/test.rb:8:in `each_value'
           2: from /mnt/my/test.rb:8:in `block in f'
           1: from /mnt/my/test.rb:8:in `f'
/mnt/my/test.rb:8:in `each_value': stack level too deep (SystemStackError)
like image 607
koshigoe Avatar asked Dec 29 '17 14:12

koshigoe


1 Answers

We have seen this in some edge cases as well. We have confirmed that both the OS and Ruby stack sizes are the same in all cases. (alpine 2.4.3, alpine 2.5.0, and running 2.5.0 locally on MacOS)

The only differentiator we can find thusfar is that the Alpine 2.5.0 images are built off of Alpine 3.7, which builds Ruby with LibreSSL. The 2.4.3 image is built off of Alpine 3.4, which is still using OpenSSL. Our local 2.5.0 builds against OpenSSL do not show this "short" stack length.

like image 159
Joe Avatar answered Nov 15 '22 23:11

Joe