-1

I have a code that looks like this:

//global variables

void signal_handler() {
    //deallocation of global variables
    free(foo);
    close(foo_2);
    exit(0);
}

int main () {
    signal(SIGINT, signal_handler);

    //irrelevant code
}

As you can see, I changed the CTRL+C interruption to execute the signal_handler function once instead of killing the process right away. I read somewhere that some functions like might be free are not async-safe and would NOT execute in the signal_handler but I'm not sure about that.

Can I execute functions like free, close, exit or even pthread_join in a signal handler?

user157629
  • 624
  • 4
  • 17
  • No. asdfasdfasfsadfas – 12431234123412341234123 Dec 10 '20 at 15:46
  • What kind of software are you coding? Some web thing, some scientific code (e.g. [CAD](https://en.wikipedia.org/wiki/Computer-aided_design) software) or some C code driving a nuclear missile? What happens to you when your software crashes? – Basile Starynkevitch Dec 10 '20 at 16:40
  • This is basically the same question as [How to avoid using `printf()` in a signal handler?](https://stackoverflow.com/q/16891019/15168). There's a list of functions that can be called in a signal handler (`close()` is one, but `fclose()` is not; neither `free()` nor `exit()` is, but you can call `_exit()` or `_Exit()` safely instead; `pthread_join()` is not callable from a signal handler). – Jonathan Leffler Dec 10 '20 at 20:49

4 Answers4

1

No. Only functions listed in man 7 signal-safety are safe to call inside a signal handler.

close is listed and should be safe. free is not. For reasons why you would have to look at its source code (it contains locks). exit is not safe because it can call arbitrary cleanup handlers. You have _exit which exits abruptly without the cleanup.

  • Note: `pthread_join` in its current glibc implementation looks safeish to me, but the standard does not guarantee that other implementations or future versions of it would be as well. The man page does mention some other `pthread_*` functions so the absence of `pthread_join` should be interpreted as it being unsafe. –  Dec 10 '20 at 17:21
1

You techincally can compile a program that calls such functions in a signal handler, nothing stops you from doing that. However it will result in undefined behavior if the function you are trying to execute is not async-signal-safe. It's not like unsafe function would just "NOT execute" as you say, they very well could, but that'd still be undefined behavior.

A list of async-signal-safe functions is documented in man 7 signal-safety. The close() function is safe, while free() and phtread_join() are not. The exit() function is also not safe to call from a signal handler, if you wish to exit from such context you will have to do so using _exit() instead.

The only way to safely call a function that is not async-signal-safe when receiving a signal is to "remember" that you have to call it (for example setting a global variable) and then do so after returning from the signal handler.

Marco Bonelli
  • 63,369
  • 21
  • 118
  • 128
  • If you something that causes UB, you get UB even before you reach that point. So technically you can't according to the C standard, because a program that causes UB could do nothing at all. – 12431234123412341234123 Dec 10 '20 at 15:47
  • free could probably deadlock, if the thread was interrupted while inside memory allocation routines. – Sven Nilsson Dec 10 '20 at 15:51
  • @SvenNilsson oh yeah it surely could, that's one of the infinite amount of possibilities when invoking undefined behavior :') – Marco Bonelli Dec 10 '20 at 15:52
  • @MarcoBonelli In reality yes. But not according to the C standard. If your program will reach UB sometimes in the future, with the information the program already has, nothing is guaranteed even before hitting that point. Your compiler may know that `free()` can't be called in a signal handler and optimize away the `signal(SIGINT, signal_handler);` call. – 12431234123412341234123 Dec 10 '20 at 16:08
  • @12431234123412341234123 I'll give you that "compiler optimizations are assuming no undefined behavior" etc, etc. Though I don't think that's a thing at all, it would have to be documented as per C99 $3.4.3.2. In any case, yeah, I'll just reword to make it clear in both contexts. – Marco Bonelli Dec 10 '20 at 16:20
1

Short answer is no:

7.1.4 Use of library functions
...
4     The functions in the standard library are not guaranteed to be reentrant and may modify objects with static or thread storage duration.188)
188) Thus, a signal handler cannot, in general, call standard library functions
C 2011 Online Draft

Real-world example of the consequences - I worked on a system that communicated with an Access database. There was a signal handler that tried to write an error message to the console with fprintf, but somehow during the signal handling process stderr got mapped to the .mdb file that stored the database, overwriting the header and ruining the database beyond repair.

There's honestly not a whole lot you can do in a signal handler other than set a flag to be checked elsewhere.

John Bode
  • 119,563
  • 19
  • 122
  • 198
  • Note that Standard C only permits the operations that every implementation can support — a very minimal set of operations. POSIX makes much more stringent demands on the system and there is an extensive collection of functions that can be used safely from signal handlers — as noted in my answer to the duplicate question. – Jonathan Leffler Dec 10 '20 at 20:54
  • C11 [§7.14.1.1 The `signal` function ¶5](http://port70.net/~nsz/c/c11/n1570.html#7.14.1.1p5) specifies the rules: _If the signal occurs other than as the result of calling the `abort` or `raise` function, the behavior is undefined if the signal handler refers to any object with static or thread storage duration that is not a lock-free atomic object other than by assigning a value to an object declared as `volatile sig_atomic_t`, or the signal handler calls any function in the standard library other than the `abort` function, the `_Exit` function, the `quick_exit` function, … _[…continued…]_ – Jonathan Leffler Dec 10 '20 at 21:00
  • _[…continuation…]_ … _or the `signal` function with the first argument equal to the signal number corresponding to the signal that caused the invocation of the handler. Furthermore, if such a call to the `signal` function results in a SIG_ERR return, the value of errno is indeterminate._ It also references footnote 252 which says: _If any signal is generated by an asynchronous signal handler, the behavior is undefined._ POSIX provides more reasonable semantics, and other platforms likewise extend what is defined behaviour over and above what the C standard permits (which is very minimal). – Jonathan Leffler Dec 10 '20 at 21:01
0

Can I execute free() or close() in a signal handler?

You definitely should not. See signal(7) and signal-safety(7)

In practice, it might work like you want perhaps more than half of the time. IIRC, the GCC compiler is doing like you want to do, and it usually works.

A better approach is to use some write(2) to a pipe(7) (from inside your signal handler) and from time to time check that pipe (in your main program) with poll(2) or related things.

Or you could set some volatile sigatomic_t flag; (perhaps it should be also _Atomic) in your signal handler, and check that flag elsewhere (in the main program, outside of signal handlers).

Qt is explaining that better than I could do in a few minutes.

On Linux, see also signalfd(2) and eventfd(2).

Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547