2

Background

In my C++ program I have a SIGALRM handler in which I want to convert the signal into an exception by doing a throw (I understand that in general this is not good practice, but in the system I am working on it's probably the only option). My problem here is that SIGALRM handler could be invoked when we are doing a malloc, while throw will call __cxa_allocate_exception which does another malloc. The two malloc calls can hit a deadlock in glibc 2.12. I tried pre-allocating the exception, but the call to __cxa_allocate_exception still happened. I checked the source code of gcc and there doesn't seem to be any condition of putting the __cxa_allocate_exception call.

Additional Background

I install signal handler right before a try block and uninstall it after the catch. I'm throwing from the signal handler and this way I think it will be inside the try block (Let's not consider the case where the signal is received when we are in the catch logic) and can be caught correctly.

I think I'm hitting the malloc deadlock described here: https://sourceware.org/bugzilla/show_bug.cgi?id=13699 and here https://sourceware.org/ml/libc-alpha/2012-02/msg00272.html .

Question

My question is: is there anyway I can prevent throw from calling malloc? (Also, I understand that I can block the SIGALRM signal when I'm doing a malloc, but I'm afraid that there are too many places to block).

Thanks in advance. Any help/reference is high appreciated.

Lonely
  • 71
  • 6
  • 2
    How are you planning on catching the exception that you're throwing? – templatetypedef May 25 '16 at 00:23
  • Is `__cxa_allocate_exception` a weak symbol, by any chance? Then you could just define your own to replace it. EDIT: I checked, and it's not, at least in libstdc++3 (used by g++). It does a little bookkeeping besides just allocating memory, too. – Cameron May 25 '16 at 00:24
  • You're not trying to throw from a signal handler are you? – M.M May 25 '16 at 00:24
  • 1
    What happens if you get this signal inside a destructor, or during a `noexcept` function, or while another exception is active? – user2357112 May 25 '16 at 00:26
  • If throwing from a signal handler is really your only option, we're probably going to need more information about the constraints you're working under that forced you into that design decision. – user2357112 May 25 '16 at 00:28
  • 2
    "convert the signal into an exception"? How is that possible? A signal handler isn't called *from* anywhere when a signal occurs, so there is no way the exception can be caught. Or is there something I'm missing? – Brian Bi May 25 '16 at 00:31
  • 1
    Brian is right. Where exactly are you expecting the stack to unwind to? Even if the implementation somehow unwinds the stack to an exception handler how is the program going to handle the now invalid stack when you return from processing the signal? Why are you punishing yourself like this? – Captain Obvlious May 25 '16 at 00:37
  • Thanks for all the comments. A bit more background: I install signal handler right before a try block and uninstall it after the catch. I'm throwing from the signal handler and this way I think it will be inside the try block (Let's not consider if the signal is received in the catch logic) and can be caught correctly. – Lonely May 25 '16 at 00:40
  • Regarding the malloc deadlock: I think I hit a problem similar to what is described here: https://sourceware.org/bugzilla/show_bug.cgi?id=13699 and here http://www.sourceware.org/ml/libc-alpha/2012-02/msg00272.html . Apparently I'm not using a newer version of glibc but I'm not able to upgrade for now. – Lonely May 25 '16 at 00:42
  • The behaviour of such code is *implementation-defined* (C++14 [support.runtime]/10) so you should consult the compiler documentation to see what it says about signal handlers – M.M May 25 '16 at 00:42
  • 1
    signal handlers may be executed asynchronously (unless invoked by calling `raise` specifically) – M.M May 25 '16 at 00:45
  • 1
    [Duplicate](http://stackoverflow.com/questions/6535258/c-exceptions-and-signal-handlers) and a [duplicate](http://stackoverflow.com/questions/1717991/throwing-an-exception-from-within-a-signal-handler). You can't throw exceptions from signal handlers (unless the exception is caught before the signal handler returns). End of story. – Sam Varshavchik May 25 '16 at 01:22
  • @SamVarshavchik it's implementation-defined whether or not you can – M.M May 25 '16 at 03:25
  • I agree with @Brian and Sam. Signal handlers are not called from C++ contexts and cannot sensibly throw exceptions in general. You need to redesign all this. XY problem here. – user207421 May 25 '16 at 04:07

2 Answers2

1

The general problem is that if your signal handler is called while in ANY async-unsafe library function (such as malloc or printf), jumping out of the signal handler (via longjmp or exception) will likely leave the glibc in an inconsistent state, which will crash or otherwise misbehave the next time you call anything.

So even if you were able to avoid the call to malloc in the exception setup, and throw the exception and catch and handle it, your program would stiil crash the next time it called malloc afterwards.

The only way to avoid this is to ensure that the signal handler cannot be called while in an async-unsafe library function. You can do that by using sigblock to block and unblock the signal around EVERY call to a signal-unsafe function ANYWHERE in your program:

oldmask = sigblock(SIGALRM);
...call to malloc or printf or whatever...
setsetmask(oldmask);

This is possible, but not terribly practical.

Chris Dodd
  • 119,907
  • 13
  • 134
  • 226
-1

The manual states that malloc is "AS-Unsafe", which means it's not safe to call it inside asychronous signal handlers. If you do, your program has undefined behavior.

  • `malloc` can be replaced easy enough. Provide a custom implementation that can be switched to a reserved block while the signal handler is active. Might even be able to get away with throwing some RAII into the mix for good measure to switch back during the exception – Captain Obvlious May 25 '16 at 00:41
  • The problem is that the malloc is called from the "throw" (which calls __cxa_allocate_exception, which directly calls malloc) rather than by myself. I'm actually asking if I can avoid this. – Lonely May 25 '16 at 00:43
  • @Lonely Whether it's by you or the library it still results in undefined behavior, and deadlocks are a perfectly acceptable [normal result](http://comments.gmane.org/gmane.comp.lib.glibc.user/1852) of that. I'm not sure of a workaround as I don't fully know your problem. – uh oh somebody needs a pupper May 25 '16 at 00:46
  • @sleeptightpupper The question is asking how to make the standard library not call `malloc`. – user253751 May 25 '16 at 01:12
  • 1
    @immibis The most direct way of doing that is not to throw an exception from a signal handler. – uh oh somebody needs a pupper May 25 '16 at 01:21