Thanks to visit codestin.com
Credit goes to github.com

Skip to content

ruby-ffi segfaults with latest libffi on x86_64-linux #989

@larskanis

Description

@larskanis

When compiled in the project repository on x86_64-linux like so:

rake compile -- --disable-system-libffi --enable-libffi-alloc

Then rake spec fails like so:

/home/lars/comcard/ffi/lib/ffi/library.rb:226: [BUG] Segmentation fault at 0x00007f7a218b8fc8
ruby 3.1.3p185 (2022-11-24 revision 1a6b16756e) [x86_64-linux]

-- Control frame information -----------------------------------------------
c:0021 p:---- s:0128 e:000127 CFUNC  :attach
c:0020 p:0247 s:0122 e:000121 METHOD /home/lars/comcard/ffi/lib/ffi/library.rb:226
c:0019 p:0070 s:0102 e:000101 CLASS  /home/lars/comcard/ffi/spec/ffi/async_callback_spec.rb:15
c:0018 p:0007 s:0099 e:000098 BLOCK  /home/lars/comcard/ffi/spec/ffi/async_callback_spec.rb:9 [FINISH]
c:0017 p:---- s:0096 e:000095 CFUNC  :module_exec
c:0016 p:0043 s:0092 e:000091 METHOD /home/lars/.rvm/gems/ruby-3.1.3/gems/rspec-core-3.12.0/lib/rspec/core/example_group.rb:398
c:0015 p:0155 s:0082 e:000081 BLOCK  /home/lars/.rvm/gems/ruby-3.1.3/gems/rspec-core-3.12.0/lib/rspec/core/example_group.rb:271 [FINISH]
c:0014 p:0028 s:0072 e:000071 BLOCK  /home/lars/.rvm/gems/ruby-3.1.3/gems/rspec-core-3.12.0/lib/rspec/core/dsl.rb:43 [FINISH]
c:0013 p:0024 s:0066 e:000065 BLOCK  /home/lars/.rvm/gems/ruby-3.1.3/gems/rspec-core-3.12.0/lib/rspec/core/dsl.rb:84 [FINISH]
c:0012 p:0047 s:0061 E:002470 TOP    /home/lars/comcard/ffi/spec/ffi/async_callback_spec.rb:8 [FINISH]
c:0011 p:---- s:0058 e:000057 CFUNC  :load
c:0010 p:0007 s:0053 e:000052 METHOD /home/lars/.rvm/gems/ruby-3.1.3/gems/rspec-core-3.12.0/lib/rspec/core/configuration.rb:2117
c:0009 p:0022 s:0044 e:000043 BLOCK  /home/lars/.rvm/gems/ruby-3.1.3/gems/rspec-core-3.12.0/lib/rspec/core/configuration.rb:1617 [FINISH]
c:0008 p:---- s:0039 e:000038 CFUNC  :each
c:0007 p:0017 s:0035 e:000034 METHOD /home/lars/.rvm/gems/ruby-3.1.3/gems/rspec-core-3.12.0/lib/rspec/core/configuration.rb:1615
c:0006 p:0036 s:0031 e:000030 METHOD /home/lars/.rvm/gems/ruby-3.1.3/gems/rspec-core-3.12.0/lib/rspec/core/runner.rb:102
c:0005 p:0007 s:0025 e:000024 METHOD /home/lars/.rvm/gems/ruby-3.1.3/gems/rspec-core-3.12.0/lib/rspec/core/runner.rb:86
c:0004 p:0065 s:0019 e:000018 METHOD /home/lars/.rvm/gems/ruby-3.1.3/gems/rspec-core-3.12.0/lib/rspec/core/runner.rb:71
c:0003 p:0020 s:0011 e:000010 METHOD /home/lars/.rvm/gems/ruby-3.1.3/gems/rspec-core-3.12.0/lib/rspec/core/runner.rb:45
c:0002 p:0025 s:0006 e:000005 EVAL   /home/lars/.rvm/gems/ruby-3.1.3/gems/rspec-core-3.12.0/exe/rspec:4 [FINISH]
c:0001 p:0000 s:0003 E:002400 (none) [FINISH]

-- Ruby level backtrace information ----------------------------------------
/home/lars/.rvm/gems/ruby-3.1.3/gems/rspec-core-3.12.0/exe/rspec:4:in `<main>'
/home/lars/.rvm/gems/ruby-3.1.3/gems/rspec-core-3.12.0/lib/rspec/core/runner.rb:45:in `invoke'
/home/lars/.rvm/gems/ruby-3.1.3/gems/rspec-core-3.12.0/lib/rspec/core/runner.rb:71:in `run'
/home/lars/.rvm/gems/ruby-3.1.3/gems/rspec-core-3.12.0/lib/rspec/core/runner.rb:86:in `run'
/home/lars/.rvm/gems/ruby-3.1.3/gems/rspec-core-3.12.0/lib/rspec/core/runner.rb:102:in `setup'
/home/lars/.rvm/gems/ruby-3.1.3/gems/rspec-core-3.12.0/lib/rspec/core/configuration.rb:1615:in `load_spec_files'
/home/lars/.rvm/gems/ruby-3.1.3/gems/rspec-core-3.12.0/lib/rspec/core/configuration.rb:1615:in `each'
/home/lars/.rvm/gems/ruby-3.1.3/gems/rspec-core-3.12.0/lib/rspec/core/configuration.rb:1617:in `block in load_spec_files'
/home/lars/.rvm/gems/ruby-3.1.3/gems/rspec-core-3.12.0/lib/rspec/core/configuration.rb:2117:in `load_file_handling_errors'
/home/lars/.rvm/gems/ruby-3.1.3/gems/rspec-core-3.12.0/lib/rspec/core/configuration.rb:2117:in `load'
/home/lars/comcard/ffi/spec/ffi/async_callback_spec.rb:8:in `<top (required)>'
/home/lars/.rvm/gems/ruby-3.1.3/gems/rspec-core-3.12.0/lib/rspec/core/dsl.rb:84:in `block (2 levels) in expose_example_group_alias_globally'
/home/lars/.rvm/gems/ruby-3.1.3/gems/rspec-core-3.12.0/lib/rspec/core/dsl.rb:43:in `block in expose_example_group_alias'
/home/lars/.rvm/gems/ruby-3.1.3/gems/rspec-core-3.12.0/lib/rspec/core/example_group.rb:271:in `block in define_example_group_method'
/home/lars/.rvm/gems/ruby-3.1.3/gems/rspec-core-3.12.0/lib/rspec/core/example_group.rb:398:in `subclass'
/home/lars/.rvm/gems/ruby-3.1.3/gems/rspec-core-3.12.0/lib/rspec/core/example_group.rb:398:in `module_exec'
/home/lars/comcard/ffi/spec/ffi/async_callback_spec.rb:9:in `block in <top (required)>'
/home/lars/comcard/ffi/spec/ffi/async_callback_spec.rb:15:in `<module:LibTest>'
/home/lars/comcard/ffi/lib/ffi/library.rb:226:in `attach_function'
/home/lars/comcard/ffi/lib/ffi/library.rb:226:in `attach'

-- Machine register context ------------------------------------------------
 RIP: 0x00007f7a21771c6c RBP: 0x000055a04f813f30 RSP: 0x00007ffcd3d2b5c8
 RAX: 0x00007f7a218b8fc8 RBX: 0x000055a04f951b70 RCX: 0x00007f7a218b8fc8
 RDX: 0x0000000000000017 RDI: 0x00007f7a218b8fc8 RSI: 0x00007f7a1da55420
  R8: 0x0000000000000100  R9: 0x0000000000000000 R10: 0x000000000000000f
 R11: 0x00007f7a21771c00 R12: 0x000055a04f951b50 R13: 0x00007f7a21ba0010
 R14: 0x00007ffcd3d2b5f0 R15: 0x00007f7a1daf0e08 EFL: 0x0000000000010202

-- C level backtrace information -------------------------------------------
/home/lars/.rvm/rubies/ruby-3.1.3/lib/libruby.so.3.1(rb_vm_bugreport+0x510) [0x7f7a21e821b0]
/home/lars/.rvm/rubies/ruby-3.1.3/lib/libruby.so.3.1(rb_bug_for_fatal_signal+0xf4) [0x7f7a21c73534]
/home/lars/.rvm/rubies/ruby-3.1.3/lib/libruby.so.3.1(sigsegv+0x4f) [0x7f7a21dd3e8f]
[0x7f7a2163bcf0]
[0x7f7a21771c6c]
[0x7f7a1da55500]
[0x7f7a1da51970]
[0x7f7a1da5552a]
[0x7f7a1da52ade]
/home/lars/.rvm/rubies/ruby-3.1.3/lib/libruby.so.3.1(vm_call_cfunc_with_frame+0x11f) [0x7f7a21e56cdf]
/home/lars/.rvm/rubies/ruby-3.1.3/lib/libruby.so.3.1(vm_exec_core+0x118) [0x7f7a21e66818]
/home/lars/.rvm/rubies/ruby-3.1.3/lib/libruby.so.3.1(rb_vm_exec+0xc3) [0x7f7a21e6c133]
/home/lars/.rvm/rubies/ruby-3.1.3/lib/libruby.so.3.1(yield_under+0x3e6) [0x7f7a21e71186]
/home/lars/.rvm/rubies/ruby-3.1.3/lib/libruby.so.3.1(vm_call_cfunc_with_frame+0x11f) [0x7f7a21e56cdf]
/home/lars/.rvm/rubies/ruby-3.1.3/lib/libruby.so.3.1(vm_sendish.constprop.0+0x163) [0x7f7a21e5c8d3]
/home/lars/.rvm/rubies/ruby-3.1.3/lib/libruby.so.3.1(vm_exec_core+0x184) [0x7f7a21e66884]
/home/lars/.rvm/rubies/ruby-3.1.3/lib/libruby.so.3.1(rb_vm_exec+0x56c) [0x7f7a21e6c5dc]
/home/lars/.rvm/rubies/ruby-3.1.3/lib/libruby.so.3.1(vm_call_bmethod+0x13a) [0x7f7a21e7b10a]
/home/lars/.rvm/rubies/ruby-3.1.3/lib/libruby.so.3.1(vm_call_symbol+0x165) [0x7f7a21e7c605]
/home/lars/.rvm/rubies/ruby-3.1.3/lib/libruby.so.3.1(vm_sendish.constprop.0+0x163) [0x7f7a21e5c8d3]
/home/lars/.rvm/rubies/ruby-3.1.3/lib/libruby.so.3.1(vm_exec_core+0x184) [0x7f7a21e66884]
/home/lars/.rvm/rubies/ruby-3.1.3/lib/libruby.so.3.1(rb_vm_exec+0xc3) [0x7f7a21e6c133]

gdb shows a more usable backtrace:

Program received signal SIGABRT, Aborted.
__pthread_kill_implementation (no_tid=0, signo=6, threadid=<optimized out>) at ./nptl/pthread_kill.c:44
Download failed: Das Argument ist ungültig.  Continuing without source file ./nptl/./nptl/pthread_kill.c.
44	./nptl/pthread_kill.c: Datei oder Verzeichnis nicht gefunden.
(gdb) bt
#0  __pthread_kill_implementation (no_tid=0, signo=6, threadid=<optimized out>) at ./nptl/pthread_kill.c:44
#1  __pthread_kill_internal (signo=6, threadid=<optimized out>) at ./nptl/pthread_kill.c:78
#2  __GI___pthread_kill (threadid=<optimized out>, signo=signo@entry=6) at ./nptl/pthread_kill.c:89
#3  0x00007ffff763bc46 in __GI_raise (sig=sig@entry=6) at ../sysdeps/posix/raise.c:26
#4  0x00007ffff76227fc in __GI_abort () at ./stdlib/abort.c:79
#5  0x00007ffff7bb6c70 in die () at error.c:784
#6  rb_bug_for_fatal_signal (default_sighandler=0x0, sig=sig@entry=11, ctx=ctx@entry=0x5555555f88c0, 
    fmt=fmt@entry=0x7ffff7ec45bd "Segmentation fault at %p") at error.c:825
#7  0x00007ffff7dafe8f in sigsegv (sig=11, info=0x5555555f89f0, ctx=0x5555555f88c0) at signal.c:964
#8  <signal handler called>
#9  __memcpy_avx_unaligned_erms () at ../sysdeps/x86_64/multiarch/memmove-vec-unaligned-erms.S:352
#10 0x00007ffff3a41500 in memcpy (__len=<optimized out>, __src=<optimized out>, __dest=<optimized out>)
    at /usr/include/x86_64-linux-gnu/bits/string_fortified.h:29
#11 ruby_nonempty_memcpy (n=<optimized out>, src=<optimized out>, dest=<optimized out>)
    at /home/lars/.rvm/rubies/ruby-3.1.3/include/ruby-3.1.0/ruby/internal/memory.h:659
#12 prep_trampoline (ctx=<optimized out>, code=<optimized out>, closure=0x555555ebee90, errmsg=<optimized out>, 
    errmsgsize=<optimized out>) at ../../../../ext/ffi_c/MethodHandle.c:306
#13 0x00007ffff3a3d970 in rbffi_Closure_Alloc (pool=0x555555b1b880) at ../../../../ext/ffi_c/ClosurePool.c:230
#14 0x00007ffff3a4152a in rbffi_MethodHandle_Alloc (fnInfo=0x555555ebec70, function=0x7ffff3a07900 <testFunctionAdd>)
    at ../../../../ext/ffi_c/MethodHandle.c:96
#15 0x00007ffff3a3eade in function_attach (self=140737279952640, module=140737281197600, name=<optimized out>)
    at ../../../../ext/ffi_c/Function.c:394
#16 0x00007ffff7e32cdf in vm_call_cfunc_with_frame (ec=0x55555555d980, reg_cfp=0x7ffff7995a90, calling=<optimized out>)
    at /home/lars/.rvm/src/ruby-3.1.3/vm_insnhelper.c:3037
#17 0x00007ffff7e42818 in vm_sendish (method_explorer=<optimized out>, block_handler=<optimized out>, cd=<optimized out>, 
    reg_cfp=<optimized out>, ec=<optimized out>) at /home/lars/.rvm/src/ruby-3.1.3/vm_callinfo.h:349
#18 vm_exec_core (ec=0x7ffff7b5dfc8, initial=140737281004576) at /home/lars/.rvm/src/ruby-3.1.3/insns.def:778
#19 0x00007ffff7e48133 in rb_vm_exec (ec=0x55555555d980, mjit_enable_p=true) at vm.c:2211
#20 0x00007ffff7e4d186 in invoke_block (captured=<optimized out>, opt_pc=<optimized out>, type=<optimized out>, cref=0x7ffff3941178, 
    self=140737281224120, iseq=0x7ffff3adcb40, ec=0x55555555d980) at vm.c:1316

Unfortunately valgrind doesn't show more information. It doesn't detect an invalid memory access, but just notices a segfault in memmove:

==3903277== Process terminating with default action of signal 6 (SIGABRT)
==3903277==    at 0x4F0E26B: __pthread_kill_implementation (pthread_kill.c:44)
==3903277==    by 0x4F0E26B: __pthread_kill_internal (pthread_kill.c:78)
==3903277==    by 0x4F0E26B: pthread_kill@@GLIBC_2.34 (pthread_kill.c:89)
==3903277==    by 0x4EB7C45: raise (raise.c:26)
==3903277==    by 0x4E9E7FB: abort (abort.c:79)
==3903277==    by 0x4890C6F: die (error.c:784)
==3903277==    by 0x4890C6F: rb_bug_for_fatal_signal.cold (error.c:825)
==3903277==    by 0x4A89E8E: sigsegv (signal.c:964)
==3903277==    by 0x4EB7CEF: ??? (in /usr/lib/x86_64-linux-gnu/libc.so.6)
==3903277==    by 0x484E98F: memmove (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)

I bisected the change in libffi and it showed that the segfault was introduced in commit libffi/libffi@9ba5592 and is present until latest master commit.

A CI run with updated libffi looks like so: https://github.com/larskanis/ffi/actions/runs/3966261950 . Only Linux seems to be affected, Macos and Windows aren't affected. We don't test on other CPU architectures.

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