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 the event accordingly. C99 provides The C Standard provides functions for sending and handling signals within a C program.
Signals are handled by a process 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 signal handler is conceptually equivalent to
Code Block |
---|
typedef void (*SighandlerTypesighandler_t)(int signum); extern SighandlerTypesighandler_t signal( int signum, SighandlerType sighandler_t handler ); |
Signal handlers can be interrupted by signals, including their own. If a signal is not reset before its handler is called, the handler can interrupt its own execution. A handler that always successfully executes its code despite interrupting itself or being interrupted is asynchronousasync-signal-safe.
Some platforms provide the ability to mask signals while a signal handler is being processed. If a signal is masked while its own handler is processed, the handler is non-interruptible noninterruptible and need not be asynchronousasync-signal-safe. However, even when a signal is masked while its own handler is processed, the handler must still avoid invoking async-signal-safe unsafe functions because their execution may be (or have been) interrupted by another signal.
Vulnerabilities can arise if a non-asynchronoussignal handler that is not async-signal-safe signal handler is interrupted with any unmasked signal, including its own.
...
Noncompliant Code Example
This non-compliant noncompliant code example registers a single signal handler to process both SIGUSR1
and SIGUSR2
. The variable sig2
should be set to one 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) { /* doDo 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.
Compliant Solution (POSIX)
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) { /* handleHandle error */ } if (sigaddset(&act.sa_mask, SIGUSR1)) { /* handleHandle error */ } if (sigaddset(&act.sa_mask, SIGUSR2)) { /* handleHandle error */ } if (sigaction(SIGUSR1, &act, NULL) != 0) { /* handleHandle error */ } if (sigaction(SIGUSR2, &act, NULL) != 0) { /* handleHandle error */ } while (sig2 == 0) { /* doDo nothing or give up CPU for a while */ } /* ... */ return 0; } |
POSIX recommends sigaction()
and deprecates the use of signal()
to register signal handlers. Unfortunately, sigaction()
is not defined in C99 and in the C Standard and is consequently not as portable a solution.
Risk Assessment
Interrupting a non-interruptible noninterruptible signal handler can result in a variety of vulnerabilities \ [[Zalewski 01|AA. C References#Zalewski 01]\]Zalewski 2001]. Wiki Markup
Recommendation | Severity | Likelihood | Remediation Cost | Priority | Level |
---|---|---|---|---|---|
SIG00- |
3 (high)
3 (likely)
C | High | Likely | High | P9 | L2 |
Automated Detection
Tool | Version | Checker | Description | ||||||
---|---|---|---|---|---|---|---|---|---|
CodeSonar |
| BADFUNC.SIGNAL | Use of signal | ||||||
Helix QAC |
| C5019 | |||||||
LDRA tool suite |
| 44 S | Enhanced enforcement | ||||||
Parasoft C/C++test |
| CERT_C-SIG00-a | The signal handling facilities of <signal.h> shall not be used | ||||||
PC-lint Plus |
| 586 | Assistance provided: reports use of the signal function |
Related Vulnerabilities
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
References
Wiki Markup |
---|
\[[Dowd 06 | AA. C References#Dowd 06]\] Chapter 13, "Synchronization and State" (Signal Interruption and Repetition)
\[[ISO/IEC 03|AA. C References#ISO/IEC 03]\] Section 5.2.3, "Signals and interrupts"
\[[Open Group 04|AA. C References#Open Group 04]\] [longjmp|http://www.opengroup.org/onlinepubs/000095399/functions/longjmp.html]
\[[OpenBSD|AA. C References#OpenBSD]\] [{{signal()}} Man Page|http://www.openbsd.org/cgi-bin/man.cgi?query=signal]
\[[Zalewski 01|AA. C References#Zalewski 01]\] |
Related Guidelines
SEI CERT C++ Coding Standard | VOID SIG00-CPP. Mask signals handled by noninterruptible signal handlers |
MITRE CWE | CWE-662, Insufficient synchronization |
Bibliography
[C99 Rationale 2003] | Subclause 5.2.3, "Signals and Interrupts" |
[Dowd 2006] | Chapter 13, "Synchronization and State" ("Signal Interruption and Repetition") |
[IEEE Std 1003.1:2013] | XSH, System Interface, longjmp |
[OpenBSD] | signal() Man Page |
[Zalewski 2001] | "Delivering Signals for Fun and Profit" |
...
11. Signals (SIG) 11. Signals (SIG) SIG01-A. Understand implementation-specific details regarding signal handler persistence