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

Stack overflows #271

Closed
jhass opened this issue Nov 30, 2014 · 11 comments
Closed

Stack overflows #271

jhass opened this issue Nov 30, 2014 · 11 comments

Comments

@jhass
Copy link
Member

jhass commented Nov 30, 2014

I'm not sure if that's possible, but I think it would be great if we could turn stack overflows into exceptions. Currently they just cause a segfault.

To be clear what I'm talking about, a simple trigger:

def foo
  foo
end
foo

Valgrind hints:

==27879== Stack overflow in thread 1: can't grow stack to 0xffe801ff8
==27879== 
==27879== Process terminating with default action of signal 11 (SIGSEGV)
==27879==  Access not within mapped region at address 0xFFE801FF8
==27879==    at 0x4017D1: *foo:Nil (in /home/jhass/projects/crystal/tmp3)
==27879==  If you believe this happened as a result of a stack
==27879==  overflow in your program's main thread (unlikely but
==27879==  possible), you can try to increase the size of the
==27879==  main thread stack using the --main-stacksize= flag.
==27879==  The main thread stack size used in this run was 8388608.
@asterite
Copy link
Member

Yes, it would be nice.

I tried this some time ago:

require "signal"

Signal.trap(Signal::SEGV) do
  puts 1
end

def foo
  foo
end

foo

But the signal handler is never invoked. I don't know if this is the correct way to do it.

Do you know if in C++ or another non-interpreted language this happens? We can learn from that.

@waj
Copy link
Member

waj commented Nov 30, 2014

Is not invoked because the signal handler would have to run in the same stack. To solve this sigaction must be used and set an alternate stack with sigaltstack. However I think it would be fairly complicated to return the program to a consistent state after the signal. Also, the SEGV signal is also raised for other conditions, like accessing an invalid pointer. The stack overflow could only be assumed checking the current stack pointer.

@jhass
Copy link
Member Author

jhass commented Nov 30, 2014

I'm not saying that exception should be recoverable, or be an exception at all really, I think it would just be a great feature of crystal to print something more informational to the users and for a stackoverflow a stacktrace can be very helpful.

@waterlink
Copy link
Contributor

Can't it be solved in a "soft" way? ie: by limiting the depth of method calls. Will probably cause performance issues, though..

@waterlink
Copy link
Contributor

Or I would look into this one: http://llvm.org/docs/doxygen/html/StackProtector_8cpp_source.html


EDIT: nope, it is majorly for security protection (to prevent datastructures (such as string or int[]) to overflow, when they are created on stack).

@waterlink
Copy link
Contributor

The other "soft"/"hard" method to do that is:

  • At entrypoint of program memorize somewhere current tip of stack
  • At start of each function compare current tip of stack with memorized head of stack
  • Jump to handling code when the difference is more than certain threshold (95% of stack size or something)

Handling code is something that is responsible for raising an exception.

This will in theory add about 5-6 asm instructions to each function call.


EDIT: this will still be susceptible to SEGV, if you allocate something huge on stack, or pass around some huge datastructure by value. But for normal recursion should work.

@refi64
Copy link
Contributor

refi64 commented Jul 21, 2015

My two cents:

Ignore the stack overflow. Use @waj's suggestion to trap SIGSEGV, then figure out how to recover the line number info and print it. This is what Nim does.

A stack trace would be nice, but it seems impossible to implement portably.

@radarek
Copy link
Contributor

radarek commented Sep 20, 2015

+1 for printing something more usefull than "Segmentation fault: 11" :).

Rubinius prints both C++ and Ruby backtrace on segfault (here is example of this behavior https://gist.github.com/benweint/44b15ff689fd694b83a8).
It does it by installing handler for SIGSEGV (and other signals like SIGABRT etc.) in a separate thread. Installed handler prints proper information and raises (c++).

@rdp
Copy link
Contributor

rdp commented Oct 27, 2016

+1 here, on mac today all I get is this, no hint I just overflowed the stack:

Invalid memory access (signal 11) at address 0x7fff56047ff8
[4449889259] *CallStack::print_backtrace:Int32 +107
[4449880119] __crystal_sigfault_handler +55
[140736039340986] _sigtramp +26

@ysbaddaden
Copy link
Contributor

For segfaults, build with --debug then run in gdb or lldb: you'll have a backtrace with file:line numbers!

@rdp
Copy link
Contributor

rdp commented Apr 24, 2017

Nice, the line numbers do show up in lldb. Would be nice if they showed up from within "spawn"s as well, but that's more generic exceptions and not just stack overflows...

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

No branches or pull requests

8 participants