A signal is a mechanism for transferring control that is typically used to notify a process that an event has occurred. That process can then respond to that event accordingly. C99 provides The C standard provides functions for sending and handling signals within a C program.
Processes handle signals by registering a signal handler using the signal()
function, which is specified as
Code Block |
---|
void (*signal(int sig, void (*func)(int)))(int);
|
This is conceptually equivalent to
Code Block |
---|
typedef void (*sighandler_t)(int signum);
extern sighandler_t signal(
int signum,
sighandler_t handler
);
|
...
This noncompliant code example registers a single signal handler to process both SIGUSR1
and SIGUSR2
. The variable sig2
should be set to 1
if one or more SIGUSR1
signals are followed by SIGUSR2
, essentially implementing a finite state machine within the signal handler.
Code Block | ||||
---|---|---|---|---|
| ||||
#include <signal.h>
volatile sig_atomic_t sig1 = 0;
volatile sig_atomic_t sig2 = 0;
void handler(int signum) {
if (signum == SIGUSR1) {
sig1 = 1;
}
else if (sig1) {
sig2 = 1;
}
}
int main(void) {
if (signal(SIGUSR1, handler) == SIG_ERR) {
/* Handle error */
}
if (signal(SIGUSR2, handler) == SIG_ERR) {
/* Handler error */
}
while (sig2 == 0) {
/* Do nothing or give up CPU for a while */
}
/* ... */
return 0;
}
|
Unfortunately, there is a race condition occurs in the implementation of handler()
. If handler()
is called to handle SIGUSR1
and is interrupted to handle SIGUSR2
, it is possible that sig2
will not be set.
...
The POSIX sigaction()
function assigns handlers to signals in a similar manner to the C99 C signal()
function, but it also allows signal masks to be set explicitly. Consequently, sigaction()
can be used to prevent a signal handler from interrupting itself.
Code Block | ||||
---|---|---|---|---|
| ||||
#include <signal.h>
#include <stdio.h>
volatile sig_atomic_t sig1 = 0;
volatile sig_atomic_t sig2 = 0;
void handler(int signum) {
if (signum == SIGUSR1) {
sig1 = 1;
}
else if (sig1) {
sig2 = 1;
}
}
int main(void) {
struct sigaction act;
act.sa_handler = &handler;
act.sa_flags = 0;
if (sigemptyset(&act.sa_mask) != 0) {
/* Handle error */
}
if (sigaddset(&act.sa_mask, SIGUSR1)) {
/* Handle error */
}
if (sigaddset(&act.sa_mask, SIGUSR2)) {
/* Handle error */
}
if (sigaction(SIGUSR1, &act, NULL) != 0) {
/* Handle error */
}
if (sigaction(SIGUSR2, &act, NULL) != 0) {
/* Handle error */
}
while (sig2 == 0) {
/* Do nothing or give up CPU for a while */
}
/* ... */
return 0;
}
|
POSIX recommends sigaction()
and deprecates signal()
. Unfortunately, sigaction()
is not defined in C99 and in the C standard and is consequently not as portable a solution.
...
CERT C++ Secure Coding Standard: CWE-662, "Insufficient Synchronizationsynchronization"
ISO/IEC 2003 Section 5.2.3, "Signals and interrupts"
...
[Dowd 2006] Chapter 13, "Synchronization and State" ("Signal Interruption interruption and Repetitionrepetition")
[Open Group 2004] longjmp
[OpenBSD] signal()
Man Page
[Zalewski 2001]
...