Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Segfault when include sorted_set in ruby 3+ on alpine #13

Open
kimyu92 opened this issue Apr 25, 2022 · 8 comments
Open

Segfault when include sorted_set in ruby 3+ on alpine #13

kimyu92 opened this issue Apr 25, 2022 · 8 comments

Comments

@kimyu92
Copy link

kimyu92 commented Apr 25, 2022

Environment:

ruby: 3.0.4/3.1.2
alpine 3.15.4
sample official ruby image from dockerhub: https://github.com/docker-library/ruby/blob/dd1b1c9650fd9470edf8399abb2a746e597a821b/3.0/alpine3.15/Dockerfile

Hi 👋 , I have noticed sorted_set is required in ruby 3 and business_time has made an update to include sorted_set.

Problem

As soon as, I installed both rmagick and rbtree or rmagick with sorted_set, segmentation fault would happen immediately with the single file rails setup below.

I have noticed sorted_set includes rbtree as dependency instead of rbtree3 and I'm not sure it's intended or not. Installing rbtree3 would not cause segfault immediately but installing rbtree or sorted_set while require rmagick with trigger segfault immediately as soon as the script run in alpine linux

Notes

Stack trace

/usr/local/bundle/gems/carrierwave-2.2.2/lib/carrierwave/processing/rmagick.rb:352: [BUG] Segmentation fault at 0x0000000000000000
ruby 3.0.4p208 (2022-04-12 revision 3fa771dded) [x86_64-linux-musl]

-- Control frame information -----------------------------------------------
c:0066 p:---- s:0334 e:000333 CFUNC  :read
c:0065 p:0050 s:0329 e:000328 METHOD /usr/local/bundle/gems/carrierwave-2.2.2/lib/carrierwave/processing/rmagick.rb:352
c:0064 p:0018 s:0317 e:000316 METHOD /usr/local/bundle/gems/carrierwave-2.2.2/lib/carrierwave/processing/rmagick.rb:176
c:0063 p:0050 s:0311 e:000310 BLOCK  /usr/local/bundle/gems/carrierwave-2.2.2/lib/carrierwave/uploader/processing.rb:83 [FINISH]
c:0062 p:---- s:0305 e:000304 CFUNC  :each
c:0061 p:0009 s:0301 e:000300 BLOCK  /usr/local/bundle/gems/carrierwave-2.2.2/lib/carrierwave/uploader/processing.rb:75
c:0060 p:0016 s:0298 e:000297 METHOD /usr/local/bundle/gems/carrierwave-2.2.2/lib/carrierwave/uploader/callbacks.rb:15
c:0059 p:0018 s:0292 e:000291 METHOD /usr/local/bundle/gems/carrierwave-2.2.2/lib/carrierwave/uploader/processing.rb:74
c:0058 p:0010 s:0287 e:000286 BLOCK  /usr/local/bundle/gems/carrierwave-2.2.2/lib/carrierwave/uploader/callbacks.rb:14 [FINISH]
c:0057 p:---- s:0283 e:000282 CFUNC  :each
c:0056 p:0012 s:0279 e:000278 METHOD /usr/local/bundle/gems/carrierwave-2.2.2/lib/carrierwave/uploader/callbacks.rb:14
c:0055 p:0176 s:0273 e:000272 METHOD /usr/local/bundle/gems/carrierwave-2.2.2/lib/carrierwave/uploader/cache.rb:144
c:0054 p:0065 s:0268 e:000267 BLOCK  /usr/local/bundle/gems/carrierwave-2.2.2/lib/carrierwave/mounter.rb:63
c:0053 p:0002 s:0264 e:000263 METHOD /usr/local/bundle/gems/carrierwave-2.2.2/lib/carrierwave/mounter.rb:176
c:0052 p:0005 s:0259 e:000258 BLOCK  /usr/local/bundle/gems/carrierwave-2.2.2/lib/carrierwave/mounter.rb:47 [FINISH]
c:0051 p:---- s:0255 e:000254 CFUNC  :map
c:0050 p:0035 s:0251 e:000250 METHOD /usr/local/bundle/gems/carrierwave-2.2.2/lib/carrierwave/mounter.rb:46
c:0049 p:0011 s:0245 e:000244 METHOD /usr/local/bundle/gems/carrierwave-2.2.2/lib/carrierwave/mount.rb:146
c:0048 p:0006 s:0240 e:000239 METHOD /usr/local/bundle/gems/carrierwave-2.2.2/lib/carrierwave/mount.rb:373
c:0047 p:0054 s:0235 e:000234 METHOD /usr/local/bundle/gems/carrierwave-2.2.2/lib/carrierwave/orm/activerecord.rb:75
c:0046 p:0012 s:0229 e:000227 BLOCK  test.rb:68 [FINISH]
c:0045 p:---- s:0225 e:000224 CFUNC  :instance_exec
c:0044 p:0013 s:0220 e:000219 METHOD /usr/local/bundle/gems/rspec-core-3.11.0/lib/rspec/core/example.rb:457
c:0043 p:0010 s:0214 e:000213 METHOD /usr/local/bundle/gems/rspec-core-3.11.0/lib/rspec/core/hooks.rb:365
c:0042 p:0007 s:0209 e:000208 BLOCK  /usr/local/bundle/gems/rspec-core-3.11.0/lib/rspec/core/hooks.rb:529 [FINISH]
c:0041 p:---- s:0205 e:000204 CFUNC  :each
c:0040 p:0012 s:0201 e:000200 METHOD /usr/local/bundle/gems/rspec-core-3.11.0/lib/rspec/core/hooks.rb:528
c:0039 p:0013 s:0194 e:000193 BLOCK  /usr/local/bundle/gems/rspec-core-3.11.0/lib/rspec/core/hooks.rb:615 [FINISH]
c:0038 p:---- s:0190 e:000189 CFUNC  :reverse_each

Repro

  1. Pull either docker pull ruby:3.0.4-alpine3.15 or docker pull ruby:3.1.2-alpine3.15
  2. Save this file as repro.rb in container
  3. Run command to install imagemagick 7, apk add pkgconfig imagemagick imagemagick-dev imagemagick-libs
  4. Run command to install sqlite3 apk add sqlite sqlite-dev
  5. Run command to install other sys libs apk add build-base gcc autoconf libffi-dev openssl-dev
  6. Run ruby repro.rb inside docker container
# frozen_string_literal: true

require 'bundler/inline'

gemfile(true) do
  source 'https://rubygems.org'

  git_source(:github) { |repo| "https://github.com/#{repo}.git" }

  gem 'rails', '~> 7.0.2.3'
  gem 'rspec-rails', '~> 5.1.1'

  gem 'carrierwave'
  gem 'rmagick', '~> 4.2.5'

  # either gem would make interpreter blows up
  # adding , require: false would on either gem would make it work but not ideal
  # gem 'rbtree'
  gem 'sorted_set'
  gem 'sqlite3'
end

require 'rmagick'

require 'active_support'
require 'active_support/core_ext/object/blank'
require 'active_record'

require 'action_controller/railtie'
require 'action_view/railtie'

require 'rspec/autorun'
require 'rspec/rails'
require 'carrierwave/orm/activerecord'

ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:')

ActiveRecord::Schema.define do
  drop_table :users, if_exists: true

  create_table :users, force: true do |t|
    t.timestamps null: false
    t.string :avatar
  end
end

class AvatarUploader < CarrierWave::Uploader::Base
  include CarrierWave::RMagick
  storage :file

  process resize_to_fit: [777, 777]
end

class User < ActiveRecord::Base
  mount_uploader :avatar, AvatarUploader
end

RSpec.configure do |config|
  config.filter_run_excluding skip: true
  config.run_all_when_everything_filtered = true
  config.filter_run focus: true
end

RSpec.describe 'Segfault error' do
  let(:user) { User.new }
  let(:file) { Tempfile.new }
  let(:avatar) { Rack::Test::UploadedFile.new(file, 'application/pdf', true, original_filename: 'sample.pdf') }
  let(:avatar_key) { sample_key(User.new.uploader, extension: 'pdf') }

  before do
    user.avatar = avatar
  end

  it { expect(user.avatar).to eq(avatar) }
end
@kimyu92 kimyu92 changed the title Segfault when include sorted_set in ruby 3.0.4 alpine 3.15 Segfault when include sorted_set in ruby 3+ on alpine Apr 25, 2022
@kimyu92
Copy link
Author

kimyu92 commented Apr 25, 2022

@knu @kyrylo any insight on this?

@kyrylo
Copy link

kyrylo commented Apr 26, 2022

Hey, @kimyu92. You mentioned that when you use rbtree3 the segfault doesn't happen immediately. Did you mean that it does happen after a certain amount of time?

Based on my research in airbrake/airbrake-ruby#641 the issue was with rmagick. I see you did open an issue there 👍 rmagick/rmagick#1334

@kimyu92
Copy link
Author

kimyu92 commented Apr 26, 2022

You mentioned that when you use rbtree3 the segfault doesn't happen immediately. Did you mean that it does happen after a certain amount of time?

What I meant was simply placing rbtree3 in gemfile without explicitly requiring it, won't cause the blow up immediately. However, placing rbtree3 in gemfile and call require rbtree would cause seg fault which is different behavior than rbtree v2 gem.

gemfile(true) do
  ..
  gem 'rmagick'
  gem 'rbtree3'
end

# ...
require 'rmagick'

💣

gemfile(true) do
  ..
  gem 'rmagick'
  gem 'rbtree3'
end

# ...
require 'rbtree'
require 'rmagick'

💣

gemfile(true) do
  ..
  gem 'rmagick'
  gem 'rbtree'
end

# ...
require 'rmagick'

In short, it's still very strange that why including rbtree would collide or change behavior of rmagick causing it seg fault

@kimyu92
Copy link
Author

kimyu92 commented Apr 27, 2022

Based on my research in airbrake/airbrake-ruby#641 the issue was with rmagick. I see you did open an issue there 👍 rmagick/rmagick#1334

Based a simpler gist repro steps, after looking into more the issue is probably related to rbtree

@kimyu92
Copy link
Author

kimyu92 commented May 2, 2022

Turn out this is the issue where rbtree clashes with ghostscript based on two libraries exporting the dict_ functions which expect different data types

Reference: this comment

Any potential adjustment is considered to tweak rbtree or rbtree3?

Also, where is the actual source code for rbtree v0.4.5 come from?

cc @knu @kyrylo

@kyrylo
Copy link

kyrylo commented May 3, 2022

I don't know where the original source code comes from (probably from rubyforge) and I am not sure what fork the rbtree gem uses. I actually wondered the same question: zzak/rbtree#2

I haven't had a chance to dig into the issue in rbtree3. If you dare, you are welcome😁 https://github.com/kyrylo/rbtree3

@bf4
Copy link

bf4 commented Dec 24, 2023

GitHub code mirror is e.g. mame/rbtree#5

@bf4
Copy link

bf4 commented Dec 24, 2023

business_time removed the gem bokmann/business_time#214

would you consider an implementation which doesn't require rbtree as a fallback for those us who cannot install it? e.g. as originally in the stdlib? ruby/set#2

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants