2

On Android (and I assume on generic Linux as well), a signal handler function set with sigaction is called in a separate thread (probably dedicated specifically to signal handling). Is there a way to register the handler callback so that it's executed in the same thread that triggered the signal?

Violet Giraffe
  • 32,368
  • 48
  • 194
  • 335

1 Answers1

3

On generic Linux, signal handlers are not called in a separate thread by default. (I suspect it's the same on Android, but I haven't looked into it.) To handle signals in a separate thread, you have to explicitly call sigwait(3) from some thread where you want to handle signals. This is a common setup.

Instead, the default behavior (for process-directed signals) is to pick an existing thread "at random" to handle an incoming signal. (There might be some consistency to it of course, but that's an implementation detail.)

However, there are also thread-directed signals, which are guaranteed to be handled in a specific thread. Examples of thread-directed signals include signals generated in response to hardware exceptions, like SIGSEGV, SIGBUS, SIGILL, etc., which will be handled in the same thread (meaning there's nothing special you need to do!), as well as signals sent with pthread_kill(3) to a specific thread.

The signal(7) man page (note the section number) is a good reference.

Ulfalizer
  • 4,664
  • 1
  • 21
  • 30
  • I see... Sadly, SIGSEGV also ends up being handled in a separate thread. Is there no way to disable this behavior? – Violet Giraffe Apr 15 '15 at 05:06
  • How did you determine that it's being handled in a separate thread? From some experimentation with two threads where one `sigwait()`s for `SIGSEGV` and the other segfaults, the `SIGSEGV` signal handler is called in the thread that segfaults instead of in the one that `sigwait()`s, which is what I would have expected too. It would be weird to handle something like `SIGSEGV` in a different thread, since the original thread must be stopped anyway. – Ulfalizer Apr 15 '15 at 08:56
  • I tried blocking `SIGSEGV` with `pthread_sigmask()` in the thread that segfaults before segfaulting now too, but that just causes the process to die immediately without calling the signal handler at all. – Ulfalizer Apr 15 '15 at 09:02
  • Wow, that's news. You're right, `pthread_self()` matches for the signal source and signal handler. – Violet Giraffe Apr 15 '15 at 09:20
  • OK, now it totally doesn't make sense to me. Is it possible that the system creates a new stack for the signal handler while still running it in the same thread? Because the stack is totally separate and doesn't include the fault site (I mean the call stack here). – Violet Giraffe Apr 15 '15 at 09:22
  • Should use the same stack by default I think. There's an option `SA_ONSTACK` that can be used with `sigaction()` to use a separate stack (established with `sigaltstack()`). – Ulfalizer Apr 15 '15 at 09:26
  • How are you comparing the stacks btw? By looking at addresses? If you're just trying to generate a backtrace from the signal handler, then maybe something is broken (with the stack or backtrace generation logic) that prevents the fault site from showing up. – Ulfalizer Apr 15 '15 at 09:30
  • I certainly didn't pass the `SA_ONSTACK` flag, but something definitely prevents me from seeing the stack past the handler. Yes, I'm trying to print the backtrace. It works perfectly when I simply call my `dumpBacktrace` function, but when called from signal handler, all I get is this (outputs from Android 4.0.3 and Android 5.0): https://gist.github.com/VioletGiraffe/a3089382cea3ddfb43cd – Violet Giraffe Apr 15 '15 at 09:45
  • P. S. The exact code I use for creating the backtrace is specified in this question. It'd be great if you could take a look and find my mistake (there's bounty, too!) :) http://stackoverflow.com/questions/29559347/how-to-unwind-the-stack-to-get-backtrace-for-the-specified-stack-pointer-sp – Violet Giraffe Apr 15 '15 at 09:46
  • Not sure if they're available on Android, but did you try the `backtrace(3)` functions? The problem might be that whatever backtrace library you're using doesn't know how to unwind past the signal handler entry on the stack. I know `backtrace(3)` can do that on desktop x86 machines at least. – Ulfalizer Apr 15 '15 at 09:59
  • To confirm that the signal handler is running on the same stack, you could just print the addresses of some local variables outside and inside the signal handler and see if they're close btw. – Ulfalizer Apr 15 '15 at 10:01
  • `backtrace` functions don't seem to be available. – Violet Giraffe Apr 15 '15 at 10:24
  • The addresses for the local `int i` declared in the first line of the each function (the fault generator and the signal handler) are 102 000 bytes apart. Not sure if it allows to call it the same or different stacks definitively. – Violet Giraffe Apr 15 '15 at 10:27
  • I dug into it some more, and it looks like Android always uses a separate signal stack (for both the main thread and any threads created with `pthread_create()`). Here's the straight-from-the-horse's-mouth version: https://github.com/android/platform_bionic/blob/master/libc/bionic/pthread_create.cpp (see `__init_alternate_signal_stack()`). – Ulfalizer Apr 15 '15 at 11:01
  • And it's not possible to undo that, i. e. make the signal handler being called on the same stack? – Violet Giraffe Apr 15 '15 at 11:32
  • https://groups.google.com/forum/#!topic/android-platform/VKlX7sNEV10 recommends using libbacktrace, but I can't see a way to pass it a `ucontext` to give it the right context inside the `SIGSEGV` handler. Messy stuff... – Ulfalizer Apr 15 '15 at 12:16