Bug #13236
closedRuby segfault
Description
I have a program that segfault under certain conditions. It does not happen often, so it is hard to get the segfault.
The code is in https://github.com/DamienRobert/drain
You can run 'rake test' to (sometime, not often) get the core dump. I was able to get the coredump across different computers and different ruby versions.
In the tests I construct a graph with a cyclic path, so there is probably a bug in the gc when there are cyclic dependencies.
I join a coredump (compressed with xz), here is the backtrace (not very usefull because I did not compile ruby with debugging symbols):
Core was generated by `/usr/bin/ruby -w -Ilib:test -I/usr/lib/ruby/gems/2.4.0/gems/rake-12.0.0/lib /us'.
Program terminated with signal SIGABRT, Aborted.
#0 0x00007facdff1304f in raise () from /usr/lib/libc.so.6
[Current thread is 1 (Thread 0x7face08fe700 (LWP 7251))]
(gdb) bt
#0 0x00007facdff1304f in raise () from /usr/lib/libc.so.6
#1 0x00007facdff1447a in abort () from /usr/lib/libc.so.6
#2 0x00007face03096e1 in ?? () from /usr/lib/libruby.so.2.4
#3 0x00007face03c50ee in ?? () from /usr/lib/libruby.so.2.4
#4 <signal handler called>
#5 0x00007face02c9b44 in ?? () from /usr/lib/libruby.so.2.4
#6 0x00007face0321d0d in ?? () from /usr/lib/libruby.so.2.4
#7 0x00007face0322f13 in ?? () from /usr/lib/libruby.so.2.4
#8 0x00007face032515d in rb_gc_call_finalizer_at_exit ()
from /usr/lib/libruby.so.2.4
#9 0x00007face03104c4 in ruby_cleanup () from /usr/lib/libruby.so.2.4
#10 0x00007face0310645 in ruby_run_node () from /usr/lib/libruby.so.2.4
#11 0x00000000004007cb in ?? ()
#12 0x00007facdff00291 in __libc_start_main () from /usr/lib/libc.so.6
#13 0x00000000004007fa in _start ()
Files
Updated by Gondolin (Damien Robert) over 7 years ago
Damien Robert wrote:
The code is in https://github.com/DamienRobert/drain
You can run 'rake test' to (sometime, not often) get the core dump.
PS: I apologize that I was not able to get a minimal working example. I tried to do so in 'test_graph_segfault.rb' but it does not segfault. However removing test_graph.rb or test_converter.rb seems to prevent the segfault, but it is hard to tell because I am not able to get the segfault all the time. That's why I sent the coredump, I have several others (one where the message I get is an error due to a 'double free') if you need.
Updated by shevegen (Robert A. Heiler) over 7 years ago
Some bugs are just like that - they exist somewhere out there but they are hard
to find or reproduce. Almost real heisenbugs. :)
Updated by nobu (Nobuyoshi Nakada) over 7 years ago
- Description updated (diff)
- Status changed from Open to Feedback
Could you enable debugging?
Updated by Gondolin (Damien Robert) over 7 years ago
nobu (Nobuyoshi Nakada) wrote:
Could you enable debugging?
So I think I have managed to bisect the segfault. When I comment a specific test I never see the segfault, but when I uncomment it I get (sometimes) the segfault. The test itself in isolation does not segfault, it is only when I run a fairly large amount of other tests that I can get the segfault. Contrary to what I was thinking at first, the tests about graphs with loops are not the ones responsible for the segfault. I can provide the debugging information if you still need but they only concern the other tests, not the one that causes the segfault.
Here is the tested code and the test:
module Meta
extend self
#convert a class into a module using refinements
#ex: (Class.new { include Meta.refined_module(String) { def length; super+5; end } }).new("foo").length #=> 8
#This uses the fact that a refining module of klass behaves as if it had
#klass has his direct ancestor
def refined_module(klass,&b)
klass=klass.singleton_class unless Module===klass
Module.new do
#including the module rather than just returning it allow us to
#still be able to use 'using' ('using' does not work directly on
#refining modules, only the enclosing ones)
include refine(klass) {
module_eval(&b) if block_given?
}
end
end
end
describe Meta do
it "Can convert a class to module" do
(Class.new { include DR::Meta.refined_module(String) { def length; super+5; end } }).new("foo").length.must_equal(8)
end
end
Updated by nobu (Nobuyoshi Nakada) over 7 years ago
- Status changed from Feedback to Assigned
- Assignee set to shugo (Shugo Maeda)
- Backport changed from 2.2: UNKNOWN, 2.3: UNKNOWN, 2.4: UNKNOWN to 2.2: DONTNEED, 2.3: REQUIRED, 2.4: REQUIRED
Thank you, reduced code with GC.stress
could reproduce it.
module M
include refine(String) {def test;:ok end}
end
class C
include M
end
GC.stress = true
C.new("foo").test
But I wonder if it is possible to "convert a class to module".
Updated by nobu (Nobuyoshi Nakada) over 7 years ago
- Status changed from Assigned to Closed
Applied in changeset trunk|r58083.
class.c: prohibit refinement module
- class.c (ensure_includable): cannot include refinement
module, or the type and the class do not match.
[ruby-core:79632] [Bug #13236]
Updated by usa (Usaku NAKAMURA) over 7 years ago
- Backport changed from 2.2: DONTNEED, 2.3: REQUIRED, 2.4: REQUIRED to 2.2: DONTNEED, 2.3: DONE, 2.4: REQUIRED
ruby_2_3 r58519 merged revision(s) 58082,58083.
Updated by nagachika (Tomoyuki Chikanaga) over 7 years ago
- Backport changed from 2.2: DONTNEED, 2.3: DONE, 2.4: REQUIRED to 2.2: DONTNEED, 2.3: DONE, 2.4: DONE
ruby_2_4 r58632 merged revision(s) 58082,58083.