The signal()
function has implementation-defined behavior and behaves differently on Windows, for example, on Windows than it does on Unix many UNIX systems.
The following code example code illustrates shows this behavior:
Code Block |
---|
#include <stdio.h> #include <signal.h> volatile sig_atomic_t e_flag = 0; void handler(int signum) { e_flag = 1; } int main(void) { if (signal(SIGINT, handler); == SIG_ERR) { /* Handle error */ } while (!e_flag) {} puts("Escaped from first while ()"); e_flag = 0; while (!e_flag) {} puts("Escaped from second while ()"); return 0; } |
Unix Many UNIX (and UnixUNIX-like) systems automatically reinstall signal handlers upon handler execution, meaning that the signal handler defined by the user is left in place until it is explicitly removed. For example, when this code is compiled with gcc GCC 3.4.4 and executed under Red Hat Linux, the SIGINT
is captured both times by handler
.:
Code Block |
---|
% ./SIG01-Atest ^C Escaped from first while () ^C Escaped from second while () % |
When a signal handler is installed with the signal()
function in Windows and some UNIX systems, the default action is restored for that signal after the signal is triggered. This means that signal handlers are not automatically reinstalled. For example, when this code is compiled with Microsoft Visual Studio 2005, version 8.0, only the first SIGINT
is captured by handler
.:
Code Block |
---|
> SIG01-Atest.exe ^C Escaped from first while () ^C > |
...
Different actions must be taken depending on whether or not the application requires signal handlers to be persistent.
...
Persistent Handlers
Errors and potential vulnerabilities exist when the actual signal handler Asynchronous signals may originate from malicious actors external to the process. Consequently, vulnerabilities may exist if the signal-handler-persistence behavior is inconsistent with the developer's expectations, for example, such as when the developer expects the signal handler to persist but it does not.
...
Noncompliant Code Example
...
This non-compliant noncompliant code example fails to persist the signal handler on Windows platforms .and on those UNIX systems where handlers are not persistent by default:
Code Block | ||||
---|---|---|---|---|
| ||||
void handler(int signum) { /* handlingHandle codesignal */ } |
...
Noncompliant Code Example
...
A common solution approach to make create persistent signal handlers is to call signal()
inside from within the handler itself, thus 'consequently unresetting' the reset signal.:
Code Block | ||||
---|---|---|---|---|
| ||||
void handler(int signum) { #ifdef WINDOWS if (signal(signum, handler); #endif == SIG_ERR) { /* Handle error */ } /* handlingHandle codesignal */ } |
Unfortunately, this solution still permits contains a race window. , starting when the OS first host environment 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 persistence.defeating the persistent behavior. (See SIG34-C. Do not call signal() from within interruptible signal handlers.)
A secure solution would must prevent the OS environment from resetting the signal in the first place, and thereby guarantee guaranteeing persistence. Unfortunately, Windows does not provide a secure solution to this problem.
Compliant Solution (POSIX)
The POSIX defines the sigaction(2)
function , which assigns handlers to signals like in a manner similar to the C signal(2)
, function but also allows one to explicitly set persistence. One can thus use sigaction(2)
and sidestep the race window on non-persistent OS's.signal-handler persistence to be controlled via the SA_RESETHAND
flag. (Leaving the flag clear makes the handler persistent.)
Code Block | ||||
---|---|---|---|---|
| ||||
/* /* Equivalent to signal( SIGUSR1, handler); but makes * signal persistent. */ struct sigaction act; act.sa_handler = &handler; act.sa_flags = 0; /* makes signal persistent by default */ if (sigfillsetsigemptyset( &act.sa_mask) != 0) { /* handleHandle error */ } if (sigaction(SIGUSR1, &act, NULL) != 0) { /* handleHandle error */ } |
POSIX recommends sigaction(2)
and deprecates signal()
. Unfortunately, sigaction()
is not C99-compliant.
...
defined in the C Standard and is consequently not as portable a solution.
Nonpersistent Handlers
Errors may also occur when the developer expects the default action to be restored for a signal , but instead, the signal handler persists instead.
...
Noncompliant Code Example (
...
UNIX)
This non-compliant noncompliant code example fails to reset the signal handler to its default behavior on Unix systems.systems where handlers are persistent by default:
Code Block | ||||
---|---|---|---|---|
| ||||
void handler(int signum) { /* handlingHandle codesignal */ } |
Compliant Solution (
...
UNIX and Windows)
A C99C-compliant solution to reset the handler on a Unix UNIX system is to rebind the signal to the implementation-defined default handler in the first line of the handler itself. Windows, however, automatically resets handlers to their default behavior.
Code Block | ||||
---|---|---|---|---|
| ||||
void handler(int signum) { #ifndef WINDOWS if (signal(signum, SIG_DFL); == SIG_ERR) { /* Handler error */ } #endif /* handlingHandle codesignal */ } |
Windows automatically resets handlers to default.
There is With the compliant solution for UNIX, no race condition occurs that can be utilizied exploited by an attacker in sending a second signal here, . This is because a second signal sent to the handler, before it the latter calls signal(signum, SIG_DFL)
, will merely only cause it the handler to restart , and call signal()
anyway.
This solution is an exception to SIG34-C. Do not call signal() from within interruptible signal handlers.
Compliant Solution (POSIX)
The POSIX defines the sigaction(2)
function , which assigns handlers to signals like in a manner similar to the C signal(2)
, function but also allows one to explicitly set persistence. One can thus use sigaction(2)
and sidestep the race window on non-persistent OS's.signal-handler persistence to be controlled via the SA_RESETHAND
flag. (Setting the flag makes the handler nonpersistent.)
Code Block | ||||
---|---|---|---|---|
| ||||
/* /* Equivalent to signal( SIGUSR1, handler); but makes * signal nonpersistent. */ struct sigaction act; act.sa_handler = &handler; act.sa_flags = SA_RESETHAND; /* makes signal non-persistent */ if (sigfillsetsigemptyset( &act.sa_mask) != 0) { /* handleHandle error */ } if (sigaction(SIGUSR1, &act, NULL) != 0) { /* handleHandle error */ } |
...
Risk Assessment
Failure to understand implementation-specific details regarding signal-handler persistence can lead to unexpected behavior.
Recommendation | Severity | Likelihood | Remediation Cost | Priority | Level |
---|---|---|---|---|---|
SIG01- |
1 (low)
1 (unlikely)
3 (low)
P3
C | Low | Unlikely | Low | P3 | L3 |
Automated Detection
Tool | Version | Checker | Description | ||||||
---|---|---|---|---|---|---|---|---|---|
CodeSonar |
| BADFUNC.SIGNAL | Use of signal | ||||||
Compass/ROSE | Could detect possible violations by flagging any signal handler that calls | ||||||||
Helix QAC |
| C5020 | |||||||
LDRA tool suite |
| 97 D | Partially implemented | ||||||
Parasoft C/C++test |
| CERT_C-SIG01-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 |
---|
\[[ISO/IEC 9899-1999TR2|AA. C References#ISO/IEC 9899-1999]\] Section 7.14.1.1, "The {{signal}} function" |
Related Guidelines
SEI CERT C++ Coding Standard | VOID SIG01-CPP. Understand implementation-specific details regarding signal handler persistence |
...
SIG00-A. Avoid using the same handler for multiple signals 12. Signals (SIG) SIG02-A. Avoid using signals to implement normal functionality