My app uses a SIGSEGV (Segmentation Violation) signal handler (registered with sigaction()
).
When my app accesses a NULL pointer, my SIGSEGV handler gets a control, prints (to the console and log) the stack trace of the offending thread, and invokes the default signal handler which results in the core dump (crash dump) generation. So when a crash is reported by the testers, I quickly see in the log the offending thread stack trace (without analyzing the core dump).
However in the stack overflow case my signal handler is not launched, the app crashes (with silent log). While analyzing the core dump I (or my colleagues) see that the crash has happened in the middle of the correct code. It takes a significant time to understand that the stack overflow was the reason of the crash. So I want at least a message “Stack Overflow” in the log (in case of the stack overflow).
While reading the SIGALTSTACK(2) man page (NOTES section) I realize that my signal handler is called on the stack of the offending thread, that is why in case of the stack overflow the signal handler cannot be called (there is no stack to call the signal handler). However it is possible to run the signal handler on an alternate stack. This will let my signal handler to log the message “Stack Overflow”.
But the man page makes me think that if I switch my SIGSEGV handler to use an alternate stack then the handler will ALWAYS be executed on an alternate stack and will NOT be able to log the offending thread stack trace for NULL-pointer-access case.
Hence the question: “How to make the SIGSEGV handler be launched on an alternate stack for stack overflow case only (and on a currently active stack for all other cases)?”
To get the stack trace I use the following calls: backtrace(), backtrace_symbols(), abi::__cxa_demangle().
Advertisement
Answer
Instead of resetting the handler and re-raising SIGSEGV
on null pointer dereference, check if you’re running on the alt stack, and if so, use sigaction
to turn off the alternate stack, then re-raise the signal and return. You’ll then get the new signal handled on the main stack.
Of course it would be better to just ensure that stack overflow can’t happen. Don’t create nontrivial-sized stack-based buffers and don’t use recursion.