From: SASADA Koichi Date: 2013-04-21T23:48:00+09:00 Subject: [ruby-core:54490] Re: [ruby-trunk - Bug #8254] Ruby segfaults on second SystemStackError from parser charliesome discovered that the reason of this issues is longjmp from segv handler. After receiving SIGSEGV, segv handler runs on the altstack. And returns by *longjmp* ruby's world if it is caused by stack overflow. https://github.com/ruby/ruby/blob/trunk/signal.c#L670 "longjmp" doesn't care about signal status, and system can't restore signal status (especially altstack status). System assumes that altstack is used continuously. and second sigsegv handler can't use altstack. To solve this issue, charliesome replaced all of setjmp/longjmp pair to sigsetjmp/siglongjmp by r40402. This change fixes this problem. However, sigsetjmp/siglongjmp (especially sigsetjmp) requires system calls and slower than setjmp on the older systems. $ time ./miniruby -ve '5_000_000.times{1.times{}}' ruby 2.1.0dev (2013-04-21 trunk 40402) [x86_64-linux] real 0m3.393s user 0m1.904s sys 0m1.488s $ time ../versions/install-trunk-daily_2013-04-16T12_00/bin/ruby -ve '5_000_000.times{1.times{}}' ruby 2.1.0dev (2013-04-16 trunk 40318) [x86_64-linux] real 0m1.221s user 0m1.216s sys 0m0.008s on Linux 2.6.32-5-amd64 (Debian squid) So I asked to revert this change (r40403). --------------------------------------------- charliesome proposed several solutions: (1) Use libsigsegv * I'm not sure because it seems GNU product. (2) Use sigsetjmp/siglongjmp on newer systems only if there is no performance problem (3) Mix sigsetjmp/siglongjmp and setjmp/longjmp We need to restore signal status (altstack status), so use siglongjmp only at segv handler. (S1) [main] sigsetjmp(root) ($) (S2) [main] sigsetjmp(root) .... [foo] setjmp(foo) ... ($) (S3) [main] sigsetjmp(root) .... [foo] setjmp(foo) ... [bar] setjmp(bar) ($) (S4) [main] sigsetjmp(root) .... [foo] setjmp(foo) ... [bar] setjmp(bar) ... [SEGV handler] siglongjmp(root) ($) (S5) [main] sigsetjmp(root) ($) # signal status was restored (S6) [main] sigsetjmp(root) longjmp(bar) ($) (S7) [main] sigsetjmp(root) .... [foo] setjmp(foo) ... [bar] setjmp(bar) ($) ($) is program counter. * This is interesting technique, but I'm not sure this approach works fine because longjmp() at (S6) jumps into deeper stack frame. Ideas are welcome. (2013/04/11 21:36), charliesome (Charlie Somerville) wrote: > > Issue #8254 has been updated by charliesome (Charlie Somerville). > > > =begin > It seems to happen with any stack overflow from C: > > #include > > VALUE f() { > f(); > } > > Init_x() { > rb_define_global_function("f", f, 0); > } > > When (({f})) is called the second time, Ruby segfaults. > =end > ---------------------------------------- > Bug #8254: Ruby segfaults on second SystemStackError from parser > https://bugs.ruby-lang.org/issues/8254#change-38446 > > Author: charliesome (Charlie Somerville) > Status: Open > Priority: Normal > Assignee: > Category: > Target version: > ruby -v: ruby 2.0.0p0 (2013-02-24 revision 39474) [x86_64-darwin11.4.0] > > > =begin > When the parser overflows the stack, it raises SystemStackError. > > The second time this happens, Ruby segfaults. > > Code sample: > > n = 10_000 # adjust for your platform > begin > eval "1+" * n + "1" > rescue SystemStackError > eval "1+" * n + "1" > end > > =end > > -- // SASADA Koichi at atdot dot net