Unhadled C++ exceptions cause abort · Issue #994 · eventmachine/eventmachine · GitHub
Skip to content

Unhadled C++ exceptions cause abort #994

Open

Description

@uvlad7

The following example demonstrates this

$ ruby -reventmachine -e 'at_exit { puts "bye" }; EventMachine.setuid_string "unknown"'
terminate called after throwing an instance of 'std::runtime_error'
  what():  setuid_string failed: No such file or directory
Aborted (core dumped)
$ echo $?
134

I understand that immediate abort is intended, but I believe rb_fatal should be used to wrap all unhandled C++ exceptions, as letting them pass across library boundaries is not considered a good practice.

Activity

  1. uvlad7 commented on Sep 1, 2024

    @uvlad7
    Author

    Here are some examples:

    vladimir@np940x5n:~$ irb
    3.0.2 :001 > require 'ffi'
     => true 
    3.0.2 :002 > module RbFFI
    3.0.2 :003 >   extend FFI::Library
    3.0.2 :004 >   ffi_lib FFI::CURRENT_PROCESS
    3.0.2 :005 > end
     => [#<FFI::DynamicLibrary:0x000058dfabc49850 @name="[current process]">] 
    3.0.2 :006 > require 'fiddle'
     => false 
    3.0.2 :007 > rb_eException = Fiddle.dlwrap Exception # Just a hacky way to convert get VALUE representation of Ruby object 
     => 97717656452720 
    3.0.2 :008 > RbFFI.attach_function :rb_define_class, [:string, :ulong], :ulong
     => #<FFI::Function address=0x00007bd66cc67d10> 
    3.0.2 :009 > rb_eFatal = RbFFI.rb_define_class("fatal", rb_eException)
     => 97717656451440 
    3.0.2 :010 > rb_eFatal = RbFFI.rb_define_class("fatal", rb_eException)
     => 97717656451440 
    3.0.2 :011 > RbFFI.attach_function :rb_raise, [:ulong, :string, :varargs], :void
     => 
    #<FFI::VariadicInvoker:0x000058dfacb21bc0
     @fixed=[#<FFI::Type::Builtin::ULONG size=8 alignment=8>, #<FFI::Type::Builtin::STRING size=8 alignment=8>],
     @type_map=nil> 
    3.0.2 :012 > RbFFI.rb_raise rb_eFatal, "fatal"
    /home/vladimir/.local/share/rtx/installs/ruby/3.0.2/lib/ruby/gems/3.0.0/gems/ffi-1.16.3/lib/ffi/variadic.rb:47:in `invoke': fatal (fatal)
    	from /home/vladimir/.local/share/rtx/installs/ruby/3.0.2/lib/ruby/gems/3.0.0/gems/ffi-1.16.3/lib/ffi/variadic.rb:47:in `call'
    	from /home/vladimir/.local/share/rtx/installs/ruby/3.0.2/lib/ruby/gems/3.0.0/gems/ffi-1.16.3/lib/ffi/variadic.rb:62:in `rb_raise'
    	from (irb):12:in `<main>'
    	from /home/vladimir/.local/share/rtx/installs/ruby/3.0.2/lib/ruby/gems/3.0.0/gems/irb-1.14.0/exe/irb:9:in `<top (required)>'
    	from /home/vladimir/.local/share/mise/installs/ruby/3.0.2/bin/irb:25:in `load'
    	from /home/vladimir/.local/share/mise/installs/ruby/3.0.2/bin/irb:25:in `<main>'
    	from /home/vladimir/.local/share/mise/installs/ruby/3.0.2/bin/ruby_executable_hooks:22:in `eval'
    	from /home/vladimir/.local/share/mise/installs/ruby/3.0.2/bin/ruby_executable_hooks:22:in `<main>'
    3.0.2 :013 > RbFFI.rb_raise rb_eException, "fatal"
    /home/vladimir/.local/share/rtx/installs/ruby/3.0.2/lib/ruby/gems/3.0.0/gems/ffi-1.16.3/lib/ffi/variadic.rb:47:in `invoke': fatal (Exception)
    	from /home/vladimir/.local/share/rtx/installs/ruby/3.0.2/lib/ruby/gems/3.0.0/gems/ffi-1.16.3/lib/ffi/variadic.rb:47:in `call'
    	from /home/vladimir/.local/share/rtx/installs/ruby/3.0.2/lib/ruby/gems/3.0.0/gems/ffi-1.16.3/lib/ffi/variadic.rb:62:in `rb_raise'
    	from (irb):13:in `<main>'
    	from /home/vladimir/.local/share/rtx/installs/ruby/3.0.2/lib/ruby/gems/3.0.0/gems/irb-1.14.0/exe/irb:9:in `<top (required)>'
    	from /home/vladimir/.local/share/mise/installs/ruby/3.0.2/bin/irb:25:in `load'
    	from /home/vladimir/.local/share/mise/installs/ruby/3.0.2/bin/irb:25:in `<main>'
    	from /home/vladimir/.local/share/mise/installs/ruby/3.0.2/bin/ruby_executable_hooks:22:in `eval'
    	from /home/vladimir/.local/share/mise/installs/ruby/3.0.2/bin/ruby_executable_hooks:22:in `<main>'
    3.0.2 :014 > RbFFI.attach_function :rb_fatal, [:string, :varargs], :void
     => #<FFI::VariadicInvoker:0x000058dfab66d170 @fixed=[#<FFI::Type::Builtin::STRING size=8 alignment=8>], @type_map=nil> 
    3.0.2 :015 > RbFFI.rb_fatal "faaaaaaaaaaaaaaaaatal"
    /home/vladimir/.local/share/rtx/installs/ruby/3.0.2/lib/ruby/gems/3.0.0/gems/ffi-1.16.3/lib/ffi/variadic.rb:47:in `invoke': faaaaaaaaaaaaaaaaatal (fatal)
    	from /home/vladimir/.local/share/rtx/installs/ruby/3.0.2/lib/ruby/gems/3.0.0/gems/ffi-1.16.3/lib/ffi/variadic.rb:47:in `call'
    	from /home/vladimir/.local/share/rtx/installs/ruby/3.0.2/lib/ruby/gems/3.0.0/gems/ffi-1.16.3/lib/ffi/variadic.rb:62:in `rb_fatal'
    	from (irb):15:in `<top (required)>'
    	from /home/vladimir/.local/share/rtx/installs/ruby/3.0.2/lib/ruby/gems/3.0.0/gems/irb-1.14.0/lib/irb/workspace.rb:121:in `eval'
    	from /home/vladimir/.local/share/rtx/installs/ruby/3.0.2/lib/ruby/gems/3.0.0/gems/irb-1.14.0/lib/irb/workspace.rb:121:in `evaluate'
    	from /home/vladimir/.local/share/rtx/installs/ruby/3.0.2/lib/ruby/gems/3.0.0/gems/irb-1.14.0/lib/irb/context.rb:633:in `evaluate_expression'
    	from /home/vladimir/.local/share/rtx/installs/ruby/3.0.2/lib/ruby/gems/3.0.0/gems/irb-1.14.0/lib/irb/context.rb:601:in `evaluate'
    	from /home/vladimir/.local/share/rtx/installs/ruby/3.0.2/lib/ruby/gems/3.0.0/gems/irb-1.14.0/lib/irb.rb:1049:in `block (2 levels) in eval_input'
    	from /home/vladimir/.local/share/rtx/installs/ruby/3.0.2/lib/ruby/gems/3.0.0/gems/irb-1.14.0/lib/irb.rb:1388:in `signal_status'
    	from /home/vladimir/.local/share/rtx/installs/ruby/3.0.2/lib/ruby/gems/3.0.0/gems/irb-1.14.0/lib/irb.rb:1041:in `block in eval_input'
    	from /home/vladimir/.local/share/rtx/installs/ruby/3.0.2/lib/ruby/gems/3.0.0/gems/irb-1.14.0/lib/irb.rb:1120:in `block in each_top_level_statement'
    	from /home/vladimir/.local/share/rtx/installs/ruby/3.0.2/lib/ruby/gems/3.0.0/gems/irb-1.14.0/lib/irb.rb:1117:in `loop'
    	from /home/vladimir/.local/share/rtx/installs/ruby/3.0.2/lib/ruby/gems/3.0.0/gems/irb-1.14.0/lib/irb.rb:1117:in `each_top_level_statement'
    	from /home/vladimir/.local/share/rtx/installs/ruby/3.0.2/lib/ruby/gems/3.0.0/gems/irb-1.14.0/lib/irb.rb:1040:in `eval_input'
    	from /home/vladimir/.local/share/rtx/installs/ruby/3.0.2/lib/ruby/gems/3.0.0/gems/irb-1.14.0/lib/irb.rb:1021:in `block in run'
    	from /home/vladimir/.local/share/rtx/installs/ruby/3.0.2/lib/ruby/gems/3.0.0/gems/irb-1.14.0/lib/irb.rb:1020:in `catch'
    	from /home/vladimir/.local/share/rtx/installs/ruby/3.0.2/lib/ruby/gems/3.0.0/gems/irb-1.14.0/lib/irb.rb:1020:in `run'
    	from /home/vladimir/.local/share/rtx/installs/ruby/3.0.2/lib/ruby/gems/3.0.0/gems/irb-1.14.0/lib/irb.rb:904:in `start'
    	from /home/vladimir/.local/share/rtx/installs/ruby/3.0.2/lib/ruby/gems/3.0.0/gems/irb-1.14.0/exe/irb:9:in `<top (required)>'
    	from /home/vladimir/.local/share/mise/installs/ruby/3.0.2/bin/irb:25:in `load'
    	from /home/vladimir/.local/share/mise/installs/ruby/3.0.2/bin/irb:25:in `<main>'
    	from /home/vladimir/.local/share/mise/installs/ruby/3.0.2/bin/ruby_executable_hooks:22:in `eval'
    	from /home/vladimir/.local/share/mise/installs/ruby/3.0.2/bin/ruby_executable_hooks:22:in `<main>'
    vladimir@np940x5n:~$ irb
    3.0.2 :001 > require 'ffi'
     => true 
    3.0.2 :002 > module RbFFI
    3.0.2 :003 >   extend FFI::Library
    3.0.2 :004 >   ffi_lib FFI::CURRENT_PROCESS
    3.0.2 :005 > end
     => [#<FFI::DynamicLibrary:0x000060ee91216340 @name="[current process]">] 
    3.0.2 :006 > RbFFI.attach_function :rb_fatal, [:string, :varargs], :void
     => #<FFI::VariadicInvoker:0x000060ee911b43c0 @fixed=[#<FFI::Type::Builtin::STRING size=8 alignment=8>], @type_map=nil> 
    3.0.2 :007 > at_exit { puts 'byebye' }
     => #<Proc:0x000060ee9124ed08 (irb):7> 
    3.0.2 :008 > RbFFI.rb_fatal "faaaaaaaaaaaaaaaaatal"
    byebye
    /home/vladimir/.local/share/rtx/installs/ruby/3.0.2/lib/ruby/gems/3.0.0/gems/ffi-1.16.3/lib/ffi/variadic.rb:47:in `invoke': faaaaaaaaaaaaaaaaatal (fatal)
    	from /home/vladimir/.local/share/rtx/installs/ruby/3.0.2/lib/ruby/gems/3.0.0/gems/ffi-1.16.3/lib/ffi/variadic.rb:47:in `call'
    	from /home/vladimir/.local/share/rtx/installs/ruby/3.0.2/lib/ruby/gems/3.0.0/gems/ffi-1.16.3/lib/ffi/variadic.rb:62:in `rb_fatal'
    	from (irb):8:in `<top (required)>'
    	from /home/vladimir/.local/share/rtx/installs/ruby/3.0.2/lib/ruby/gems/3.0.0/gems/irb-1.14.0/lib/irb/workspace.rb:121:in `eval'
    	from /home/vladimir/.local/share/rtx/installs/ruby/3.0.2/lib/ruby/gems/3.0.0/gems/irb-1.14.0/lib/irb/workspace.rb:121:in `evaluate'
    	from /home/vladimir/.local/share/rtx/installs/ruby/3.0.2/lib/ruby/gems/3.0.0/gems/irb-1.14.0/lib/irb/context.rb:633:in `evaluate_expression'
    	from /home/vladimir/.local/share/rtx/installs/ruby/3.0.2/lib/ruby/gems/3.0.0/gems/irb-1.14.0/lib/irb/context.rb:601:in `evaluate'
    	from /home/vladimir/.local/share/rtx/installs/ruby/3.0.2/lib/ruby/gems/3.0.0/gems/irb-1.14.0/lib/irb.rb:1049:in `block (2 levels) in eval_input'
    	from /home/vladimir/.local/share/rtx/installs/ruby/3.0.2/lib/ruby/gems/3.0.0/gems/irb-1.14.0/lib/irb.rb:1388:in `signal_status'
    	from /home/vladimir/.local/share/rtx/installs/ruby/3.0.2/lib/ruby/gems/3.0.0/gems/irb-1.14.0/lib/irb.rb:1041:in `block in eval_input'
    	from /home/vladimir/.local/share/rtx/installs/ruby/3.0.2/lib/ruby/gems/3.0.0/gems/irb-1.14.0/lib/irb.rb:1120:in `block in each_top_level_statement'
    	from /home/vladimir/.local/share/rtx/installs/ruby/3.0.2/lib/ruby/gems/3.0.0/gems/irb-1.14.0/lib/irb.rb:1117:in `loop'
    	from /home/vladimir/.local/share/rtx/installs/ruby/3.0.2/lib/ruby/gems/3.0.0/gems/irb-1.14.0/lib/irb.rb:1117:in `each_top_level_statement'
    	from /home/vladimir/.local/share/rtx/installs/ruby/3.0.2/lib/ruby/gems/3.0.0/gems/irb-1.14.0/lib/irb.rb:1040:in `eval_input'
    	from /home/vladimir/.local/share/rtx/installs/ruby/3.0.2/lib/ruby/gems/3.0.0/gems/irb-1.14.0/lib/irb.rb:1021:in `block in run'
    	from /home/vladimir/.local/share/rtx/installs/ruby/3.0.2/lib/ruby/gems/3.0.0/gems/irb-1.14.0/lib/irb.rb:1020:in `catch'
    	from /home/vladimir/.local/share/rtx/installs/ruby/3.0.2/lib/ruby/gems/3.0.0/gems/irb-1.14.0/lib/irb.rb:1020:in `run'
    	from /home/vladimir/.local/share/rtx/installs/ruby/3.0.2/lib/ruby/gems/3.0.0/gems/irb-1.14.0/lib/irb.rb:904:in `start'
    	from /home/vladimir/.local/share/rtx/installs/ruby/3.0.2/lib/ruby/gems/3.0.0/gems/irb-1.14.0/exe/irb:9:in `<top (required)>'
    	from /home/vladimir/.local/share/mise/installs/ruby/3.0.2/bin/irb:25:in `load'
    	from /home/vladimir/.local/share/mise/installs/ruby/3.0.2/bin/irb:25:in `<main>'
    	from /home/vladimir/.local/share/mise/installs/ruby/3.0.2/bin/ruby_executable_hooks:22:in `eval'
    	from /home/vladimir/.local/share/mise/installs/ruby/3.0.2/bin/ruby_executable_hooks:22:in `<main>'
    vladimir@np940x5n:~$ 

    As you can see, rb_fatal is not the same as just using rb_throw with rb_eFatal, it cannot be rescued, but it's not the same as throwing unhandled C++ exception, although abort() is called in the end, some sort of cleanup is performed (when possible), and at_exit was called.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions