A signal handler should not re-assert reassert its desire to handle its own signal. This is often done on non-persistent platforms, that is, when they receive a signal that is bound to a handler, they unbind the signal to default behavior before calling the handler. nonpersistent platforms—that is, platforms that, upon receiving a signal, reset the handler for the signal to SIG_DFL before calling the bound signal handler. Calling signal()
under these conditions presents a race condition. (See SIG01-C. Understand implementation-specific details regarding signal handler persistence.)
A signal handler may call signal()
to reset the signal, or change the current way signals are handled.
Non-Compliant Code Example
Code Block | ||
---|---|---|
| ||
void handler(int signum) {
signal(signum, handler);
/* handling code */
}
/* ... */
signal(signum, handler);
|
only if it does not need to be asynchronous-safe (that is, if all relevant signals are masked so that the handler cannot be interrupted).
Noncompliant Code Example (POSIX)
On nonpersistent platforms, this noncompliant code example contains a race window, starting when the host environment resets the signal Inside the handler, the signal()
function permits a race window. starting when the OS first resets the signal, and ending when the handler calls signal()
. During that time, a second signal sent to the program will still trigger the default signal behavior, thereby destroying the persistence consequently defeating the persistent behavior implied by the call to signal()
funtion inside from within the handler to reassert the binding.
If the OS environment is persistent (that is, it does not reset the handler when the signal is received), the signal()
call from within the handler
's signal()
function is redundant.
Code Block | ||||
---|---|---|---|---|
| ||||
#include <signal.h>
void handler(int signum) {
if (signal(signum, handler) == SIG_ERR) {
/* Handle error */
}
/* Handle signal */
}
void func(void) {
if (signal(SIGUSR1, handler) == SIG_ERR) {
/* Handle error */
}
} |
Compliant Solution
...
(POSIX)
Calling the For persistent platforms, the handler's signal()
function is unnecessary.from within the signal handler to reassert the binding is unnecessary for persistent platforms, as in this compliant solution:
Code Block | ||||
---|---|---|---|---|
| ||||
#include <signal.h> void handler(int signum) { /* handlingHandle codesignal */ } /* ... */ signal(signum, handler); void func(void) { if (signal(SIGUSR1, handler) == SIG_ERR) { /* Handle error */ } } |
Compliant Solution (POSIX)
POSIX defines the sigaction(2)
function, which assigns handlers to signals like in a similar manner to signal(2)
, but also allows one the caller to explicitly set persistence. One can thus use Consequently, the sigaction(2)
and sidestep )
function can be used to eliminate the race window on non-persistent OS's.nonpersistent platforms, as in this compliant solution:
Code Block | ||||
---|---|---|---|---|
| ||||
#include <signal.h> #include <stddef.h> void handler(int signum) { /* handlingHandle codesignal */ } /* ... */ /* Equivalent to signal( signum, handler); but make signal persistent */ void func(void) { struct sigaction act; act.sa_handler = &handler; act.sa_flags = 0; if (sigemptyset( &act.sa_mask) != 0) { /* handleHandle error */ } if (sigaction(signumSIGUSR1, &act, NULL) != 0) { /* handleHandle error */ } } |
Although the handler in this example does not call signal()
, it could do so safely because the signal is masked and the handler cannot be interrupted. If the same handler is installed for more than one signal, the signals must be masked explicitly in act.sa_mask
to ensure that the handler cannot be interrupted because the system masks only the signal being delivered.
POSIX recommends that new applications should use sigaction()
rather than signal()
. The sigaction()
function is not defined by the C Standard In fact, POSIX recommends sigaction(2)
and deprecates signal(2)
. Unfortunately, sigaction(2)
is not C99-compliant, and is not supported on some platforms, including Windows.
Compliant Solution (Windows)
There is no safe way to implement persistent signal-handler behavior on Windows platforms, and it should not be attempted. If a design depends on this behavior, and the design cannot be altered, it may be necessary to claim a deviation from this rule after completing an appropriate risk analysis.
The reason for this is that Windows is a nonpersistent platform as discussed above. Just before calling the current handler function, Windows resets the handler for the next occurrence of the same signal to SIG_DFL
. If the handler calls signal()
to reinstall itself, there is still a race window. A signal might occur between the start of the handler and the call to signal()
, which would invoke the default behavior instead of the desired handler.
Exceptions
SIG34-C-EX1: For implementations with persistent signal handlers, it is safe for a handler to modify the behavior of its own signal. Behavior modifications include ignoring the signal, resetting to the default behavior, and having the signal handled by a different handler. A handler reasserting its binding is also safe but unnecessary.
The following code example resets a signal handler to the system's default behavior:
Code Block | ||||
---|---|---|---|---|
| ||||
#include <signal.h>
void handler(int signum) {
#if !defined(_WIN32)
if (signal(signum, SIG_DFL) == SIG_ERR) {
/* Handle error */
}
#endif
/* Handle signal */
}
void func(void) {
if (signal(SIGUSR1, handler) == SIG_ERR) {
/* Handle error */
}
} |
Risk Assessment
Two signals in quick succession can trigger the a race condition on non-persistent nonpersistent platforms, thereby causing the signal's default behavior despite a handler's attempt to override it.
Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
---|---|---|---|---|---|
SIG34-C |
1 (low)
1 (unlikely)
3 (low)
P3
Low | Unlikely | Low | P3 | L3 |
Automated Detection
Tool | Version | Checker | Description | ||||||
---|---|---|---|---|---|---|---|---|---|
Astrée |
| signal-handler-signal-call | Partially checked | ||||||
Axivion Bauhaus Suite |
| CertC-SIG34 | |||||||
CodeSonar |
| BADFUNC.SIGNAL | Use of signal | ||||||
Compass/ROSE | Can detect violations of this rule. However, false positives may occur on systems with persistent handlers | ||||||||
Cppcheck Premium |
| premium-cert-sig34-c | Fully implemented | ||||||
Helix QAC |
| C5021 C++5022 | |||||||
Klocwork |
| MISRA.STDLIB.SIGNAL | |||||||
LDRA tool suite |
| 97 D | Fully implemented | ||||||
Parasoft C/C++test |
| CERT_C-SIG34-a | Properly define signal handlers | ||||||
PC-lint Plus |
| 2762, 2763 | Fully supported | ||||||
| CERT C: Rule SIG34-C | Checks for signal call from within signal handler (rule partially covered) | |||||||
RuleChecker |
| signal-handler-signal-call | Partially checked |
Related Vulnerabilities
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
References
Wiki Markup |
---|
\[[ISO/IEC 9899-1999TR2|AA. C References#ISO/IEC 9899-1999]\] Section 7.14.1.1, "The {{signal}} function" |
Related Guidelines
Key here (explains table format and definitions)
Taxonomy | Taxonomy item | Relationship |
---|---|---|
CERT C Secure Coding Standard | SIG01-C. Understand implementation-specific details regarding signal handler persistence | Prior to 2018-01-12: CERT: Unspecified Relationship |
ISO/IEC TS 17961:2013 | Calling signal from interruptible signal handlers [sigcall] | Prior to 2018-01-12: CERT: Unspecified Relationship |
...
SIG33-C. Do not recursively invoke the raise() function 12. Signals (SIG) 13. Error Handling with errno (ERR)