2

I have read the following:

When a signal handler is called in a C program, its corresponding signal action is set to SIG_DFL. You must reset the signal action if you want to handle the same signal again.

However, the following code keeps printing I have received a SIGHUP without resetting it, it's not falling back to it's default handler.

#include <signal.h> 
#include <stdio.h> 
#include <unistd.h>


void sighup() { 
  //signal(SIGHUP, sighup);
  printf("I have received a SIGHUP\n"); 
} 

int main(){
  signal(SIGHUP, sighup); 
  while(1){
   raise(SIGHUP); 
   sleep(1);
  }
 return 0;
}
  • Signals are sent to _processes_, not programs. When the program exits, the process that was running it - along with its signal settings - ceases to exist. Your quote is referring to the need to reset the signal handler each time it's called in a single process run. – Gene Mar 21 '20 at 01:17
  • 1
    Depends on the OS. If using a POSIXish unix/linux system, using `sigaction()` gives you more control over things like that. – Shawn Mar 21 '20 at 01:20
  • @Gene sorry, what? no idea what you're talking about. –  Mar 21 '20 at 01:21
  • The **Portability** section of the linux manpage for [signal(2)](http://man7.org/linux/man-pages/man2/signal.2.html) gives an overview of the historical issues with `signal()` in different OSes. As is often the case, it comes down to SysV vs BSD. – Shawn Mar 21 '20 at 01:23
  • Also note that it's unsafe to use `printf()` in a signal handler - the linux [signal-safety(7)](http://man7.org/linux/man-pages/man7/signal-safety.7.html) manpage explains more. – Shawn Mar 21 '20 at 01:27
  • 1
    When you quote text from somewhere, cite the source. Include a URL if possible, as well as title, author, and other identifying information. Aside from providing credit that other people deserve, it provides context to understand the text. – Eric Postpischil Mar 21 '20 at 01:34

4 Answers4

3

Whatever book or article you are quoting is quite correct... for some compilation environments.

It would, generally speaking, be a bad idea if a signal handler function could be interrupted by the same signal. That would result in a recursive invocation of the signal handler, and make it much more difficult to write signal handlers.

So it is not permitted, at least by default. But Posix allows two radically different mechanisms to prevent nested handler calls. When signal has been called with a signal handler function as an argument (that is, neither SIG_DFL nor SIG_IGN) and the corresponding signal sig is raised:

… it is implementation-defined whether the equivalent of a:

   signal(sig, SIG_DFL);

is executed or the implementation prevents some implementation-defined set of signals (at least including sig) from occurring until the current signal handling has completed. (From the Posix description of signal().)

Both block the signal during the execution of the signal handler, but the first option does not restore the signal handler, while the second one will leave the signal blocked if the signal handler does not return normally (eg. through longjmp or execve()).

If you are writing a program which might be run on different operating systems, or even on the same operating system with a different compiler, or even on the same machine with the same compiler but different compiler options, then you cannot count on one or the other behaviour. And there is no portable way to tell which one occurred.

For this reason, signal should not be used to install signal handlers; use sigaction, as recommended by Posix:

The sigaction() function provides a more comprehensive and reliable mechanism for controlling signals; new applications should use sigaction() rather than signal().
(from the same Posix page, in the "Application Usage" section. Note that this advice is 20 years old; "new applications" should be interpreted in that context.)

Just to demonstrate how difficult it is to make conclusions on the basis of a single test, I modified your program to use write rather than printf, in order to avoid the restriction on use of printf in a signal handler, and compiled it on a reasonably modern Ubuntu system using gcc's default settings, and the --std=c11 option:

$ cat sig.c
#include <signal.h> 
#include <unistd.h>

void sighup() { 
  write(2, "SIGHUP\n", 7);
} 

int main(){
  signal(SIGHUP, sighup); 
  while(1){
    raise(SIGHUP); 
    sleep(1);
  }
  return 0;
}

$ gcc -Wall -o sig sig.c
$ ./sig
SIGHUP
SIGHUP
SIGHUP
SIGHUP
SIGHUP
^C
$ gcc -Wall -std=c11 -o sig sig.c
$ ./sig
SIGHUP
Hangup
$

The explanation for this behaviour is found in the Linux manpage for signal, which you might want to read (to the end :-) ). Basically, Linux natively provides the SIG_DFL semantics, but glibc's wrapper function actually calls sigaction in order to implement the alternative handling, blocking the signal until the handler returns. Except that glibc only uses this alternative implementation if the feature-test macro _DEFAULT_SOURCE has been set. Gcc sets this macro by default, but since it enables features outside of the C standard, it is not set if you request a standard C compilation environment.

Community
  • 1
  • 1
rici
  • 234,347
  • 28
  • 237
  • 341
1

As others have mentioned, sigaction may give you more control.

But, you can't [are not permitted to] call printf from a signal handler. It can mess up the heap and the stdout stream [or have other unspecified effects].

Only a limited number of functions can be called from within a signal handler without disastrous/unpredictable results [and printf is not one of them].


UPDATE:

sorry is there an article on this? what does signal handler have to do with the heap?

Yes, see both man 7 signal and [more importantly] man 7 signal-safety.

printf does malloc/free to get a [temporary] struct. The signal handler can be invoked at any time.

What would happen if the base task was in the middle of allocating/freeing memory. Between threads, malloc/free use a mutex to prevent corruption of two thread trying to use the heap at the same time.

But, the mutex does not prevent a signal handler from firing in the middle, when the heap is in an indeterminate state.

So, you could have:

T0: enter malloc
T1: lock mutex
T2: start some allocation

T3: enter signal handler
T4: call printf
T5: call malloc
T6: change heap
T7: exit malloc
T8: exit printf
T9: exit signal handler

T10: try to finish allocation, using stale linked list pointers that are no
     longer valid -- bang, bang!

The same issue can occur with the values inside the FILE struct for stdout

Craig Estey
  • 30,627
  • 4
  • 24
  • 48
  • sorry is there an article on this? what does signal handler have to do with the heap? –  Mar 21 '20 at 01:31
  • http://man7.org/linux/man-pages/man7/signal-safety.7.html im guessing –  Mar 21 '20 at 01:32
1

In the days before POSIX, the UNIX 7th Edition, System III and System V signal handling did reset the signal handler back to SIG_DFL, so the normal technique was to use a correctly defined signal handler (it gets the signal number as an argument), and reset the signal handler as the first action:

void sig_handler(int signum)
{
    signal(signum, sig_handler);
    …the rest of the handler…
 }

This didn't avoid a small window of vulnerability where a second signal delivered to the same process was likely to kill it. In BSD 4.2 and later, signal() did not reset the signal handler ('reliable signals'). (See: Michael Kerrisk The Linux Programming Interface: A Linux and Unix System Programming Handbook, 1st Edn 2010 — or a Google search for 'BSD reliable signals', which includes Unix signals and process groups.)

POSIX added sigaction() which has a far more flexible interface. It can simulate the old System III signal handling, or the BSD reliable signal handling, or its own extensions (SA_SIGINFO, etc).

See also:

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
-1

A quick Google search suggests that the passage you quoted applies only to the IBM ILE version of the signal() function.

The man pages for signal on Linux and BSD seem to contain no such notices, and as you have observed, do not seem to exhibit such behaviour.

Siguza
  • 21,155
  • 6
  • 52
  • 89